我有一个本地脚本,我想在远程机器上运行而不将其复制到机器上,因此我使用: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 变量中以代替源语句。