我想打开一个终端并让它接受执行一个命令然后终止(这样终端就关闭了)。可选地,这应该只在前一个命令成功执行时发生,以解决拼写错误、遗忘sudo
等问题。
我一直在尝试通过管道传输read line
到 bash/sh 等不同的解决方案,但收效甚微。以下是一些尝试:
exec foot sh -c "output=$(read line); sh -c $output
exec foot sh -c "read line | sh"
ETC。
答案1
我假设foot
这是您的终端模拟器并且它接受命令作为参数。
在urxvt
调用序列中将是urxvt -e command args...
。
您遇到的第一个问题不是read
shell 内置命令如何工作的!
其次阅读dash(1)
有关内置的手册页read
(我希望你正在使用 dash,如果你追求的是正确的sh
,bash
那么用作 是不可靠的sh
,它根本无法遵循 POSIX 标准模拟)。
第三,您需要读入要执行的行,然后用foot
您的命令/行替换调用的 shell。所以我会这样做:
$ cat test.sh
#!/bin/sh
exec foot sh -c "IFS=\"\" read -p \"Type in a command:\" -r line_input ; exec \${line_input}"
不幸的是,shell 真的是一个糟糕的“阅读器”,或者更确切地说,shellread
真的是一个糟糕的通用行编辑器。
Shell 坚持始终通过 $IFS 字符将读入的行拆分为多个变量,因此IFS=""
代码会尝试阻止这种情况(在某些特殊情况下,shell 无论如何都会进行拆分,因为这很愚蠢)。
Shell 还坚持解释行嵌入\
字符,除非-r
还指定,因此我们这样做。
即便如此,你仍然会遇到 shell 对直接在命令输入中输入的带有空格的参数进行自动分割的愚蠢问题read
。
但我希望我的片段足以满足您的目的。
答案2
问题已加标签狂欢。这个答案使用 Bash。
bash -t
在读取并执行一个命令(可能是复杂的命令,一个命令“行”)后退出,该命令“行”可能相当长(甚至多行)的 shell 代码。bash -t
无论命令的退出状态如何,都会退出。要有条件地退出,您需要更复杂的东西。
创建一个文件~/.single_command_bashrc
并将以下代码放入其中:
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
PS1="(transient) $PS1"
BASH_LATE_ENOUGH=
PROMPT_COMMAND='
[ "$?" -eq 0 ] && [ -n "$BASH_LATE_ENOUGH" ] && exit
BASH_LATE_ENOUGH=x
'
该文件不必是可执行的。
现在,如果您运行bash --rcfile ~/.single_command_bashrc
,那么新打开的交互式 Bash 将会启动/etc/bash.bashrc
and ~/.bashrc
(就像不带 would 调用的交互式 Bash 一样--rcfile
);但另外它还会自行配置以执行您想要的操作。
PROMPT_COMMAND
在这个 Bash 中,每次要打印提示时,都会执行存储在 中的 shell 代码。该代码使 shell 在成功执行命令后打印提示之前退出。因为在第一个提示之前还有一个成功的命令,BASH_LATE_ENOUGH
所以使用名为的辅助变量来确保exit
在第一个提示之前不会执行。我可以通过添加 来获得类似的结果false
,~/.single_command_bashrc
但如果出于某种原因set -e
当时处于活动状态,那么这false
将使 shell 过早退出。在我看来,变量是一个更好的解决方案。
你需要告诉你的终端模拟器运行bash --rcfile ~/.single_command_bashrc
。判断独自根据你发布的内容,我认为命令可能是:
foot bash --rcfile ~/.single_command_bashrc
(我使用konsole
,就我的情况而言是konsole -e bash --rcfile …
。)
笔记:
如果
bash
进入~/
的选项参数中--rcfile
,它将自行扩展。这意味着即使在没有发生波浪号扩展的情况下,您也可以使用上述(或类似)命令。我的意思是,无论 是否在运行~/
前扩展foot
,或者通过foot
(我完全不知道是否foot
尝试过),或者通过我们的特殊用途bash
,它都应该可以工作。用户将能够执行的“一个命令”可以是几乎任意复杂的 shell 代码:许多由
;
and/or分隔&
、由&&
and/or连接的命令||
;包括管道、此处文档等。这可能是也可能不是你想要的。在输入命令时,您直接在 Bash 中输入:您可以使用提示符(我的代码对其进行了稍微扩展)、按键绑定、制表符补全,可以使用任何 shell 语法。无需担心
IFS
、read
或eval
任何其他东西,无需额外的解析级别。shell 可以正常工作通常情况下直到下一个提示即将被打印。用户可以轻松绕过该机制。以下是一些明显的攻击角度:
-
whatever; false
-
unset PROMPT_COMMAND
解决方案当然不是限制用户的方法。它适用于合作的用户。
-
我们的文件只是设置了
PROMPT_COMMAND
。如果你通常使用PROMPT_COMMAND
某些东西(例如已经在中设置了它~/.bashrc
),那么你可能想要附加到它PROMPT_COMMAND
而不是覆盖它。exit
退出 shell。退出 shell 后,终端仿真器是否自动关闭由终端仿真器决定。有些命令在退出状态中编码了有用的信息,而这些命令的正式“失败”(非零退出状态)并不一定意味着出了问题。例如,
echo foo | grep bar
退出状态grep
表示1
“未找到模式”,并不意味着grep
未能完成其工作。
答案3
我将在read
命令中添加以下参数:
命令前:IFS=
命令上:-r -d $'\0')
IFS=
(等号后没有任何内容)将内部字段分隔符设置为“无值”。因此,空格、制表符和换行符被视为单词的一部分,从而保留空白。
-r
将保留输入值中的任何反斜杠。
-d
设置换行符分隔符,将其设置为 NULL 字符(ASCII 代码零),以防输入的值包含换行符。