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