以前也學過一些shell,不過學得並不是很深入,動手寫的代碼的時間也不是很多。前不久將shell比較細的過了一遍,leader布置了任務讓用shell寫一個腳本將redis源碼壓縮包從一個服務器上傳到另外一個服務器,同時在本服務器上傳入命令,解壓源碼包並在另一個服務器上啟動redis服務。
雖然學了一些shell,但是本次任務中需要使用的SCP和expect卻是第一次使用,其中也遇到了一些問題,所以在這里總結一下。
Expect
現代的Shell對程序提供了最小限度的控制(開始,停止,等等),而把交互的特性留給了用戶。 這意味着有些程序, 你不能非交互的運行,比如說passwd。簡單點說就是你得手動輸入密碼,shell沒有提供給你將密碼直接在程序中一次性搞定的方法。
也正是因為如此,expect誕生了。
注意expect是獨立於shell之外的,或者說他們完全不是一個東西(但是兩者都是linux下很強大的工具)。當時我因為絕得expect是shell的一個補充,所以在expect中使用了很多shell的語法,呵呵!!!結果你懂的。
我在這里就簡單說一下我用到的expect的基本語法,以及走過的坑,同時如果大家有願望去學習一下的話我會在后面提供幾個給我不少借鑒的網頁,以便大家深入學習。
- 最簡單的#!/usr/bin/env sh和#!/usr/bin/env expect 就懶得說了;
- 其次在expect中,傳入參數的個數是用$argc訪問的,然而在shell中$#。雖然很簡單,但是shell中下面這幾個參數還是記得的好:

3. shell中設置變量,$a="hello world",expect中 set a "hello world"
4. 在expect中查看變量的值:send_user "$a";
5. 在expect中執行shell的程序: spawn <shellCommand>
6. expect中查看傳入的參數的值:[lindex $argv 1] [lindex $argv 2] [lindex $argv 3]...[lindex $argv n]
7. 特別有用的:用於進行交互的expect 和 send。我也沒學得太深,只能大概舉一個例子:
spawn ssh -l username 192.168.1.1
expect "*password:"
send "ispass\r"
就差不多這個樣子吧!特別注意的是:send 后面的密碼最后一定要跟一個\r,否則你等着錯吧!!!
剛好到這里就再說說我遇到的一個大坑——情況是這樣的,為了提高代碼的復用性,導師都是讓我不要把什么密碼啊這一類的寫死的,要么傳參要么設置環境變量值,我選擇了傳參的方法,就比如下面這個程序:
hello.sh
#!/usr/bin/env expect
set host [lindex $argv 1]
set passwd [lindex $argv 2]
spawn ssh -l username $host
expect "*password:"
send "$passwd"
執行的命令是這樣的:./hello.sh "192.168.1.1" "ispass\r"
好了,TMD。一直出錯,不記得具體報什么錯了,好像是提示密碼錯誤。怎么會出錯呢,糾結,糾結.....足足糾結幾個小時啊!!!不知道錯哪兒了
后來看到了這篇文檔(http://blog.itpub.net/27042095/viewspace-745589/)的第一張圖片,恍然大悟啊!!!趕快改正過來。
hello.sh
#!/usr/bin/env expect
set host [lindex $argv 1]
set passwd [lindex $argv 2]
spawn ssh -l username $host
expect "*password:"
send "$passwd\r"
執行命令:./hello.sh "192.168.1.1" "ispass"
8. 這個一定要注意:如果你在expect中利用ssh登錄了某個服務器,你希望直接進入那個服務器(簡單點說就是你現在是在服務器長操作了,看到的是服務器的文件)一定要使用interact
簡單舉一個例子吧:
#!/usr/bin/expect set timeout 30 spawn ssh -l username 192.168.1.1 expect "password:" send "ispass\r" interact
expect eof
9.程序嘛!還是盡量有expect eof 和exit的好一點;
10. 最大的坑來了,expect的if用法:
if { <條件判斷> } {
<commands>...
} else {
<commands>...
}
也許你沒仔細看,我來提醒你一下:
- if的后面是大括號{},不是();
- if和后面的大括號之間是有一個空格的,你造嗎?
- 注意下面這張圖片的紅色框起來的部分:

你造上面紅色框框起來的部分必須在一行嗎?而且彼此之間是必須有空格分隔的。唉,當時這個問題,都快把自己調試哭了!!!
有了上面的語法,大家簡單編輯一個小程序已經夠了!!!至少完成我的任務我只用到了上面的語法,如果大家想比較深入的學習,下面的幾個網站值得一看呢!!!
很淺的語法:http://www.cnblogs.com/arlenhou/p/learn_expect.html
例子雖短,但給我比較多啟發的一個網站:https://www.centos.bz/tag/expect/
expect學習筆記及實例詳解(真的不錯) :http://blog.itpub.net/27042095/viewspace-745589/
Expect 教程中文版:http://fanqiang.chinaunix.net/a4/b8/20010912/1000001278.html
SCP
scp的語法是簡單又實用,本來還想講一下的。不過實在感覺下面這篇文檔已經太棒了,所以這里直接給個連接吧!!!
SCP 命令:http://www.cnblogs.com/hitwtx/archive/2011/11/16/2251254.html
最后需要提一下的是,本程序中用到了利用ssh控制遠程服務器執行命令的方法:
ssh <username>@<host> "<command1>;<command2>...<command3>"
如:ssh zhangsan@192.168.1.1 "cd /home;ls -l"
注意兩個點:
1、雙引號,必須有。如果不加雙引號,第二個ls命令在本地執行;
2、分號,兩個命令之間用分號隔開。
