我有一个只能以用户名(myuser)登录的系统,但是我需要以其他用户(scriptuser)的身份运行命令。到目前为止,我已经提出了以下命令来运行所需的命令:

ssh -tq myuser@hostname "sudo -u scriptuser bash -c \"ls -al\""


但是,当我尝试运行更复杂的命令时,例如[[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"我很快就惹麻烦了。我不确定如何将示例复杂命令传递给bash -c,当\"已经界定了我要传递的命令的边界时(所以我不知道如何引用/ tmp / Some目录,其中包含一个空格) 。

是否有通用的解决方案允许我传递任何命令,而不管报价多么复杂/疯狂,还是我遇到了某种限制?是否还有其他可能的并且也许更具可读性的解决方案?

评论

将脚本复制到$ remote,然后执行。

那行得通,但是我发现一个不留下任何脚本文件的解决方案(以防万一失败等等)会更干净一些。

每次运行结束时都有脚本rm本身

考虑使用fabric fabfile.org。如果您需要远程进行sudo操作,可以使您的生活更加轻松。

如果您已安装Ansible,则此命令将显示您的用例的文档:ansible-doc脚本

#1 楼

我有时使用的一个技巧是使用base64对命令进行编码,然后将其通过管道传输到其他站点的bash上:

MYCOMMAND=$(base64 -w0 script.sh)
ssh user@remotehost "echo $MYCOMMAND | base64 -d | sudo bash"


这将对脚本进行编码,并带有任何逗号,安全字符串中的反斜杠,引号和变量,然后将其发送到其他服务器。 (需要-w0来禁用换行,默认情况下,换行发生在第76列)。另一方面,$(base64 -d)会解码脚本并将其馈送给bash以执行。通过转义来解决问题,因为您无需逃避任何操作。它不会在远程主机上创建文件,并且您可以轻松运行非常复杂的脚本。

评论


甚至:ssh user @ remotehost“ echo`base64 -w0 script.sh | base64 -d | sudo bash”

–尼古拉斯B.
2014年9月3日23:49



值得注意的是,在大多数情况下,可以将lzop或gzip替换为base64,以加快传输速度。但是,可能存在边缘情况。 YMMV。

– CodeGnome
2014年9月4日在12:29

请注意,但如果是脚本,我希望任何传输都不会超过几kb。

–ThoriumBR
2014年9月4日12:33

这里也有无用的回声用法。 base64脚本| ssh remotehost'base64 -d | sudo bash'就足够了:)

–霍布斯
2014年9月5日在7:48

虽然我还没有测试过该解决方案,但是它会输出脚本的结果,例如在stdout中输出'yum check-update'吗?我想问一下,因为如果有人认为我做的事情显然很幼稚,那么我只能捡些东西。

– Soham Chakraborty
16年8月22日在7:52

#2 楼

看到-tt选项吗?阅读ssh(1)手册。

ssh -tt root@host << EOF
sudo some # sudo shouldn't ask for a password, otherwise, this fails. 
lines
of
code 
but be careful with $variables
and $(other) \`stuff\`
exit # <- Important. 
EOF


我经常做的一件事是使用vim并使用:!cat % | ssh -tt somemachine技巧。

评论


当然:!cat%| (command)可以用:w!(command)或:!(command)<%来完成。

–斯科特
2014年9月4日于17:51

是。我的路线是虐待猫

– moebius_eye
2014年9月5日在9:13

运行ssh -tt导致我的ssh在某些命令运行后不会终止(在末尾添加退出无济于事)

–user498092
18/12/7在10:01

#3 楼

我认为最简单的解决方案是修改@thanasisk的注释。

创建脚本,scp到计算机上,然后运行它。

将脚本rm放在开始。外壳程序已打开文件,因此已将其加载,然后可以毫无问题地将其删除。在某个时候失败。

#4 楼

您可以将%q格式说明符与printf一起使用,以照顾变量转义:

cmd="ls -al"
printf -v cmd_str '%q' "$cmd"
ssh user@host "bash -c $cmd_str"


printf -v将输出写入变量(在这种情况下,为$cmd_str) 。我认为这是最简单的方法。不需要传输任何文件或对命令字符串进行编码(就我喜欢的窍门而言)。

这里有一个更复杂的示例,它也适用于方括号和&符: />
$ ssh user@host "ls -l test"
-rw-r--r-- 1 tom users 0 Sep  4 21:18 test
$ cmd="[[ -f test ]] && echo 'this really works'"
$ printf -v cmd_str '%q' "$cmd"
$ ssh user@host "bash -c $cmd_str"
this really works


我还没有用sudo对其进行过测试,但它应该像这样简单:

ssh user@host "sudo -u scriptuser bash -c $cmd_str"


如果您如果需要,您可以跳过一个步骤并避免创建中间变量:

$ ssh user@host "bash -c $(printf '%q' "$cmd")"
this really works


,或者甚至完全避免创建变量:

ssh user@host "bash -c $(printf '%q' "[[ -f test ]] && echo 'this works as well'")"


评论


这是一个很棒的解决方案。实际上,在类似情况下,我以前必须使用过printf,但是我总是忘记了。感谢您发布此:-)

–乔恩·L。
15年4月23日在7:26

#5 楼

更新:示例现在显式使用sudo。这是一种将Bash语法与复合赋值结合使用的方法,可通过sudo通过SSH执行任意复杂的命令: br />正确格式化命令以便在其他地方动态执行(例如嵌套shell,远程SSH shell等)可能非常棘手-有关原因的详细说明,请参见https://stackoverflow.com/a/53197638/111948。复杂的bash结构(如上面的语法)可以帮助这些命令正常工作。

有关cat<<如何组合以形成内联此处文档的更多信息,请参见https://stackoverflow.com/a / 21761956/111948

评论


这个问题要求一个sudo命令。

– Pedro Vagner
20-4-2在21:19

@PedroVagner-感谢您指出,我已经相应更新了我的答案。

– Dejay Clayton
20-4-6的16:40

#6 楼

您是否知道可以使用sudo给您一个Shell,您可以在其中以所选用户身份运行命令?


-i,--login

运行目标用户的密码数据库条目指定的shell作为登录shell。这意味着外壳将读取特定于登录的资源文件,例如
.profile或.login。如果指定了命令,则通过外壳的-c选项将其传递到外壳以执行。如果未指定
命令,则执行交互式shell。 sudo尝试在运行外壳程序之前更改为该用户的主目录。该命令在与用户登录时所接收的环境类似的环境下运行。sudoers(5)手册中的“命令环境”部分介绍了-i选项如何影响该环境。使用sudoers策略时在其中运行命令的环境。


评论


这似乎是我的明显解决方案。 >须藤-i -u brian

– code_monk
2014年9月7日,下午2:26

在远程访问的情况下,与“ ssh -t”结合使用时效果最佳。该问题已包含在内,但需要重复“我看不懂此/ whole /页面”的内容。 :)

– dannysauer
15年7月13日在17:57

#7 楼

您可以在本地计算机上定义脚本,然后将其定义并通过管道传输到远程计算机:

user@host:~/temp> echo "echo 'Test'" > fileForSsh.txt
user@host:~/temp> cat fileForSsh.txt | ssh localhost

Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Invalid argument
Test


评论


您可以使用的UUOC <

–user9517
2014年9月2日,9:54

您可以修改示例以包括sudo -u scriptuser吗?考虑到我需要在要传递给计算机的脚本中使用变量,heredoc会有用吗?

–VoY
2014年9月2日上午10:36

我正在尝试:echo“ sudo`cat fileForSsh.txt`” | ssh ...但我一直在获取sudo:抱歉,您必须有一个tty才能运行sudo。

–jas_raj
2014-09-2 10:49



尽管如果可以更改/ etc / sudoers文件,此问题可能会有所帮助

– jas_raj
2014年9月2日在10:51



这对我有用:ssh -tq user @ host“ sudo bash -s”
– AVee
2014年9月4日18:00

#8 楼

简单容易。


ssh user @ servidor“ bash -s”

#9 楼

如果使用足够现代的Bash shell,则可以将命令放入函数中,然后使用export -p -f function_name将其打印为字符串。然后,该字符串的结果可以包含不一定必须以root用户身份运行的任意命令。
/>
#!/bin/bash
remote_main() {
   local dest="$HOME/destination"

   tar xzv -C "$dest"
   chgrp -R www-data "$dest"
   # Ensure that newly written files have the 'www-data' group too
   find "$dest" -type d -exec chmod g+s {} \;
}
tar cz files/ | ssh user@host "$(declare -pf remote_main); remote_main"



检索到的示例将保存登录用户的文件,并为根安装程序:

remote_main() {
    wget https://example.com/screenrc -O ~/.screenrc
    sudo apt-get update && sudo apt-get install screen
}
ssh user@host "$(declare -pf remote_main); remote_main"


如果要使用sudo运行整个命令,则可以使用以下命令:

remote_main() {
    wget https://example.com/screenrc -O ~user/.screenrc
    apt-get update && apt-get install screen
}
ssh user@host "$(declare -pf remote_main);
    sudo sh -c \"$(declare -pf remote_main); remote_cmd\""
# Alternatively, if you don't need stdin and do not want to log the command:
ssh user@host "$(declare -pf remote_main);
    (declare -pf remote_main; echo remote_cmd) | sudo sh"


#10 楼

这是我(未经实际测试)的cr脚解决方案:

#!/usr/bin/env ruby
# shell-escape: Escape each argument.
ARGV.each do|a|
  print " '#{a.gsub("'","\'\\'\'")}' "
end


不行: />下一步是创建一个已经执行此操作的betterssh脚本:

ssh -tq myuser@hostname "$(shell-escape sudo -u scriptuser bash -c "$(shell-escape ls -al)")"


#11 楼

对于需要将参数传递给脚本的这些人,应如下所示:

ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | sudo bash -s <param1> <param2> <paramN>"


#12 楼

首先,创建一个本地脚本,然后使用以下命令远程执行它:

cat <Local Script.sh> | ssh user@server "cat - | sudo -u <user> /bin/bash"