我正在尝试通过ssh
远程执行命令。但我将调用命令保存为变量,例如:
SSH_CMD="ssh -i path_to/identity"
SSH_USER=user
SSH_DST=server
dir1=dir1
dir2=dir2
现在如果我
eval $SSH_CMD $SSH_USER@$SSH_DST "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
我收到错误(通常是mv: cannot stat 'dir1': No such file or directory
:)
但如果我:
ssh -i path_to/identity $SSH_USER@$SSH_DST "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
这样可行。所以问题似乎出在我的处理方式上eval
。正确的方法是什么?
答案1
eval $SSH_CMD $SSH_USER@$SSH_DST "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
这实际上与运行相同
eval "$SSH_CMD $SSH_USER@$SSH_DST mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
eval
将它获取的参数连接到单个字符串,然后将其作为 shell 命令运行。特别是,&&
作用在本地端,所以 shell 首先运行ssh ... mkdir dir1
,如果成功,它运行mv dir1 dir2
,等等,mkdir
在远程主机上运行,但mv
在本地主机上dir1
不存在。
这里没有必要使用eval
。你的第一个命令不需要它:
$SSH_CMD $SSH_USER@$SSH_DST "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
$SSH_CMD
什么时候不是引用后,它会被分成三个单词ssh
, -i
, 和path_to/identity
正如你想要的那样。这可行,但有点不干净,如果您必须在 中包含带有空格的参数$SSH_CMD
,或者在那里使用全局字符,您会遇到麻烦。
如果您需要存储可变数量的命令参数(例如 in ) SSH_CMD
,最好使用数组。另外,也引用其他变量以防止分词:
ssh_cmd=(ssh -i path_to/identity)
ssh_user=user
ssh_host=server
dir1=dir1
dir2=dir2
"${ssh_cmd[@]}" "$ssh_user@$ssh_host" "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"
答案2
我认为没有理由在这里使用eval
,它只是在您的命令中引入了许多潜在漏洞。
您应该尽可能将选项存储在数组中,而不是像这样的变量:
SSH_OPTS=( -i path_to/identity )
SSH_USER=user
SSH_DST=server
dir1=dir1
dir2=dir2
那么你应该确保总是双引号你的变量:
ssh "${SSH_OPTS[@]}" "${SSH_USER}@${SSH_DST}" "mkdir $dir1 && mv $dir1 $dir2 && touch $dir2"