![通过脚本控制 bash 实例(以稳健的方式)](https://linux22.com/image/153836/%E9%80%9A%E8%BF%87%E8%84%9A%E6%9C%AC%E6%8E%A7%E5%88%B6%20bash%20%E5%AE%9E%E4%BE%8B%EF%BC%88%E4%BB%A5%E7%A8%B3%E5%81%A5%E7%9A%84%E6%96%B9%E5%BC%8F%EF%BC%89.png)
我想用一些(最终是交互式的)脚本(比如 python)以稳健的方式控制一些 bash 程序(本地或 ssh)。我想发送命令,在执行结束时获取该命令的标准输出/标准错误,以及该命令的退出代码。请注意,bash 命令是由外部用户实时选择的,因此它可以是任何命令。
为了了解我想要什么,我想提供一个函数(编程语言并不重要):
def send_command(command):
# how to implement ????
return (stdin_output, stderr_output, exit_code)
这样我就可以运行:
(o,e,ex) = send_command("pwd")
print("The output of your last command is ", o)
(o,e,ex) = send_command("cd /tmp")
(o,e,ex) = send_command("pwd")
print("The output of your last command is ", o)
这会产生:
The output of your last command is /home/me/
The output of your last command is /tmp/
此外,我想确保没有人command
可以“搞乱”我的Python脚本,并让它相信命令已经完成,而实际上它还没有完成。例如,我不想让他记录接下来的所有命令,所以下面的代码应该永远阻塞在第一行(第一个命令是无限循环)并且永远不会调用第二行:
send_command("echo $PS1; <malicious command that loops forever and sends all the next inputs to a webserver>")
send_command("echo mysecretpassword") # This line should never be executed
我尝试过的:
一种选择是每次运行一个新的 bash 实例,并在此控制台中运行脚本。但这有两个缺点:首先,它不是非常高效,因为每次它都会从头开始加载 bash/ssh。其次,bash(环境、工作目录)的状态在每次运行时都是新的。例如:如果我运行“cd /tmp”,则在下一个命令中我将回到家。
我看到的另一个选项是expect
/ pexpect
。这个解决方案的问题是,据我了解,它只是对输出的解析。这种解析会丢失 bash 输出的语义。例如,如果我期望$
,我无法确保命令已完成,并且可能是命令本身编写了此文本。同样,如果程序不完全受信任,情况可能会更糟:程序可能会冒充 bash,并读取所有 bash 命令。人们可能会尝试设置一个复杂的 PS1,但此环境变量会转发到命令,因此任何输出此变量(如“env”)的程序都会破坏此命令,因此这并不是一个真正可靠的解决方案。最后,我没有看到比运行输出最后键入命令的代码并解析此结果的 bash 命令更好的获取命令退出代码的“好”方法。
我错过了第三个选择吗?
谢谢!
- 编辑 - 我发现环境变量PROMPT_COMMAND
,据我所知,它没有在环境变量中传输。但仍然可以使用简单的方法直接从 shell 读取它,echo
因此它也不是超级强大......
$ PS1=""
PROMPT_COMMAND='echo -n "Prompt MYSECRETNUMBERSETBYEXPECT (last exit $?) "'
Prompt MYSECRETNUMBERSETBYEXPECT (last exit 0) ls /tmp/o
file.txt
Prompt MYSECRETNUMBERSETBYEXPECT (last exit 0) rm /tmp/a/donotexist
rm: impossible de supprimer '/tmp/a/donotexist': No such file or directory
Prompt MYSECRETNUMBERSETBYEXPECT (last exit 1) env | grep SECRET
Prompt MYSECRETNUMBERSETBYEXPECT (last exit 1)
- 编辑2 - 另一个似乎更强大的解决方案是使用pexpect
随机秘密提示,然后拥抱任何command
:
PS1=""; <command>; PS1="My secret PS1 61547851396512fdsqjklqdf (last code $?) >>$ "
(此代码只是删除/重新创建命令之前/之后的 PS1 秘密提示,以确保该命令无权访问提示值)。如果需要,甚至可以将输出重定向到文件,而无需通过替换<command>
为{ <command>; } > /tmp/out.stdout 2> /tmp/out.stderr
(不要忘记空格和分号)来创建子 shell。这是最好的继续方式吗?
答案1
你可以尝试使用sysdig
,它基本上是您系统的间谍软件。
从sysdiog 维基您可以使用以下方式stdout
从进程名称观察cat
:
sysdig -s4096 -A -c stdout proc.name=cat
foo
您还可以观察名为如下的用户执行的任何命令:
sysdig -c spy_users user.name=foo
您还可以参考这个 StackExchange 问题:
https://stackoverflow.com/questions/54771786/record-bash-interaction- saving-stdin-stdout-seperately
这对你有很多想法。