bash 将命令作为输入传递给另一个命令(su、ssh、sh 等)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37586811/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pass commands as input to another command (su, ssh, sh, etc)
提问by tripleee
I have a script where I need to start a command, then pass some additional commands as commandsto that command. I tried
我有一个脚本,我需要在其中启动一个命令,然后将一些附加命令作为命令传递给该命令。我试过
su
echo I should be root now:
who am I
exit
echo done.
... but it doesn't work: The su
succeeds, but then the command prompt is just staring at me. If I type exit
at the prompt, the echo
and who am i
etc start executing! And the echo done.
doesn't get executed at all.
...但它不起作用:su
成功了,但是命令提示符只是盯着我看。如果键入exit
的提示,echo
和who am i
等开始执行!并且echo done.
根本没有被执行。
Similarly, I need for this to work over ssh
:
同样,我需要为此工作ssh
:
ssh remotehost
# this should run under my account on remotehost
su
## this should run as root on remotehost
whoami
exit
## back
exit
# back
How do I solve this?
我该如何解决这个问题?
I am looking for answers which solve this in a general fashion, and which are not specific to
su
orssh
in particular. The intent is for this question to become a canonicalfor this particular pattern.
我正在寻找以一般方式解决这个问题的答案,而不是特定于
su
或ssh
特别的。目的是让这个问题成为这个特定模式的规范。
采纳答案by tripleee
A shell script is a sequence of commands. The shell will read the script file, and execute those commands one after the other.
shell 脚本是一个命令序列。shell 将读取脚本文件,并一个接一个地执行这些命令。
In the usual case, there are no surprises here; but a frequent beginner error is assuming that somecommands will take over from the shell, and start executing the following commands in the script file instead of the shell which is currently running this script. But that's not how it works.
通常情况下,这里没有任何意外;但是一个常见的初学者错误是假设某些命令将从 shell 接管,并开始在脚本文件中执行以下命令,而不是当前运行此脚本的 shell。但这不是它的工作原理。
Basically, scripts work exactlylike interactive commands, but how exactlythey work needs to be properly understood. Interactively, the shell reads a command (from standard input), runs that command (with input from standard input), and when it's done, it reads another command (from standard input).
基本上,脚本的工作正是像交互式命令,但如何准确,他们的工作需要得到正确的理解。交互地,shell 读取命令(来自标准输入),运行该命令(使用来自标准输入的输入),并在完成后读取另一个命令(来自标准输入)。
Now, when executing a script, standard input is still the terminal(unless you used a redirection) but the commands are read from the script file, not from standard input. (The opposite would be very cumbersome indeed - any read
would consume the next line of the script, cat
would slurp all the rest of the script, and there would be no way to interact with it!) The script file onlycontains commands for the shell instance which executes it (though you can of course still use a here document etc to embed inputs as command arguments).
现在,在执行脚本时,标准输入仍然是终端(除非您使用重定向),但命令是从脚本文件中读取的,而不是从标准输入中读取的。(相反的确实会非常麻烦——任何read
都会消耗脚本的下一行,cat
会吞掉脚本的所有其余部分,并且无法与之交互!)脚本文件只包含 shell 实例的命令执行它(尽管您当然仍然可以使用此处的文档等将输入嵌入为命令参数)。
In other words, these "misunderstood" commands (su
, ssh
, sh
, sudo
, bash
etc) when run alone (without arguments) will start an interactive shell, and in an interactive session, that's obviously fine; but when run from a script, that's very often not what you want.
换句话说,这些“误解”的命令(su
,ssh
,sh
,sudo
,bash
等)单独运行时(不带参数)将启动一个交互的shell,并在互动环节,这显然罚款; 但是当从脚本运行时,这通常不是您想要的。
All of these commands have ways to accept commands by ways other than in an interactive terminal session. Typically, each command supports a way to pass it commands as options or arguments:
所有这些命令都可以通过交互式终端会话以外的方式接受命令。通常,每个命令都支持将命令作为选项或参数传递给它的方法:
su root -c 'who am i'
ssh user@remote uname -a
sh -c 'who am i; echo success'
Many of these commands will also accept commands on standard input:
其中许多命令还将接受标准输入上的命令:
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
which also conveniently allows you to use here documents:
这也方便您使用此处的文档:
ssh user@remote <<'____HERE'
uname -a
who am i
uptime
____HERE
sh <<'____HERE'
uname -a
who am i
uptime
____HERE
For commands which accept a single command argument, that command can be sh
or bash
with multiple commands:
对于接受单个命令参数的命令,该命令可以是sh
或bash
具有多个命令:
sudo sh -c 'uname -a; who am i; uptime'
As an aside, you generally don't need an explicit exit
because the command will terminate anyway when it has executed the script (sequence of commands) you passed in for execution.
顺便说一句,您通常不需要显式,exit
因为该命令在执行您为执行而传入的脚本(命令序列)后无论如何都会终止。
回答by Leon
It is important to remember that the section of the script formatted as a here-document for another shell is executed in a different shell with its own environment (and maybe even on a different machine).
重要的是要记住,格式化为另一个 shell 的 here-document 的脚本部分是在具有自己环境的不同 shell 中执行的(甚至可能在不同的机器上)。
If that block of your script contains parameter expansion, command substitution, and/or arithmetic expansion, then you must use the here-document facility of the shell slightly differently, depending on where you want those expansions to be performed.
如果您的脚本块包含参数扩展、命令替换和/或算术扩展,那么您必须稍微不同地使用 shell 的 here-document 工具,这取决于您希望在何处执行这些扩展。
1. All expansions must be performed within the scope of the parent shell.
1、所有的扩展都必须在父shell的范围内进行。
Then the delimiter of the here document must be unquoted.
那么此处文档的定界符必须不加引号。
command <<DELIMITER
...
DELIMITER
Example:
例子:
#!/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
Output:
输出:
a=0
mylogin=leon
a=0
mylogin=leon
2. All expansions must be performed within the scope of the child shell.
2、所有的扩展都必须在子shell的范围内进行。
Then the delimiter of the here document must be quoted.
然后必须引用here 文档的分隔符。
command <<'DELIMITER'
...
DELIMITER
Example:
例子:
#!/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
Output:
输出:
a=1
mylogin=root
a=0
mylogin=leon
3. Some expansions must be performed in the child shell, some - in the parent.
3. 一些扩展必须在子shell 中执行,一些- 在父shell 中执行。
Then the delimiter of the here document must be unquotedand you must escape those expansion expressions that must be performed in the child shell.
那么此处文档的定界符必须不加引号,并且您必须转义那些必须在子 shell 中执行的扩展表达式。
Example:
例子:
#!/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
Output:
输出:
a=0
mylogin=root
a=0
mylogin=leon
回答by Adam
If you want a generic solution which will work for any kind of program, you can use the expect
command.
如果您想要一个适用于任何类型程序的通用解决方案,您可以使用该expect
命令。
Extract from the manual page:
摘自手册页:
Expect
is a program that "talks" to other interactive programs according to a script.Following the script,Expect
knows what can be expected from a program and what the correct response should be. An interpreted language provides branching and high-level control structures to direct the dialogue. In addition, the user can take control and interact directly when desired, afterward returning control to the script.
Expect
是根据脚本与其他交互式程序“对话”的程序。遵循脚本,Expect
知道可以从程序中得到什么以及正确的响应应该是什么。解释型语言提供分支和高级控制结构来指导对话。此外,用户可以在需要时直接进行控制和交互,然后将控制权返回给脚本。
Here is a working example using 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
The script can then be launched with a simple command:
然后可以使用简单的命令启动脚本:
$ expect -f custom.script
You can view a full example in the following page: http://www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands
您可以在以下页面查看完整示例:http: //www.journaldev.com/1405/expect-script-example-for-ssh-and-su-login-and-running-commands
Note:The answer proposed by @tripleee would only work if standard input could be read once at the start of the command, or if a tty had been allocated, and won't work for any interactive program.
注意:@tripleee 提出的答案仅适用于在命令开始时可以读取一次标准输入,或者已分配 tty 的情况,并且不适用于任何交互式程序。
Example of errors if you use a pipe
使用管道时的错误示例
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
In SSH, you might force a TTY allocation with multiple -t
parameters, but when sudo
will ask for the password, it will fail.
在 SSH 中,您可能会强制使用多个-t
参数进行 TTY 分配,但是当sudo
要求输入密码时,它会失败。
Without the use of a program like expect
any call to a function/program which might get information from stdin will make the next command fail:
如果不使用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