我想检查只读文件系统。我写了这个awk
命令,它运行正常:
awk '{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }' /proc/mounts
我想将此命令与 一起使用ssh
。这不起作用:
$ ssh ip "awk '{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }' /proc/mounts"
bash: !~: event not found
我该怎么做?问题是由于波浪符号引起的吗?
答案1
波浪号不是罪魁祸首,感叹号才是。
双引号!~
触发本地 shell 中的历史替换。请注意,内部单引号在本地无关紧要(比较这,类似的情况;但是内部引号在远程 shell 中很重要)。
您需要让其!~
在本地 shell 中显示为单引号。问题是您还希望在远程 shell 中使用单引号。因此,您无法轻易嵌套引号。
您可以使用 禁用本地 shell 中的历史记录替换,set +H
并坚持在本地使用双引号。但请注意,您不希望$4
或被$2
任何 shell(包括本地 shell)扩展。因此,无论如何您都需要在本地使用单引号;或者使用 进行转义$
,\
例如\$2
。
转义\
只能通过不带引号的 来帮助进行不必要的历史替换\!
。双引号\!
将阻止历史替换,但这\
不会消失,它会在以后破坏一切。
可能的方法:
不加引号并转义
!
:# history substitution may be enabled ssh ip "awk '{ if ( \$4 ~ /ro,/ && \$2 "\!"~ /sys/ ) {print \$2} }' /proc/mounts" ^^^^-double-quoted locally-^^^^ ^^^^^^^-double-quoted locally-^^^^^^^ unquoted locally ^^
禁用历史替换:
set +H ssh ip "awk '{ if ( \$4 ~ /ro,/ && \$2 !~ /sys/ ) {print \$2} }' /proc/mounts" # re-enable history expansion (invoke `set -H') here if needed
将所有内容(除了在远程 shell 中至关重要的内部单引号)都用单引号引起来:
# history substitution may be enabled ssh ip 'awk '"'"'{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }'"'"' /proc/mounts' ^^^^ ^^^^^^^^^^^^-single-quoted locally-^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^ double-quoted locally ^
在本地对所有内容使用单引号,在远程 shell 中使用双引号。这会将您的问题推送到远程 shell,因此您无论如何都需要处理它们:
ssh ip 'awk "{ if ( \$4 ~ /ro,/ && \$2 !~ /sys/ ) {print \$2} }" /proc/mounts'
惊喜!我们逃脱了
$
,但没有!~
;并且没有set +H
,但命令有效。这是因为
ssh
会产生一个非交互式远程端的 shell。非交互式 Bash 默认禁用历史记录扩展(请注意,远程 shell 可能不是 Bash,也可能根本不支持历史记录)。这使得主要问题消失。如果您以非交互式方式运行原始命令,例如ssh
在本地脚本中(尽管您仍然需要转义$
字符),相同的机制将对您有所帮助。
答案2
对我来说,bash 似乎正在将 ! 预先解释为其历史功能的一部分。
您是否尝试过用 \ 转义 ! ?请参阅 如何在 Makefile 中通过 SSH 命令转义字符