我正在尝试创建一个行为类似于“通过 ssh 的符号链接”的命令,即像在本地调用一样调用远程脚本。我的脚本是:
#!/bin/zsh
if (( "$#" = 0 )); then
echo "Usage: $0 <number>" >&2; exit 1
fi
if ! [[ "$1" =~ '^[0-9]+$' ]]; then
echo "error: “$1” is not a valid number" >&2; exit 1
fi
为此,我将使用<(command)
,一个进程替换,它将创建一个具有类似路径的fifo /proc/self/fd/<n>
,将command
的标准输出重定向到它,并对该路径进行评估。fifo=<(echo 'hi!'); echo $fifo; cat $fifo
将回显/proc/self/fd/14
(或如此)然后hi!
。
所以这应该可以解决问题,让我们看看“用法”是否有效。我将把这个脚本保存到我的 $PATH 中并通过其文件名执行它exec-remote
。
#!/bin/zsh
source <(ssh myserver 'cat bin/mycommand')
差不多了!远程脚本会说
Usage: /proc/self/fd/12 <number>
代替
Usage: exec-remote <number>
…这意味着在获取远程脚本的代码时,$0
将其设置为进程替换的 fifo 路径。
但是 zsh 的source
命令似乎只接受位置参数($@
):
#!/bin/zsh
source <(ssh myserver 'cat bin/mycommand') $0
…会让我的脚本说:
error: “/proc/self/fd/12” is not a valid number
那么,我怎样才能让 zsh 执行我的远程代码,同时$0
又不影响它执行呢?
答案1
我认为source
这只是一种阻碍。将以下内容(本地)保存为exec-from-myserver
:
#!/bin/sh
name=${0##/}
exec zsh -c "$(ssh myserver "cat 'bin/$name'")" "$0" "$@"
使其可执行,然后符号链接到它:
ln -s exec-from-myserver mycommand
ln -s exec-from-myserver foo-bar-baz
# etc.
(注意:字符串cat 'bin/whatever_name_you_chose'
将是解析在远程端。带有文字的名称'
会破坏代码或使其行为不当;您甚至可以通过以某种方式命名符号链接来执行代码注入。由于您选择名称,所以这并不重要。使用常规名称就可以了。)
现在如果你调用mycommand
,本地脚本将exec
解释zsh
远程bin/mycommand
,并$0
在其上下文中将其设置为上一个(本地)的有用值$0
。调用foo-bar-baz
将尝试拉取并解释远程bin/foo-bar-baz
。示例:
$ ./mycommand
Usage: ./mycommand <number>
$ # This was your remote script talking.
$ ./mycommand 1
$ # Executed without complaint.
$ ././mycommand
Usage: ././mycommand <number>
$ # As you can see the remote script is aware of its local name and path used.
$ ./foo-bar-baz
cat: bin/foo-bar-baz: No such file or directory
$ # This was the remote cat talking, only because there is no remote foo-bar-baz.
笔记:
- 我的术语中的一个细微差别:“远程脚本”作为文件存在于远程端,但它在本地被解释;“远程猫”是真正远程的。
exec-from-myserver
总是使用zsh -c
,远程脚本中的shebang被完全忽略(您的实验source
也忽略了它)。