Linux Shell遠程執行命令(命令行與腳本方式)

shell遠程執行:

  常常須要遠程到其餘節點上執行一些shell命令,若是分別ssh到每臺主機上再去執行很麻煩,所以能有個集中管理的方式就行了。一下介紹兩種shell命令遠程執行的方法。html

前提條件:

  配置ssh免密碼登錄java

對於簡單的命令:

  若是是簡單執行幾個命令,則:node

ssh user@remoteNode "cd /home ; ls"

  基本能完成經常使用的對於遠程節點的管理了,幾個注意的點:linux

  1. 雙引號,必須有。若是不加雙引號,第二個ls命令在本地執行
  2. 分號,兩個命令之間用分號隔開

對於腳本的方式:

  有些遠程執行的命令內容較多,單一命令沒法完成,考慮腳本方式實現:web

#!/bin/bash
ssh user@remoteNode > /dev/null 2>&1 << eeooff
cd /home
touch abcdefg.txt
exit
eeooff
echo done!

遠程執行的內容在「<< eeooff 」 至「 eeooff 」之間,在遠程機器上的操做就位於其中,注意的點:shell

  1. << eeooff,ssh後直到遇到eeooff這樣的內容結束,eeooff能夠隨便修改爲其餘形式。
  2. 重定向目的在於不顯示遠程的輸出了
  3. 在結束前,加exit退出遠程節點

http://www.cnblogs.com/ilfmonday/p/ShellRemote.htmlapache

目錄 [隱藏]tomcat

SSH命令格式

usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]  
           [-D [bind_address:]port] [-e escape_char] [-F configfile]  
           [-I pkcs11] [-i identity_file]  
           [-L [bind_address:]port:host:hostport]  
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]  
           [-R [bind_address:]port:host:hostport] [-S ctl_path]  
           [-W host:port] [-w local_tun[:remote_tun]]  
           [user@]hostname [command]  

主要參數說明

-l 指定登入用戶
-p 設置端口號
-f 後臺運行,並推薦加上 -n 參數
-n 將標準輸入重定向到 /dev/null,防止讀取標準輸入。若是在後臺運行ssh的話(-f選項),就須要這個選項。
-N 不執行遠程命令,只作端口轉發
-q 安靜模式,忽略一切對話和錯誤提示
-T 禁用僞終端配置
-t (tty)爲遠程系統上的ssh進程分配一個僞tty(終端)。若是沒有使用這個選項,當你在遠程系統上運行某條命令的時候,ssh不會爲該進程分配tty(終端)。相反,ssh將會把遠端進程的標準輸入和標準輸出附加到ssh會話上去,這一般就是你所但願的(但並不是老是如此)。這個選項將強制ssh在遠端系統上分配tty,這樣那些須要tty的程序就可以正常運行。
-v verbose)顯示與鏈接和傳送有關的調試信息。若是命令運行不太正常的話,這個選項就會很是有用。安全

ssh控制遠程主機,遠程執行命令步驟

第一步,設置ssh免認證,免認證就是不用密碼認證就能夠直接登陸,這在寫腳本服務器控制時特別有用。bash

每二步,就是到遠端服務器上去執行命令

準備工做

基於公私鑰認證(可參考:Linux配置SSH密鑰登陸詳解及客戶端測試使用無密碼登陸)或者用戶名密碼認證(可參考:SSH使用expect自動輸入密碼、命令實現非交互式密碼受權)能確保登陸到遠程服務器
cmd若是是腳本,注意絕對路徑問題(相對路徑在遠程執行時就是坑)

基於公私鑰認證遠程登陸可能存在的不足

這個能夠知足咱們大多數的需求,可是一般運維部署不少東西的時候須要root權限,可是有幾處限制:
遠程服務器禁止root用戶登陸
在遠程服務器腳本里轉換身份用expect須要send密碼,這樣不夠安全

ssh 執行遠程命令格式

ssh [options] [user@]host [command]

其中,host爲想要鏈接到的OpenSSH服務器(遠程系統)的名稱,它是唯一的必需參數。host能夠是某個本地系統的名稱,也能夠是因特網上某個系統的FQDN(參見術語表)或者是一個IP地址。命令ssh host登陸到遠程系統host,使用的用戶名與正在本地系統上使用的用戶名徹底相同。若是但願登陸的用戶名與正在本地系統上使用的用戶名不一樣,那麼就應該包含user@。根據服務器設置的不一樣,可能還須要提供口令。

打開遠程shell

若是沒有提供command參數,ssh就會讓你登陸到host上去。遠程系統顯示一個shell提示符,而後就可以在host上運行命令。命令exit將會關閉與host的鏈接,並返回到本地系統的提示符。

例:命令行執行登陸而且在目標服務器上執行命令

ssh user@remoteNode "cd /home ; ls"

基本能完成經常使用的對於遠程節點的管理了,幾個注意的點:
若是想在遠程機器上連續執行多條命令,能夠用單引號或者雙引號將這些命令括起來。若是不加單引號或者雙引號,第二個ls命令在本地執行。例如 ssh user@node cd /local ls 則 ls 只會執行 cd /local 命令,ls命令在本地執行,加了雙引號或者單引號,則被括起來的命令被當作ssh命令的一個參數,因此會在遠程連續執行。
分號,兩個命令之間用分號隔開

例:在目標服務器上執行批量的命令。

#!/bin/bash  
ssh root@192.168.0.23   < < remotessh  
killall -9 java  
cd /data/apache-tomcat-7.0.53/webapps/  
exit  
remotessh  

遠程執行的內容在"< < remotessh " 至" remotessh "之間,在遠程機器上的操做就位於其中,注意的點:<< remotessh,ssh後直到遇到remotessh這樣的內容結束,remotessh能夠隨便修改爲其餘形式。在結束前,加exit退出遠程節點 若是不想日誌文件在本機出現能夠修改配置

ssh root@192.168.0.23 > /dev/null 2>&1   < < remotessh

ssh的-t參數

-t      Force pseudo-tty allocation.  This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.  Multiple -t options force tty allocation, even if ssh has no local tty.  

中文翻譯一下:就是能夠提供一個遠程服務器的虛擬tty終端,加上這個參數咱們就能夠在遠程服務器的虛擬終端上輸入本身的提權密碼了,很是安全
命令格式

ssh -t -p $port $user@$ip  'cmd'  

示例腳本

#!/bin/bash  
  
#變量定義  
ip_array=("192.168.1.1" "192.168.1.2" "192.168.1.3")  
user="test1"  
remote_cmd="/home/test/1.sh"  
  
#本地經過ssh執行遠程服務器的腳本  
for ip in ${ip_array[*]}  
do  
    if [ $ip = "192.168.1.1" ]; then  
        port="7777"  
    else  
        port="22"  
    fi  
    ssh -t -p $port $user@$ip "remote_cmd"  
done  

這個方法仍是很方便的,-t虛擬出一個遠程服務器的終端,在多臺服務器同時部署時確實節約了很多時間啊!

例:查看遠程服務器的cpu信息
假設遠程服務器IP是192.168.110.34
ssh -l www-online 192.168.110.34 「cat /proc/cpuinfo」

例:執行遠程服務器的sh文件
首先在遠程服務器的/home/www-online/下建立一個uptimelog.sh腳本

#!/bin/bash  
  
uptime >> 'uptime.log'  
  
exit 0

使用chmod增長可執行權限

chmod u+x uptimelog.sh

在本地調用遠程的uptimelog.sh

ssh -l www-online 192.168.110.34 "/home/www-online/uptimelog.sh"

執行完成後,在遠程服務器的/home/www-online/中會看到uptime.log文件,顯示uptime內容

www-online@nmgwww34:~$ tail -f uptime.log  
21:07:34 up 288 days,  8:07,  1 user,  load average: 0.05, 0.19, 0.31  

例:執行遠程後臺運行sh
首先把uptimelog.sh修改一下,修改爲循環執行的命令。做用是每一秒把uptime寫入uptime.log

#!/bin/bash  
  
while :  
do  
  uptime >> 'uptime.log'  
  sleep 1  
done  
  
exit 0

咱們須要這個sh在遠程服務器之後臺方式運行,命令以下:
ssh -l www-online 192.168.110.34 「/home/www-online/uptimelog.sh &」

www-online@onlinedev01:~$ ssh -l www-online 192.168.110.34 "/home/www-online/uptimelog.sh &"  
www-online@192.168.110.34's password: 

輸入密碼後,發現一直停住了,而在遠程服務器能夠看到,程序已經之後臺方式運行了。

www-online@nmgwww34:~$ ps aux|grep uptimelog.sh  
1007     20791  0.0  0.0  10720  1432 ?        S    21:25   0:00 /bin/bash /home/www-online/uptimelog.sh

緣由是由於uptimelog.sh一直在運行,並無任何返回,所以調用方一直處於等待狀態。
咱們先kill掉遠程服務器的uptimelog.sh進程,而後對應此問題進行解決。

ssh 調用遠程命令後不能自動退出解決方法
能夠將標準輸出與標準錯誤輸出重定向到/dev/null,這樣就不會一直處於等待狀態。
ssh -l www-online 192.168.110.34 「/home/www-online/uptimelog.sh > /dev/null 2>&1 &」

www-online@onlinedev01:~$ ssh -l www-online 192.168.110.34 "/home/www-online/uptimelog.sh > /dev/null 2>&1 &"  
www-online@192.168.110.34's password:  
www-online@onlinedev01:~$  

但這個ssh進程會一直運行在後臺,浪費資源,所以咱們須要自動清理這些進程。

實際上,想ssh退出,咱們能夠在ssh執行完成後kill掉ssh這個進程來實現。
首先,建立一個sh執行ssh的命令,這裏須要用到ssh的 -f 與 -n 參數,由於咱們須要ssh也之後臺方式運行,這樣才能夠獲取到進程號進行kill操做。
建立ssh_uptimelog.sh,腳本以下

#!/bin/bash  
  
ssh -f -n -l www-online 192.168.110.34 "/home/www-online/uptimelog.sh &" # 後臺運行ssh  
  
pid=$(ps aux | grep "ssh -f -n -l www-online 192.168.110.34 /home/www-online/uptimelog.sh" | awk '{print $2}' | sort -n | head -n 1) # 獲取進程號  
  
echo "ssh command is running, pid:${pid}"  
  
sleep 3 && kill ${pid} && echo "ssh command is complete" # 延遲3秒後執行kill命令,關閉ssh進程,延遲時間能夠根據調用的命令不一樣調整  
  
exit 0  

能夠看到,3秒後會自動退出

www-online@onlinedev01:~$ ./ssh_uptimelog.sh  
www-online@192.168.110.34's password:  
ssh command is running, pid:10141  
ssh command is complete  
www-online@onlinedev01:~$  

而後查看遠程服務器,能夠見到uptimelog.sh 在後臺正常執行。

www-online@nmgwww34:~$ ps aux|grep uptime  
1007     28061  0.1  0.0  10720  1432 ?        S    22:05   0:00 /bin/bash /home/www-online/uptimelog.sh  

查看uptime.log,每秒都有uptime數據寫入。

www-online@nmgwww34:~$ tail -f uptime.log  
22:05:44 up 288 days,  9:05,  1 user,  load average: 0.01, 0.03, 0.08  
22:05:45 up 288 days,  9:05,  1 user,  load average: 0.01, 0.03, 0.08  
22:05:46 up 288 days,  9:05,  1 user,  load average: 0.01, 0.03, 0.08  
22:05:47 up 288 days,  9:05,  1 user,  load average: 0.01, 0.03, 0.08  
22:05:48 up 288 days,  9:05,  1 user,  load average: 0.01, 0.03, 0.08  

附錄:
一、單引號和雙引號在ssh命令中的區別:
以一個例子來講明問題,

假設本地機器上配置了JAVA環境變量,在本地執行 echo $JAVA_HOME=/opt/jdk

倘若我想查看遠程機器上的JAVA環境變量,則只能使用單引號了,ssh user@node ‘ echo $JAVA ‘, 則是’ ‘ 中的$JAVA不會被shell解析,而是當作一個字符串,此時參數 echo $JAVA 傳遞給了 ssh;

若是咱們使用 ssh user@node 」 echo $JAVA 「,則 shell 首先會解析$JAVA,獲得它的值,則該命令就變成了 ssh user@node ‘ echo /opt/jdk ‘ 了

二、可能遇到的問題
問題:遠程登陸主機時出現Pseudo-terminal will not be allocated because stdin is not a terminal. 錯誤
解決方案:字面意思是僞終端將沒法分配,由於標準輸入不是終端。

因此須要增長-t -t參數來強制僞終端分配,即便標準輸入不是終端。
to force pseudo-tty allocation even if stdin isn’t a terminal.

參考樣例以下:
ssh -t -t user1@host1 -p 9527

參考資料:
ssh遠程執行命令並自動退出:http://blog.csdn.net/fdipzone/article/details/23000201

http://www.3mu.me/linux%E4%B8%AD%E7%9A%84shell%E7%94%A8ssh%E8%87%AA%E5%8A%A8%E7%99%BB%E5%BD%95%E8%BF%9C%E7%A8%8B%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%90%8E%E6%89%A7%E8%A1%8C%E5%91%BD%E4%BB%A4%E5%B9%B6%E8%87%AA%E5%8A%A8/

相關文章
相關標籤/搜索