【shell】shell實現交互


目錄

一、SHELL

1.1 輸入單個指令

1.2 輸入多行指令

限制輸入內容的個數

控制輸入內容的可見性

 二、Expect

簡介

實例

SSH登錄

 FTP文件同步


一、SHELL

1.1 輸入單個指令

自動輸入yes

echo "y" | yum install wget ,等同於yum -y  install wget

自動輸入回車

echo -e "\n" | yum remove wget

**`echo -e` 的小知識**

若字符串中出現以下字符,則特別加以處理,而不會將它當成一般文字輸出:
\a 發出警告聲;
\b 刪除前一個字符;
\c 最后不加上換行符號;
\f 換行但光標仍舊停留在原來的位置;
\v 與\f 相同;
\n 換行且光標移至行首;
\r 光標移至行首,但不換行;
\t 插入 tab 符號;
\\ 插入 ‘' 字符;
\nnn 插入 nnn(八進制)所代表的 ASCII 字符;

1.2 輸入多行指令

輸入多行指令我們需要借助輸入重定向操作符 <<

以下面這個腳本為例

multi.sh

#!/bin/bash

read -p "enter number:" no
read -p "enter name:" name
echo "you have entered $no, $name"

借助 << 符號進行自動化輸入

#!/bin/bash
sh multi.sh << EOF
1
mutoe
EOF

但是有時候這種方法並不生效,比如 ssh-keygen 命令,那只有借助強大的 expect 命令了

read -p "you are sure you wang to xxxxxx?[y/n]" input
echo $input
if [ $input = "y" ];then
    echo "ok "
fi

限制輸入內容的個數

我們還可以使用 read命令的-n 選項,此選項后面需要接一個數字,可以限制輸入內容的個數。

#!/bin/bash
read -p "May I ask your name: " name
echo "Hello $name"
read -n1 -p "Press any key to exit"
echo
exit 0

控制輸入內容的可見性

目前,我們輸入的內容都是可見的,但有些敏感的數據,如密碼,信用卡號等信息,輸入時並不想可見。那么可以使用read -s

這時再輸入時,就有一個鑰匙的標識,而且輸入時不可見。
 

 二、Expect

簡介

 expect是一個免費的編程工具,用來實現自動和交互式任務通信,安裝 yum install -y expect 

Expect的語法:

關鍵命令send、expect、spawn和interact:

  • send:用於向進程發送字符串,注意一定要在末尾加\r回車
  • expect:從進程接收字符串
  • spawn:啟動新的進程
  • interact:允許用戶繼續交互

expect 有兩種用法,一種是直接寫 expect 解釋器的腳本,和 bash 類似,以 #!/usr/bin/expect 開頭

下面是一個合格的 expect 腳本示例

#!/bin/expect

set IP     [lindex $argv 0] # 讀取第1個參數設置為 IP 變量
set PASSWD [lindex $argv 1] # 讀取第2個參數設置為 PASSWD 變量
set CMD    [lindex $argv 2] # 讀取第3個參數設置為 CMD 變量

spawn ssh $IP $CMD # spawn 來給命令加殼,以便於斷言輸出
expect { # expect 是斷言命令
  # 如果讀取到屏幕上輸出 (yes/no) 信息,則輸入 "yes" 並按下回車鍵
  # exp_continue 是繼續等待花括號內的斷言, 如果不加這一句會直接跳出 expect
  "(yes/no)?" { send "yes\r"; exp_continue }

  "password:" { send "$PASSWD\r" } # 如果讀取到屏幕上輸出 password 信息,則輸入 PASSWD 變量中的內容
  "*host " { exit 1 } # 如果讀取到 "No route to host" 等內容, 就以非0狀態退出
}
expect eof # 等待命令執行結束

需要注意的是,在 expect 解釋器內, 除了幾個特定關鍵字的命令,其他命令都不可用,這種方式適用於執行命令較少,單次需要交互較多的自動化腳本

第二種用法是在 bash 腳本中執行 expect 配合重定向操作符, 在有大量腳本需要執行的情況下推薦使用該方式

下面是我在 certbot 命令時使用的 shell 腳本,以供參考

#!/bin/bash

sudo expect << EOF
spawn certbot --nginx
expect {
  "Enter email address" { send "mutoe@foxmail.com\n";exp_continue}
  "Please read the Terms of Service" {send "A\n";exp_continue}
  "Would you be willing to share your email address" {send "N\n";exp_continue}
  "Which names would you like to activate HTTPS for" {send "\n";exp_continue}
  "You have an existing certificate that has exactly the same domains" {send "1\n";exp_continue}
  "Please choose whether or not to redirect HTTP traffic to HTTPS" {send "2\n";exp_continue}
  eof
}

 expect是關鍵的部分,在英文中,expect有“期待”的意思,采用了tcl的模式-動作語法,此語法有以下幾種模式:
單一分支語法:

expect "hello" {send "you said hello"}

多分支模式語法:

expect {
      "hello" {send "hello\r"; exp_continue}
      "world" {send "world\r"; exp_continue}
      "how are you ?" {send "Fine,thanks\r"}
}

實例

SSH登錄

例:expect腳本ssh.exp內容:

#!/usr/bin/expect 
set timeout 2
spawn ssh wan@10.229.130.107
expect {
    "[Pp]assword" {send "123456\r";}
    "[Yy]es/no" {send "yes\r";exp_continue}
}
...
...
...
send "exit\r"
exit 0

#send:用於向進程發送字符串,注意一定要在末尾加\r回車
#expect:從進程接收字符串
#spawn:啟動新的進程
#interact:允許用戶繼續交互

 FTP文件同步

經常要面臨在某個環境編輯代碼然后需要自動同步更新到其他機器的情況。對於這種問題,我們也可以借助expect腳本來實現。

例:
配置文件為sync.cfg:

#","分隔的內容分別為需要同步的文件、目標路徑和目標主機密碼
/home/long/tmp/*.py,wan@10.229.130.107:/home/wan/tmp,123456

shell腳本sync_ftp.sh內容:

#!/bin/bash
#讀取配置信息
for line in `cat sync.cfg`
do
     #刪除存在的信息文件,避免數據污染
     if [ -f info.dat ]
     then
         rm -rf info.dat
     fi
     touch info.dat

     #獲取路徑和密碼
     srcaddr=`echo $line | cut -d , -f 1`
     destaddr=`echo $line | cut -d , -f 2`
     passwd=`echo $line | cut -d , -f 3`

    #查找指定日期需要同步的文件
    for tmp_file in `find $srcaddr -mtime 1 -type f`
    do
         echo $tmp_file >> info.dat
    done
 
    #調用expect腳本進行同步
    expect sync_ftp.exp $destaddr $passwd

   #判斷是否同步成功
    if [ $? -eq 0 ]
    then
       rm -rf info.dat
       echo "all success!"
    else
       echo "sync $destaddr fail!"
    fi
done

exit 0

expect腳本sync_ftp.exp:

#!/usr/bin/expect 
set timeout 20

#讀取輸入參數
set remotehost [lindex $argv 0]
set remotepass [lindex $argv 1]

#啟動新進程,運行sftp協議
spawn sftp $remotehost
expect "[Pp]assword*" 
send "$remotepass\r"

#發送文件
expect "sftp>" 
set content [ open info.dat ]
while  { [ gets $content local_file ] != -1 }
{
     send "put $local_file\r"
     expect "sftp>" 
}
send "exit\r"
exit 0

由於文件同步是每天都需要進行的,因此可以結合定時任務crond來進一步簡化工作。crond是類unix系統下用來周期執行某種任務或者事件的一個守護進程,配置信息存放在/etct/crontab文件中。crontab是系統服務crond的控制命令。關於crontab的使用,在進程章節已經進行了介紹,這里就不再贅述了。

Expect命令是很強大的,可以實現各種自動化操作。自此,你不僅可以寫出各種厲害的shell腳本,還能讓它們自動執行,對於日常工作的處理將會有大大的提升。

原文:https://zhuanlan.zhihu.com/p/38412160

注:expect命令

[set timeout 30]     
    基本上認識英文的都知道這是設置超時時間的,現在你只要記住他的計時單位是:秒   
    
[spawn ssh -l username 192.168.1.1]    

spawn command命令會fork一個子進程去執行command命令,然后在此子進程中執行后面的命令;   

spawn是進入expect環境后才可以執行的expect內部命令,如果沒有裝expect或者直接在默認的SHELL下執行是找不到spawn命令的。所以不要用 “which spawn“之類的命令去找spawn命令。好比windows里的dir就是一個內部命令,這個命令由shell自帶,你無法找到一個dir.com 或 dir.exe 的可執行文件。    

    它主要的功能是給ssh運行進程加個殼,用來傳遞交互指令。   
   
[expect "password:"]   
    這里的expect也是expect的一個內部命令,有點暈吧,expect的shell命令和內部命令是一樣的,但不是一個功能,習慣就好了。這個命令的意思是判斷上次輸出結果里是否包含“password:”的字符串,如果有則立即返回,否則就等待一段時間后返回,這里等待時長就是前面設置的30秒   
   
[send "ispass\r"]   
    這里就是執行交互動作,與手工輸入密碼的動作等效。   
    溫馨提示: 命令字符串結尾別忘記加上 “\r”,如果出現異常等待的狀態可以核查一下。   
   
[interact]    

    執行完成后保持交互狀態,把控制權交給控制台,這個時候就可以手工操作了。如果沒有這一句登錄完成后會退出,而不是留在遠程終端上。如果你只是登錄過去執行一段命令就退出,可改為[expect eof]

expect可以讓你使用“-c”選項,直接在命令行中執行它,例如:

expect -c '
        spawn git pull
        expect {
        "Usernam*" {send "code_manager@ren001.com\n"; exp_continue }
        "Passwor*" {send "$GIT_CODE_PASSWORD\n" ; exp_continue}
        }
        '
原文鏈接:https://blog.csdn.net/appke846/article/details/80513099


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM