命令在本地工作,但在使用 ssh 调用时返回“未找到事件”

ari*_*sik 1 linux ssh bash special-characters

我想检查只读文件系统。我写了这个awk命令并且运行良好:

awk '{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }' /proc/mounts
Run Code Online (Sandbox Code Playgroud)

我想将此命令与ssh. 这不起作用:

$ ssh ip "awk '{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }' /proc/mounts"  
bash: !~: event not found
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?问题是因为波浪号字符吗?

Kam*_*ski 5

波浪号字符不是罪魁祸首,感叹号才是。

双引号!~会触发本地 shell 中的历史记录替换。请注意,内部单引号在本地并不重要(与相比,类似的情况;但内部引号在远程 shell 中很重要)。

您需要使!~本地 shell 显示为单引号。问题是您希望在远程 shell 中使用单引号。因此,您无法轻松嵌套引号。

您可以在本地 shell 中禁用历史记录替换并set +H坚持在本地使用双引号。但请注意,您不希望$4或被$2任何 shell 扩展,包括本地 shell。因此无论如何你都需要在本地使用单引号;$或者用来逃避\,例如\$2

仅使用未加引号的转义\可以帮助进行不需要的历史替换\!。双引号\!会阻止历史替换,但这\不会消失,它会在以后破坏事情。

可能的方法:

  1. 未引用并转义!

    # 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 ^^
    
    Run Code Online (Sandbox Code Playgroud)
  2. 禁用历史记录替换:

    set +H
    ssh ip "awk '{ if ( \$4 ~ /ro,/ && \$2 !~ /sys/ ) {print \$2} }' /proc/mounts"
    # re-enable history expansion (invoke `set -H') here if needed
    
    Run Code Online (Sandbox Code Playgroud)
  3. 对除了内部单引号之外的所有内容进行单引号,这在远程 shell 中至关重要:

    # history substitution may be enabled
    ssh ip 'awk '"'"'{ if ( $4 ~ /ro,/ && $2 !~ /sys/ ) {print $2} }'"'"' /proc/mounts'
            ^^^^     ^^^^^^^^^^^^-single-quoted locally-^^^^^^^^^^^^     ^^^^^^^^^^^^^
                  ^               double-quoted locally               ^
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在本地单引号所有内容,在远程 shell 中双引号。这会将您的问题推送到远程 shell,因此您无论如何都需要处理它们:

    ssh ip 'awk "{ if ( \$4 ~ /ro,/ && \$2 !~ /sys/ ) {print \$2} }" /proc/mounts'
    
    Run Code Online (Sandbox Code Playgroud)

    惊喜!我们逃脱了,$但没有!~;并且没有set +H,但该命令有效。

    这是因为在远程端ssh生成一个非交互式shell。非交互式 Bash 启动时默认禁用历史扩展(请注意,远程 shell 最初可能是也可能不是 Bash,它可能支持也可能根本不支持历史记录)。这使得主要问题消失了。如果您ssh以非交互方式运行原始命令,例如在本地脚本中(尽管您仍然需要转义$字符),则相同的机制将对您有所帮助。