tri*_*eee 19 unix ssh bash shell sh
我有一个脚本,我需要启动一个命令,然后将一些额外的命令作为命令传递给该命令.我试过了
su
echo I should be root now:
who am I
exit
echo done.
Run Code Online (Sandbox Code Playgroud)
...但它不起作用:su
成功,但命令提示符只是盯着我.如果键入exit
的提示,echo
和who am i
等开始执行!而且echo done.
根本没有执行.
同样,我需要这个工作ssh
:
ssh remotehost
# this should run under my account on remotehost
su
## this should run as root on remotehost
whoami
exit
## back
exit
# back
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个问题?
我正在寻找能够以一般方式解决这个问题的答案,这些答案并非特定于
su
或ssh
特别是.目的是使这个问题成为这种特定模式的规范.
tri*_*eee 17
shell脚本是一系列命令.shell将读取脚本文件,并依次执行这些命令.
在通常的情况下,这里没有惊喜; 但是一个常见的初学者错误是假设某些命令将从shell接管,并开始在脚本文件中执行以下命令,而不是当前运行此脚本的shell.但这不是它的工作原理.
基本上,脚本的工作正是像交互式命令,但如何准确,他们的工作需要得到正确的理解.交互式地,shell读取命令(来自标准输入),运行该命令(使用来自标准输入的输入),并且当它完成时,它读取另一个命令(来自标准输入).
现在,在执行脚本时,标准输入仍然是终端(除非您使用了重定向),但命令是从脚本文件中读取的,而不是从标准输入中读取的.(相反的情况确实非常麻烦 - 任何read
会占用脚本的下一行,cat
会玷污脚本的所有其余部分,并且无法与它进行交互!)脚本文件只包含shell实例的命令执行它(虽然你当然可以使用这里的文档等来嵌入输入作为命令参数).
换句话说,这些"误解"命令(su
,ssh
,sh
,sudo
,bash
等)单独运行时(不带参数)将启动一个交互的shell,并在互动环节,这显然罚款; 但是从脚本运行时,往往不是你想要的.
所有这些命令都有通过除交互式终端会话之外的方式接受命令的方法.通常,每个命令都支持将命令作为选项或参数传递的方法:
su root -c 'who am i'
ssh user@remote uname -a
sh -c 'who am i; echo success'
Run Code Online (Sandbox Code Playgroud)
其中许多命令也会接受标准输入的命令:
printf 'uname -a; who am i; uptime' | su
printf 'uname -a; who am i; uptime' | ssh user@remote
printf 'uname -a; who am i; uptime' | sh
Run Code Online (Sandbox Code Playgroud)
这也方便你在这里使用文件:
ssh user@remote <<'____HERE'
uname -a
who am i
uptime
____HERE
sh <<'____HERE'
uname -a
who am i
uptime
____HERE
Run Code Online (Sandbox Code Playgroud)
为其接受单个命令参数的命令,该命令可以是sh
或bash
具有多个命令:
sudo sh -c 'uname -a; who am i; uptime'
Run Code Online (Sandbox Code Playgroud)
顺便说一句,您通常不需要显式,exit
因为命令将在执行您传入执行的脚本(命令序列)时终止.
Leo*_*eon 16
重要的是要记住,格式化为另一个shell的here-document的脚本部分在具有自己的环境的不同shell中执行(甚至可能在不同的机器上).
如果脚本的那个块包含参数扩展,命令替换和/或算术扩展,那么必须使用shell的here-document工具略有不同,具体取决于您希望执行这些扩展的位置.
那么这里的文件的分隔符必须是不加引号的.
command <<DELIMITER
...
DELIMITER
Run Code Online (Sandbox Code Playgroud)
例:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Run Code Online (Sandbox Code Playgroud)
输出:
a=0
mylogin=leon
a=0
mylogin=leon
Run Code Online (Sandbox Code Playgroud)
然后必须引用此处文档的分隔符.
command <<'DELIMITER'
...
DELIMITER
Run Code Online (Sandbox Code Playgroud)
例:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<'END'
a=1
mylogin=$(whoami)
echo a=$a
echo mylogin=$mylogin
END
echo a=$a
echo mylogin=$mylogin
Run Code Online (Sandbox Code Playgroud)
输出:
a=1
mylogin=root
a=0
mylogin=leon
Run Code Online (Sandbox Code Playgroud)
然后,必须取消引用此文档的分隔符,并且必须转义必须在子shell中执行的那些扩展表达式.
例:
#!/bin/bash
a=0
mylogin=$(whoami)
sudo sh <<END
a=1
mylogin=\$(whoami)
echo a=$a
echo mylogin=\$mylogin
END
echo a=$a
echo mylogin=$mylogin
Run Code Online (Sandbox Code Playgroud)
输出:
a=0
mylogin=root
a=0
mylogin=leon
Run Code Online (Sandbox Code Playgroud)
如果您想要一个适用于任何类型程序的通用解决方案,您可以使用该expect
命令.
摘自手册页:
Expect
是一个根据脚本与其他交互程序"对话"的程序. 在脚本之后,Expect
知道程序可以期待什么以及正确的响应应该是什么.解释语言提供分支和高级控制结构来指导对话.此外,用户可以在需要时直接进行控制和交互,然后将控制权返回给脚本.
这是一个使用的工作示例expect
:
set timeout 60
spawn sudo su -
expect "*?assword" { send "*secretpassword*\r" }
send_user "I should be root now:"
expect "#" { send "whoami\r" }
expect "#" { send "exit\r" }
send_user "Done.\n"
exit
Run Code Online (Sandbox Code Playgroud)
然后可以使用一个简单的命令启动该脚本:
$ expect -f custom.script
Run Code Online (Sandbox Code Playgroud)
您可以在以下页面中查看完整示例:http://www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands
注意: @tripleee提出的答案只有在标准输入可以在命令开始时读取一次,或者如果已经分配了tty时才有效,并且不适用于任何交互式程序.
使用管道时出现错误的示例
echo "su whoami" |ssh remotehost
--> su: must be run from a terminal
echo "sudo whoami" |ssh remotehost
--> sudo: no tty present and no askpass program specified
Run Code Online (Sandbox Code Playgroud)
在SSH中,您可能强制使用多个-t
参数进行TTY分配,但是何时sudo
会要求输入密码,它将失败.
如果不使用expect
任何可能从stdin获取信息的函数/程序调用程序,则会使下一个命令失败:
ssh use@host <<'____HERE'
echo "Enter your name:"
read name
echo "ok."
____HERE
--> The `echo "ok."` string will be passed to the "read" command
Run Code Online (Sandbox Code Playgroud)