如何透過 ssh 在遠端電腦上執行腳本但來源本機文件

如何透過 ssh 在遠端電腦上執行腳本但來源本機文件

我有一個本地腳本,我想在遠端計算機上運行,而不將其複製到計算機,因此我使用: ssh user@remote < local-script.sh

這是可行的,但如果我在 local-script.sh 中新增來源語句來取得另一個文件,例如source ./another-local-script.sh,當 local-script.sh 在遠端執行時,它會在遠端上尋找來源檔案。有什麼方法可以解決這個問題,以便首先在本地解析原始檔案嗎?

答案1

你不能透明地做到這一點。

在獲取文件之前,您可以修改 bash 腳本以使用反向隧道從本機電腦取得文件,但這並不是很乾淨。

更好的方法是傳輸您需要的所有文件,然後按如下方式運行它們:

scp local-script.sh another-script.sh user@remote
ssh user@remote local-script.sh

答案2

透過控制其內容的一組受限輸入文件,您可以使用awk或類似的方法將 stdin 流中的命令替換source為來源檔案。例如,

desource <local-script.sh | ssh user@remote 

其中 desource 是腳本

#!/bin/sh
awk '$1=="source" && NF>=2 {
      file = $2; while((getline <file)>0)print $0
      close(file); next
}
{ print }' "$@"

這僅匹配第一個單字是“source”的行,並將第二個單字作為要插入的檔案。getline從該檔案中讀取一行(寫入 $0)並在檔案末尾傳回 0。該print行僅複製不符合的行。

顯然,這在其應用中受到很大限制,例如,如果包含的文件也有 source命令,則需要一些工作來遞歸。


使用變數代替 $0 的替代 getline:

while((getline inp <file)>0)print inp

使用替代腳本sed。它讀取檔案兩次,因此使用需要檔案名稱(刪除“<”即desource local-script.sh | ssh user@remote:)

#!/bin/bash
file=${1?}
cmd=$(  sed -n '/^[ \t]*source[ \t]/{=;s///;p}' <$file |
    sed  '/^[0-9]/{N;s/\n\(.*\)/{r \1;d;}/}' |
    tr ';' '\012' )
sed "$cmd" <$file

這第一次使用 sed 來匹配source行,列印出行號 (=) 並僅保留檔案名稱(s/// 重複使用相同的模式)。第二個 sed 取得行號,附加下一行 (N) 並替換換行符號和後面的檔案名稱(.* 是行的其餘部分),這將成為 sed 命令來讀取所需檔案並刪除原始行。將tr命令中的分號轉換為換行符。將產生的命令提供給原始文件上的第三個 sed。

答案3

感謝 @meuh 的幫助,但由於我無法讓你的範例在 Mac OS X 上運行,我想出了一個可以完成這項工作的函數,儘管我確信你們中的許多人都可以對此進行改進:

# desource - substitute source statements in file with contents from sourced file
function desource {
  if [ ! -f "$1" ]; then
    echo "Invalid file: $1"
    return
  fi
  declare tmp
  while read -r line; do
    if [ "${line:0:1}" == '#' ]; then continue; fi
    file=$(echo "$line" | perl -nle 'print $1 if m{(?>source )(.+)}' | sed 's|"||g')
    if [ -n "$file" ]; then
      case "${file:0:1}" in
        '/') ;;
        '~') ;;
        '$') ;;
          *) file="$(cd `dirname $1` && pwd)/$file"
      esac
      file=$(echo "$file" | sed "s|~|$HOME|" | sed "s|\$HOME|$HOME|")
      file="$(cd `dirname $file` && pwd)/$(basename $file)"
      if [ -f "$file" ]; then
        tmp="$tmp\n$(cat $file)"
      else
        echo "Invalid file: $file"
        return
      fi
    else
      tmp="$tmp\n$line"
    fi
  done < "$1"
  echo -e "$tmp"
}

基本上,它逐行讀取文件,並將每一行插入到名為 tmp 的變數中。如果該行包含來源語句,它將取得檔案名,解析檔案的絕對路徑(並假設任何相對路徑都相對於傳入的腳本)。它進行檢查以確保檔案存在,然後將這些檔案的內容新增到 tmp 變數中以取代來源語句。

相關內容