shell 常用命令
expect
expect 命令是用來實現自動化交互通信的,比如當你在腳本中通過某些命令登錄、連接、上傳、下載等和遠程服務器的交互時,可能需要讓你輸入一些賬號、密碼等信息
如果這個過程是人工手動在終端執行的,那沒啥問題,需要交互時,你手動輸入即可
但如果這個過程是交由腳本來自動執行的,難不成每次腳本執行時,還需要你在旁邊等着來輸密碼嗎?
所以,就可以借助 expect 來實現這個交互過程
可以先用 whereis
命令查找看看,是否支持 expect 命令,否則需要自行安裝
whereis expect
# expect: /usr/bin/expect /usr/share/man/man1/expect.1.gz
安裝
# Centos 系統的安裝,其余的自行查閱
#先下載 tcl,expect 依賴 tcl
yum -y install tcl
#再下載 tcl
yum -y install expect
#安裝完執行 expect,查看是否安裝成功,失敗原因請自行查閱
[root@VM_0_15_centos test]# expect
expect1.1>
基本命令
-
spawn:用於啟動一個子進程來執行后續命令
-
expect eof:用來退出 spawn 啟動的子進程,返回到當前進程環境,與 spawn 成對出現
-
expect:用於接收進程的輸出信息(輸出重定向到 expect),如果接收的字符串與期待的不匹配,則一直阻塞,直到匹配上或者超過才繼續往下執行
-
send:用於向進程發送輸入信息(輸入重定向到 send),通常需要以
\n
結尾 -
set timeout 1:用於設置 expect 命令的超時時間,單位 s,輸入 -1 時表示無限長,默認為 10s
-
[lindex $argv n]:用於獲取傳入給腳本的參數,n 表示第幾個參數,下標從 0 開始
-
set key value:用於設置變量,通常在腳本文件開頭結合上面獲取參數使用,來給參數賦值個有意義的變量,如
set ip [lindex $argv 0]
-
exp_continue:用於 expect 中需要復用匹配
-
send_user:用於打印輸出,相當於 echo
-
interact:結束自動化交互,轉入人工交互,如果腳本是純自動化場景,那么不應該有這條命令,腳本執行結束則退出。如果是半自動化場景,如自動輸入賬號密碼,連接登錄操作,登錄之后交由人工交互,那可以使用該命令
expect 命令后面可跟隨字符串或對象,如:
expect "password"
send "xxx"
# 或者
expect {
"yes" {
send "yes\n"
exp_continue
}
"password" {
send "xxx"
expect "xxx"
send "xxx"
}
}
使用
#!/usr/bin/expect
# 使用 expect 結合 ssh 登錄遠程服務器
# 將傳給腳本的參數賦值給變量
set sshPort [lindex $argv 0]
set user [lindex $argv 1]
set ip [lindex $argv 2]
set password [lindex $argv 3]
# spawn 命令新啟子進程,執行后面的命令
spawn ssh -p $sshPort $user@$ip
expect "password"
send "$password\n"
expect eof
exit 0
在當前 shell 里執行:
/usr/bin/expect login.sh "22" "root" "127.0.0.1" "xxxxx"
其實,個人覺得,expect 就是利用了 shell 的輸入輸出重定向,原先在終端里人工進行交互時,由人工手動將命令輸入給終端,命令執行結果輸出到終端給用戶反饋
而 expect 則是將輸入由傳統的人工輸入給終端重定向到由 send 命令輸入,也就是輸入信息從 send 讀取,而命令執行結果也不是輸出給終端,而是輸出給 expect 命令,這樣一來,就可以實現由腳本來自動化處理交互,畢竟輸入輸出腳本都可以拿到了
以上,個人理解
ssh
遠程連接工具,用來登錄遠程服務器
通常來說,借助 XShell 的可視化配置,就足夠連接上服務器了,但有些服務器,只能通過跳板機連接,此時就需要先登錄上跳板機,然后再跳板機上使用 ssh 命令來連接服務器
當然,XShell 也可以通過 ssh 來連接登錄服務器,你不用圖形界面的操作也行
使用
ssh -p 22 root@ip
很簡單,指定端口,登錄用戶,ip 地址就可以了,然后再手動輸入密碼
如果不想每次登錄都輸入密碼,那么需要配置 ssh 私鑰、公鑰,將公鑰放置在服務器上
netstat -lntup | grep ssh
遠程服務器上查看 ssh 端口號,默認是 22
service sshd reload
重啟 ssh 服務,因為可能需要改配置文件,如修改默認 22 端口
scp
scp 命令用於 Linux 之間復制文件和目錄,也就是直接跟遠程服務器進行文件或目錄的拷貝
跟 cp 很類似,區別在於一個僅在本機間拷貝,一個是多機子間的拷貝
語法
usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user@]host1:]file1 ... [[user@]host2:]file2
# 簡易寫法
scp [可選參數] file_source file_target
比較重要的也就是最后的 [[user@]host]file,可以指定連接遠程的用戶,ip,不指定時,將在命令執行時,手動輸入
常用參數說明
-
-p:保留原文件的修改時間,訪問時間和訪問權限。
-
-r: 遞歸復制整個目錄。
-
-v:詳細方式顯示輸出。scp和ssh(1)會顯示出整個過程的調試信息。這些信息用於調試連接,驗證和配置問題。
-
-P:注意是大寫的P, port是指定數據傳輸用到的端口號
實例
- 從本地復制到遠程
# 將本地多個文件復制到遠程 target 目錄下,多個文件間以空格隔開
scp -P 5432 -p xxx.jar xxx1.jar root@ip:target
# 將本地文件 1.mp3 拷貝到遠程服務器上的 001.mp3 文件里
scp /home/space/music/1.mp3 root@www.runoob.com:/home/root/others/music/001.mp3
- 從遠程拷貝到本地
# 將遠程目錄拷貝到當前目錄下
scp -r root@192.16.1.108:/var/www/blog .
當沒有指定用戶名時,命令輸入完畢需要手動輸入用戶名和密碼,指定了用戶名后,需要輸入密碼
密碼的輸入可以借助 expect 來實現自動交互
當然,也可以通過 -B,以及其他參數來指定 ssh 連接的配置文件,實現無密碼方式拷貝,相關信息,自行查閱
sz, rz
如果是借助 XShell 工具連接上遠程服務器后,那么可借助 sz, rz 命令來下載和上傳文件
但如果遠程服務器沒有這兩個命令的話,需要先進行安裝:
安裝
# CentOs 安裝
yum -y install lrzsz
# Ubuntu 安裝
sudo apt-get install lrzsz
使用
sz xxx.file
rz
下載文件的話,需要指定下載哪個文件,上傳則不用,因為會打開文件選擇彈框,選中即可
sftp
也是一個用來跟遠程服務器上的文件進行下載、上傳的命令
sz,rz 通常是本機上裝了個 XShell 工具,然后用於兩機之間的文件通信
但有時候,是需要在遠程多個服務器之間進行文件通信,這時候就用不了 XShell,也就用不了 sz, rz 命令了
這種場景,可以使用 scp 命令,也可以使用 sftp 命令
使用
# 先連接,端口跟 ssh 一致
sftp -P 22 root@ip
# help 命令,可以查看 sftp 支持的命令,比如 ls,lls,cd,lcd...
help
# 所有命令前加 l,表示針對本機的操作,不加 l 表示針對遠程服務器的操作
# 本機進入 tmp 目錄
lcd /tmp/
# 下載遠程文件到當前目錄下
get /usr/local/xxx.file
# 上傳當前目錄下的文件到遠程指定目錄下
put xxx.file /usr/local/
有一點需要注意,如果本機是 window 系統,那么在 window 系統和 linux 系統之間是有 sftp 傳輸文件時,由於文件系統的分隔符不一樣,在操作 lcd 命令時,可能會有問題,此時,可以直接輸入 lcd,然后會彈窗文件選擇框,選中路徑后按確定即可,比較方便
tail
通常用來實時查看日志文件:
tail -f xxx.log
這樣,只要有新日志寫入,會馬上在終端上輸出,就可以不用每次都把文件下載下來了
實例-jenkins 構建 spring-boot 項目並部署遠程服務器上
場景是這樣的,本地開發后端 spring-boot 項目,然后有一台專門的 jenkins 服務器,自動或手動觸發構建
jenkins 構建時,會自動去拉取代碼,然后執行 package.sh 打包腳本,生成 jar 包
再然后,執行 deploy.sh 腳本,將 jar 發送到另一台項目運行的服務器上,先停止舊項目的執行,然后移除舊 jar 包,執行新 jar 包,啟動后端項目
package.sh
打包腳本
#!/bin/sh
# 打包的渠道由外部傳入
environment=$1
basedir=`pwd`
# 先將舊的打包文件刪除, -d 表示判斷 target 是否是目錄
if [ -d "target" ]; then
rm -rf $basedir/target
fi
mkdir target
# 定義 mvn 打包的函數
package()
{
# mvn packgae打包
(mvn clean packge -P $environment)
# 如果 mvn 命令執行異常,將會返回非0,終止腳本,異常退出
if [ $? -ne 0 ]; then
exit 1
fi
}
# 進入項目根目錄,執行打包工作
cd $basedir/app
package
exit 0
deploy.sh
部署腳本
#!/usr/bin/expect
# 將傳給腳本的參數賦值給變量
set ip [lindex $argv 0]
set port [lindex $argv 1]
set user [lindex $argv 2]
set password [lindex $argv 3]
set targetDir [lindex $argv 4]
# 先另起進程,用 scp 命令,將打包好的 jar 包發送到項目運行的服務器上
spawn scp -P $port target/xxx.jar $user@$ip:$targetDir
# 用 expect 解決 scp 需要輸入命令的交互,實現自動化
expect {
"yes/no" {
send "yes\n"
exp_continue
}
"password" {
send "$password\n"
}
}
# jar 發送完畢就退出子進程,返回主進程,繼續處理往下命令
expect eof
# 另起進程執行 ssh 連接項目運行的服務器
spawn ssh -p $port -o "StrictHostKeyChecking no" $user@$ip
# 用 expect 解決 ssh 需要輸入命令的交互,實現自動化
expect {
"password" {
send "$password\n"
}
}
# 登錄成功,則發送需要在遠程服務器上執行的命令
expect "login"
# 包括,停止舊項目,執行新項目
send "
# 進入 jar 包存放目錄
cd xxx
# 停止運行
kill -9 `ps aux|grep 'xxx.jar'|grep -v grep|awk '{print $2}'`
# 備份
cp xxx.jar xxx.jar.bak
# 移新包
mv $targetDir/xxx.jar xxx
# 啟動項目
su - www -c "nohup java -jar xxx.jar &"
sleep 10s
exit 0
"
expect eof
exit 0
jenkins - 構建 shell
jenkins 配置里的構建 shell 命令
#!/bin/sh
source /etc/profile
# 先執行打包腳本,指定打包的渠道
sh jenkins/package.sh online
# 如果打包失敗,終止
if [ $? -ne 0 ]; then
exit 1
fi
# 執行部署腳本,指定項目運行的服務器的連接端口號,ip,登錄用戶,密碼,移包路徑
/usr/bin/expect jenkins/deploy.sh "127.0.0.1" "22" "root" "xxxx" "/temp/"