繁体   English   中英

将命令作为输入传递给另一个命令(su、ssh、sh 等)

[英]Pass commands as input to another command (su, ssh, sh, etc)

我有一个脚本,我需要在其中启动一个命令,然后将一些其他命令作为命令传递给该命令。 我试过

su
echo I should be root now:
who am I
exit
echo done.

... 但它不起作用: su成功,但命令提示符只是盯着我看。 如果我在提示符下键入exitechowho 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

我该如何解决这个问题?

我正在寻找以一般方式解决此问题的答案,而不是特定于sussh的答案。 目的是让这个问题成为这个特定模式的规范

添加到三人组答案

重要的是要记住,格式化为另一个 shell 的 here-document 的脚本部分是在具有自己环境的不同 shell 中执行的(甚至可能在不同的机器上)。

如果您的脚本块包含参数扩展、命令替换和/或算术扩展,那么您必须稍微不同地使用 shell 的 here-document 工具,具体取决于您希望在哪里执行这些扩展。

1.所有的扩展都必须在父shell的范围内进行。

那么这里文档的分隔符必须不加引号

command <<DELIMITER
...
DELIMITER

例子:

#!/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

输出:

a=0
mylogin=leon
a=0
mylogin=leon

2、所有的扩展都必须在子shell的范围内进行。

那么here文档的分隔符一定要加引号

command <<'DELIMITER'
...
DELIMITER

例子:

#!/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

输出:

a=1
mylogin=root
a=0
mylogin=leon

3. 一些扩展必须在子 shell 中执行,一些 - 在父 shell 中。

那么此处文档的分隔符必须不加引号,并且您必须转义必须在子 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

输出:

a=0
mylogin=root
a=0
mylogin=leon

shell 脚本是一系列命令。 shell 将读取脚本文件,并一个接一个地执行这些命令。

在通常情况下,这里没有惊喜; 但是一个常见的初学者错误是假设某些命令将从 shell 中接管,并开始在脚本文件中执行以下命令,而不是当前运行此脚本的 shell。 但这不是它的工作原理。

基本上,脚本的工作方式与交互式命令完全一样,但需要正确理解它们的工作方式。 交互式地,shell 读取一个命令(从标准输入),运行该命令(使用来自标准输入的输入),当它完成时,它读取另一个命令(从标准输入)。

现在,在执行脚本时,标准输入仍然是终端(除非您使用重定向),但命令是从脚本文件中读取的,而不是从标准输入中读取的。 (相反的情况确实很麻烦 - 任何read都会消耗脚本的下一行, cat会吞下脚本的所有其余部分,并且无法与之交互!)脚本文件包含用于执行它的 shell 实例(尽管您当然仍然可以使用此处的文档等将输入嵌入为命令参数)。

换句话说,这些“被误解”的命令( susshshsudobash等)在单独运行(不带参数)时将启动一个交互式 shell,而在交互式会话中,这显然很好; 但是从脚本运行时,这通常不是您想要的。

所有这些命令都可以通过交互式终端会话以外的方式接受命令。 通常,每个命令都支持一种将命令作为选项或参数传递的方式:

su root -c 'who am i'
ssh user@remote uname -a
sh -c 'who am i; echo success'

其中许多命令也将接受标准输入上的命令:

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

这也方便您使用此处的文档:

ssh user@remote <<'____HERE'
    uname -a
    who am i
    uptime
____HERE

sh <<'____HERE'
    uname -a
    who am i
    uptime
____HERE

对于接受单个命令参数的命令,该命令可以是带有多个命令的shbash

sudo sh -c 'uname -a; who am i; uptime'

顺便说一句,您通常不需要显式exit ,因为该命令在执行您传入执行的脚本(命令序列)时无论如何都会终止。

如果您想要一个适用于任何类型程序的通用解决方案,您可以使用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

然后可以使用一个简单的命令启动该脚本:

$ expect -f custom.script

您可以在以下页面中查看完整示例: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

在 SSH 中,您可能会强制使用多个-t参数分配 TTY,但是当sudo要求输入密码时,它会失败。

如果不使用像expect这样的程序,对可能从标准输入获取信息的函数/程序的任何调用都会使下一个命令失败:

ssh use@host <<'____HERE'
  echo "Enter your name:"
  read name
  echo "ok."
____HERE
--> The `echo "ok."` string will be passed to the "read" command

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM