“如何解决错误”;巴什:!d';:“未找到事件”;Bash中的命令替换
我试图解析VNC服务器启动事件的输出,但在命令替换中使用sed解析时遇到问题。具体而言,远程VNC服务器以如下方式启动:“如何解决错误”;巴什:!d';:“未找到事件”;Bash中的命令替换,bash,parsing,sed,command-substitution,Bash,Parsing,Sed,Command Substitution,我试图解析VNC服务器启动事件的输出,但在命令替换中使用sed解析时遇到问题。具体而言,远程VNC服务器以如下方式启动: address1="user1@lxplus.cern.ch" VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)" New 'lxplus0186.cern.ch:1 (user1)' desktop is lxplus0186.cern.ch:1 Starting applications s
address1="user1@lxplus.cern.ch"
VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)"
New 'lxplus0186.cern.ch:1 (user1)' desktop is lxplus0186.cern.ch:1
Starting applications specified in /afs/cern.ch/user/u/user1/.vnc/xstartup
Log file is /afs/cern.ch/user/u/user1/.vnc/lxplus0186.cern.ch:1.log
lxplus0186.cern.ch:1
VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
| sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"
然后对该启动事件中生成的标准错误输出进行分析,以提取服务器并显示信息。此时,变量VNCServerResponse
的内容如下所示:
address1="user1@lxplus.cern.ch"
VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)"
New 'lxplus0186.cern.ch:1 (user1)' desktop is lxplus0186.cern.ch:1
Starting applications specified in /afs/cern.ch/user/u/user1/.vnc/xstartup
Log file is /afs/cern.ch/user/u/user1/.vnc/lxplus0186.cern.ch:1.log
lxplus0186.cern.ch:1
VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
| sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"
可以通过以下方式解析此输出,以提取服务器和显示信息:
echo "${VNCServerResponse}" | sed '/New.*desktop.*is/!d' \
| awk -F" desktop is " '{print $2}'
结果如下:
address1="user1@lxplus.cern.ch"
VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)"
New 'lxplus0186.cern.ch:1 (user1)' desktop is lxplus0186.cern.ch:1
Starting applications specified in /afs/cern.ch/user/u/user1/.vnc/xstartup
Log file is /afs/cern.ch/user/u/user1/.vnc/lxplus0186.cern.ch:1.log
lxplus0186.cern.ch:1
VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
| sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"
我要做的是在命令替换中使用此解析,如下所示:
address1="user1@lxplus.cern.ch"
VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)"
New 'lxplus0186.cern.ch:1 (user1)' desktop is lxplus0186.cern.ch:1
Starting applications specified in /afs/cern.ch/user/u/user1/.vnc/xstartup
Log file is /afs/cern.ch/user/u/user1/.vnc/lxplus0186.cern.ch:1.log
lxplus0186.cern.ch:1
VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
| sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"
尝试执行此操作时,我遇到以下错误:
bash: !d': event not found
我不知道如何解决这个问题。在命令替换中使用sed的方式似乎存在问题。我希望得到指导。不要将$(…)
命令替换用双引号括起来。您正在要求shell对引号的内容执行求值,并且正在使用历史替换扩展功能。去掉引号,你就不再告诉shell这样做,你就不会碰到那个问题
而且是,即使输出可能包含空格或换行符或其他内容,在该赋值行上删除这些引号也是安全的。这种类型的赋值不会像命令替换或变量求值在正常shell执行行中那样进行拆分
或者,在运行shell/脚本之前,在其中禁用历史扩展。(我相信在默认情况下运行脚本时它应该关闭。)问题是,在双引号中,bash试图展开
!d
,然后将其传递到子shell。您可以通过删除双引号来解决此问题,但我还建议简化您的脚本:
VNCServerAndDisplayNumber=$(echo "$VNCServerResponse" | awk '/desktop/ {print $NF}')
这只是打印包含单词“desktop”的行上的最后一个字段
在较新的bash上,您可以使用herestring,而不是管道式的回音
:
VNCServerAndDisplayNumber=$(awk '/desktop/ {print $NF}' <<<"$VNCServerResponse")
VNCServerAndDisplayNumber=$(awk'/desktop/{print$NF}')只有在启用历史扩展时才会发生这种情况,而历史扩展通常不适用于脚本,也绝对不应该用于脚本
与其尝试解决它,不如弄清楚为什么启用了历史扩展,以及为什么不启用历史扩展
如果使用.foo
或源代码foo
执行脚本,请改用/foo
> p>如果您在 BasHC 中编写此函数,请考虑将其作为单独的脚本。
如果您的脚本(或BASH_ENV)显式地执行了设置-H
,请不要执行
Bash历史扩展在Bash命令行解析器中是一个非常奇怪的角落,您显然遇到了意外的历史扩展,如下所述。但是,脚本中的任何类型的历史扩展都是意外的,因为通常在脚本中不启用历史扩展;甚至连使用源代码运行的脚本也不例外(或
)内置
如何启用(或禁用)历史扩展
有两个shell选项可控制历史扩展:
设置-o历史记录
:需要记录历史记录
set-H
(或set-o histexpand
):要启用历史扩展,还需要额外设置
必须设置这两个选项,才能识别历史扩展。(我发现手册中对该交互不清楚,但它足够符合逻辑。)
根据bash手册,这些选项对于非交互式Shell是未设置的,因此如果您想在脚本中启用历史扩展(我无法想象您希望这样做的原因),则需要同时设置这两个选项:
set -o history -o histexpand
# A no-op to indicated history expansion
$ HIST() { :; }
# Single-quoted strings inhibit history expansion
$ HIST
$ echo '!!'
!!
# Double-quoted strings allow history expansion
$ HIST
$ echo "'!!'"
echo "'HIST'"
'HIST'
# ... and it applies also to interior command substitution.
$ HIST
$ echo "$(echo '!!')"
echo "$(echo 'HIST')"
HIST
使用source
运行脚本的情况更为复杂(我将要说的仅适用于bash v4,因为它在中未记录,将来可能会发生变化)。[注3]
历史记录(以及随后的扩展)在source
'd脚本中被关闭,但据我所知,它是通过一个不可见的内部标志来关闭的。它肯定不会出现在$SHELLOPTS
中。由于source
d脚本在当前bash上下文中运行,它共享当前的执行环境,包括shell选项。因此在的执行中de>source
d从交互式会话启动的脚本,您将在$SHELLOPTS
中看到历史记录
和histexpand
,但不会进行历史记录扩展。要启用它,您需要:
set -o history
这不是一个无操作,因为它具有重置内部标志的副作用,该标志会抑制历史记录。设置histexpand
shell选项不会产生此副作用
简而言之,我不确定您是如何在脚本中实现历史扩展的(如果,实际上,错误的命令是在脚本中而不是在交互shell中),但是您可能会考虑不这样做,除非您有一个很好的理由。
如何解析历史扩展
历史扩展的bash实现设计用于readline
,因此可以在命令输入期间执行。(默认情况下,此函数绑定到Meta-^;通常Meta是ESC,但也可以自定义。)但是,它也会在输入每一行之后,在执行任何bash解析之前立即执行
默认情况下,历史扩展字符为!并且(正如大多数文档所述)将触发历史扩展,除了:
当后跟空格或=
如果设置了shell选项extglob
,并且后跟([注1]<