我如何通过 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 XI 上运行,因此我想出了一个可以完成该工作的函数,尽管我相信你们中的许多人都可以改进这一点:

# 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 变量中以代替源语句。

相关内容