Kam*_*ski 6 bash command-line escaping
考虑这样的工具watch可以接受另一个命令(例如ls -l),如下所示:
watch ls -l
# or equivalently
watch 'ls -l'
Run Code Online (Sandbox Code Playgroud)
如果命令更复杂,那么如果当前 shell 或 get 解释诸如$variable、*、|或;之类的内容,那么一切都与引用/转义有关。这两个命令是不同的:&&watch
watch echo "$$"
watch 'echo "$$"'
Run Code Online (Sandbox Code Playgroud)
这些也是:
watch date ; echo done
watch date \; echo done
Run Code Online (Sandbox Code Playgroud)
ssh类似,它可以接受一个或多个参数并构建一个要在服务器上运行的命令。如果本地 shell 解释等,或者远程 shell 则$variable取决于引用/转义。|
有些命令sh -c需要包含代码的单个参数,但引用/转义的需求是相似的。事实上watch(或ssh) 为sh -c或类似的命令构建了这个单一参数。
我已经有了想要提供给或或类似工具的确切命令。命令是从历史记录中获取的,或者粘贴到命令行中或者键入,就像要直接执行一样;它逐字在我的命令行中。在使用类似工具之前,不应扩展或解释命令中的任何内容。如果是这样,则意味着所有扩展和解释都应该稍后定期进行。如果是这样,则意味着它应该发生在服务器上。watchsshwatchwatchssh
现在我需要做两件事:
watch在开头添加(或其他)。这不是问题。我可以最后做这件事。引用和/或转义原始命令。一般来说命令中可以包含单引号,所以一味地拥抱单引号并不是解决问题的办法。我想我知道正确的通用解决方案:
'为'"'"'或'\'',但手动完成此操作既乏味又容易出错。
我可以让 Bash 根据需要向整个命令行正确添加一级单引号/转义吗?
(注:这个问题是我回答这个问题时出现的。)
是的。我的想法是调用一个 shell 函数(通过按键)来READLINE_LINE使用该${variable@Q}功能进行操作。
文档的相关部分:
\n\n\n\n
${parameter@operator}扩展要么是 的值的转换
\nparameter,要么是关于parameter其自身的信息的转换,具体取决于 的值operator。每个operator都是一个字母:\n
Qparameter\n扩展是一个字符串,它是以可重复用作输入的格式引用的值。
(来源)
\n\n\n\n
READLINE_LINE
\nReadline 行缓冲区的内容,与bind -x[\xe2\x80\xa6] 一起使用。
(来源)
\nBash 4.4.20 中的工作原理如下:
\n_quote_all() { READLINE_LINE="${READLINE_LINE@Q}"; }\nbind -x \'"\\C-x\\C-o":_quote_all\'\nRun Code Online (Sandbox Code Playgroud)\n要测试解决方案,请在命令行中准备一条命令(不要执行),例如
\nd="$(LC_ALL=C date)"; printf \'It\'\\\'\'s now %s\\n\' "$d"\nRun Code Online (Sandbox Code Playgroud)\n(引用和整个命令可以被简化。它是故意这样的。您可以执行它以确保它是一个有效的命令,但在继续之前将其放回命令行中。)
\n点击Ctrl+ x、Ctrl+ o,它将被正确引用/转义以达到我们的目的。它看起来像这样:
\n\'d="$(LC_ALL=C date)"; printf \'\\\'\'It\'\\\'\'\\\'\\\'\'\'\\\'\'s now %s\\n\'\\\'\' "$d"\'\nRun Code Online (Sandbox Code Playgroud)\n现在您需要做的就是在前面添加watch(或ssh \xe2\x80\xa6,或其他任何内容)并执行。如果是的话watch请注意标题就像
Every 2.0s: d="$(LC_ALL=C date)"; printf \'It\'\\\'\'s now %s\\n\' "$d"\nRun Code Online (Sandbox Code Playgroud)\n它包含原始命令。命令正确到达watch,没有任何部分被过早解释。
为了方便起见,考虑这个变体:
\n_quote_all() { READLINE_LINE=" ${READLINE_LINE@Q}"; READLINE_POINT=0; }\nRun Code Online (Sandbox Code Playgroud)\n它将准备好行并将光标放在开头,以便您可以watch立即输入。或者甚至可能是这个变体(它故意使用不同的名称,我们正在为其创建一个单独的绑定):
_prepend_watch() { READLINE_LINE="watch ${READLINE_LINE@Q}"; READLINE_POINT=6; }\nbind -x \'"\\C-x\\C-w":_prepend_watch\'\nRun Code Online (Sandbox Code Playgroud)\n现在Ctrl+ x, Ctrl+w处理引用、watch自动插入并将光标置于正确的位置以便您键入选项。
使用另一个函数READLINE_POINT可以处理以下情况:键入watch(或ssh \xe2\x80\xa6)后跟一个命令,其中引用/转义就像要直接执行该命令一样。将光标放在命令开始的位置,按下按键,然后让函数修改从光标到行尾的所有内容。我在这里不提供这样的功能;如果需要的话自己写一下。
您可以堆叠解决方案。我的意思是你可以从这里开始
\ndf -h | grep -v tmpfs\nRun Code Online (Sandbox Code Playgroud)\n对此
\nwatch \'df -h | grep -v tmpfs\'\nRun Code Online (Sandbox Code Playgroud)\n对此
\nssh hostB -t \'watch \'\\\'\'df -h | grep -v tmpfs\'\\\'\'\'\nRun Code Online (Sandbox Code Playgroud)\n对此
\nssh -t hostA \'ssh -t hostB \'\\\'\'watch \'\\\'\'\\\'\\\'\'\'\\\'\'df -h | grep -v tmpfs\'\\\'\'\\\'\\\'\'\'\\\'\'\'\\\'\'\'\nRun Code Online (Sandbox Code Playgroud)\n(是的,我知道ssh -J)
只需点击Ctrl+ x、Ctrl+o并在每一步中在前面添加一个或几个单词即可。
\n