如何解决 Bash 命令替换中的错误“bash: !d': event not found”[重复]

2023-11-24

我正在尝试解析 VNC 服务器启动事件的输出,但在命令替换中使用 sed 进行解析时遇到了问题。具体地,远程VNC服务器的启动方式如下:

address1="[email protected]"
VNCServerResponse="$(ssh "${address1}" 'vncserver' 2>&1)"

然后将解析此启动事件中产生的标准错误输出,以提取服务器并显示信息。此时变量的内容VNCServerResponse是这样的:

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

可以通过以下方式解析此输出,以提取服务器并显示信息:

echo "${VNCServerResponse}" | sed '/New.*desktop.*is/!d' \
    | awk -F" desktop is " '{print $2}'

结果如下:

lxplus0186.cern.ch:1

我想要做的是在命令替换中使用此解析,如下所示:

VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
    | sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"

在尝试执行此操作时,我遇到以下错误:

bash: !d': event not found

我不知道如何解决这个问题。这似乎是命令替换中使用 sed 的方式存在问题。我将不胜感激指导。


Bash 历史扩展是 bash 命令行解析器中的一个非常奇怪的角落,并且您显然遇到了意外的历史扩展,如下所述。然而,脚本中的任何类型的历史扩展都是意外的,因为通常脚本中不启用历史扩展;甚至脚本也不能运行source (or .) 内置。

如何启用(或禁用)历史扩展

有两个 shell 选项可以控制历史扩展:

  • set -o history:需要记录历史。

  • set -H (or set -o histexpand):启用历史扩展还需要。

必须设置这两个选项才能识别历史扩展。 (我发现手册对这种互动不清楚,但它足够合乎逻辑。)

根据 bash 手册,这些选项对于非交互式 shell 是未设置的,因此如果您想在脚本中启用历史扩展(我无法想象您想要这样做的原因),您需要设置它们:

set -o history -o histexpand

脚本运行的情况source更复杂(我要说的仅适用于 bash v4,并且由于它没有记录在将来可能会发生变化)。 [注3]

历史记录(以及相应的扩展)被关闭source'd 脚本,但通过内部标志,据我所知,该标志不可见。它肯定不会出现在$SHELLOPTS。自从一个sourced 脚本在当前的 bash 上下文中运行,它共享当前的执行环境,包括 shell 选项。所以在执行一个source从交互式会话启动的 d 脚本,您将看到两者history and histexpand in $SHELLOPTS,但不会发生历史扩展。为了启用它,您需要:

set -o history

这不是一个无操作,因为它具有重置抑制历史记录的内部标志的副作用。设置histexpandshell 选项没有这种副作用。

简而言之,我不确定您如何设法在脚本中启用历史记录扩展(如果确实,行为不当的命令位于脚本中而不是交互式 shell 中),但您可能需要考虑不这样做,除非您有一个很好的理由。

历史扩展是如何解析的

The bash implementation of history expansion is designed to work with readline, so that it can be performed during command input. (By default this function is bound to Meta-^; generally Meta is ESC, but you can customize that as well.) However, it is also performed immediately after each line is input, before any bash parsing is performed.

By default, the history expansion character is !, and -- as mostly documented -- that will trigger history expansion except:

  1. when it is followed by whitespace or =

  2. if the shell option extglob is set, and it is followed by ( [Note 1]

  3. 如果它出现在单引号字符串中

  4. if it is preceded by a \ [Note 2 and see below]

  5. if it is preceded by $ or ${ [Note 1]

  6. if it is preceded by [ [Note 1]

  7. (从 bash v4.3 开始)如果它是双引号字符串中的最后一个字符。

The immediate issue here is the precise interpretation of the third case, an ! appearing inside of a single-quoted string. Normally, bash starts a new quoting context for a command substitution ($(...) or the deprecated backtick notation). For example:

$ s=SUBSTITUTED
$ # The interior single quotes are just characters
$ echo "'Echoing $s'"
'Echoing SUBSTITUTED'
$ # The interior single quotes are single quotes
$ echo "$(echo 'Echoing $s')"
Echoing $s

然而,历史扩展扫描仪并不是那么智能。它跟踪引号,但不跟踪命令替换。所以就其而言,上例中的两个单引号都是双引号单引号,也就是普通字符。所以历史扩展发生在他们两个身上:

# 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

所以如果你有一个完全正常的命令,比如sed '/foo/!d' file,您希望单引号能够保护您免受历史扩展的影响,并将其放入双引号命令替换中:

result="$(sed '/foo/!d' file)"

you suddenly find that the ! is a history expansion character. Worse, you can't fix this by backslash escaping the exclamation point, because although "\!" inhibits history expansion, it doesn't remove the backslash:

$ echo "\!"
\!

在这个特定的例子中——以及OP中的例子——双引号是完全没有必要的,因为变量赋值的右侧既不会进行文件名扩展,也不会进行分词。但是,在其他情况下,删除双引号会更改语义:

# Undesired history expansion
printf "The answer is '%s'\n" "$(sed '/foo/!d' file)"

# Undesired word splitting
printf "The answer is '%s'\n" $(sed '/foo/!d' file)

在这种情况下,最好的解决方案可能是将 sed 参数放入变量中

# Works
sed_prog='/foo/!d'
printf "The answer is '%s'\n" "$(sed "$sed_prog" file)"

(在这种情况下,$sed_prog 周围的引号是不必要的,但通常是必要的,而且它们没有什么害处。)


Notes:

  1. 当后面的字符是某种形式的左括号时,只有在字符串的其余部分中存在相应的右括号时,历史扩展的抑制才起作用。但是,它不必真正匹配左括号。例如:

    # No matching close parenthesis
    $ echo "!("
    bash: !: event not found
    # The matching close parenthesis has nothing to do with the open
    $ echo "!(" ")"
    !( )
    # An actual extended glob: files whose names don't start with a
    $ echo "!(a*)"
    b
    
  2. 如中所示bash手册中,如果历史扩展字符前面紧跟着一个反斜杠,则该字符将被视为普通字符。这确实是事实;反斜杠稍后是否被视为转义字符并不重要:

    $ echo \!
    !
    $ echo \\!
    \!
    $ echo \\\!
    \!
    

    \ also inhibits history expansion inside double quotes, but \! is not a valid escape sequence inside the double quoted string, so the backslash is not removed:

    $ echo "\!"
    \!
    $ echo "\\!"
    \!
    $ echo "\\\!"
    \\!
    
  3. 在撰写本文时,我指的是 bash v4.2 的源代码,因此任何未记录的行为可能与 v4.3 完全不同。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何解决 Bash 命令替换中的错误“bash: !d': event not found”[重复] 的相关文章

  • 错误:分配关联数组时必须使用下标

    我想要 bash 中的哈希图等效项 键为string和价值观作为list的整数 所以 我写了下面的代码 declare A PUBS PUBS FEE 345 342 FOO 1 2 44 BAR 23 67 但是 我收到一条错误消息mus
  • 无法弄清楚如何使用 AppleScript 实现 REGEX

    我编写了一个正则表达式命令来查找并输出字符串中一行数字的第一个实例 find d d replace 1 问题是 为了在 AppleScript 中实际利用它 我知道的唯一方法是调用 shell 脚本并使用 sed 我不知道如何以这种方式实
  • 通过多个换行符分割文件

    假设您有以下输入文件 Some text It may contain line breaks Some other part of the text Yet an other part of the text 并且您想要迭代每个文本部分
  • 将全名解析为其组成部分

    我们需要开发一个后端应用程序 可以将全名解析为 Prefix Dr Mr Ms etc First Name Last Name Middle Name etc 这里的挑战是它必须支持多个国家和语言的名称 我们的一个假设是 我们总是会得到一
  • Bash:使用参数扩展查找和替换

    我想更换输入法 find string include 圆进度 38px 30px 4eb630 和输出 输出字符串 include 圆进度 38px 30px using find string pattern replacement s
  • Python 方程解析器

    我正在编写一个程序 需要用户输入 x 的多项式函数 我正在使用 Tkinter 和 python 2 5 我有一个解析器方法 到目前为止 它接受输入的方程并将其拆分为项而不丢弃符号 我想获取每个术语并解析它以获得 系数 度 的元组 例如 2
  • 通过匹配模式将字符串替换为另一个文本文件中的行

    我有一个带有对应键 gt 值的文件 sort keyFile txt head ENSMUSG00000000001 ENSMUSG00000000001 Gnai3 ENSMUSG00000000003 ENSMUSG0000000000
  • Prolog - DCG 解析器,带有来自文件的输入

    作为项目的一部分 我需要编写一个解析器 它可以读取文件并解析为我可以在程序中使用的事实 文件结构如下所示 property el1 el2 我最终想要的是 property el1 property el2 我这样读我的文件 main op
  • 如何在shell脚本中扩展相对路径

    我正在编写一个脚本来使用 bash 在 linux 2 6 上设置环境变量 因此该脚本包含如下命令 export SRC DIR export LIBPATH SRC DIR lib 问题是 当我尝试 echo LIBPATH 时 它显示
  • 当存储在变量中时,Git 提交消息变得混乱[重复]

    这个问题在这里已经有答案了 我有一个 Git 提交 其中有一个摘要 然后是一些描述 所以当我看到提交消息时git log format B n 1
  • SQLPlus 中的运行循环

    我制作了一个 bash 脚本 它通过 SQLPlus 连接到数据库并运行一个包含 For 循环的 SQL 脚本 如下所示 但是一旦运行它 它就会卡在循环的 BEGIN 中 如下所示 我尝试直接通过SQLPlus运行 结果是一样的 那么任何人
  • sed:用匹配的模式替换第 n 个单词?

    我有一个具有以下特征的文本文件 每行至少有三个由空格分隔的 单词 单词 可以是任何字符或字符串 我在一些行中附加了一些注释 并提出了对原始单词进行更改的初步建议 现在想使用 sed 为我进行这些更改 因此 为了提供更清晰的图片 我的文件如下
  • 带有转义字符的 JavaCC 引用

    标记可以包含转义字符的带引号的字符串的常用方法是什么 这里有些例子 1 this is good 2 this is good 3 this is good 4 this is bad 5 this is bad 6 this is bad
  • 如何在多行而不是单行输出上打印 Linux 组名称

    我尝试过 getent group 命令 id Gn user 和一些 sed 组合 但我认为我无法实现 因此向其他程序员伸出援手 我希望能够打印此 groups abc123输出 abc123 devops 构建测试设计 预期输出 gro
  • shell脚本响应按键

    我有一个 shell 脚本 本质上是这样的 while true do read r input if input a then echo hello world fi done 这一切都很好 但我刚刚意识到在这种情况下必须按 ENTER
  • 检测目录中是否有某些内容被修改,如果是,则备份 - 否则不执行任何操作

    我有一个 数据 目录 我通过 shell 脚本定期同步到远程 NAS 但是 我想让这变得更有效率 我想在运行 rsync 之前检测 数据 中是否发生了变化 这样我就不会不必要地唤醒 NAS 上的驱动器 我正在考虑修改 shell 脚本以获取
  • Bash IF:多个条件

    我已经尝试让这件事工作几个小时 但我无法让它工作 if P SFTP a PORT 22 P FTPS PORT 990 a PORT 21 then 有人能帮我吗 我知道多个条件可以这样写 if P SFTP PORT 22 then 但
  • $.parseJSON() 在有效对象上返回 null

    jsfiddle 链接 http jsfiddle net YmUmp 1 var x Item1 1 Item2 Item3 3 alert JSON stringify x undefined 2 alert parseJSON x 第
  • 错误:选项“Z”仅在夜间编译器上被接受[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在通过斯坦福操作系统课程 cs140e https web stanford edu class cs140e 其中一个问题是 有一个名
  • 为什么 sed 将替换文本放在行首?

    在 Mac OS X bash 上 我在命令行中使用以下 sed sed s S 1 file1 txt gt file2 txt file1 txt有这样的行 some path to somewhere some stuff here

随机推荐

  • 直接在SQLite中计算两点之间的距离

    在我的 web MySQL 应用程序中 我有类似的方法来获取两点之间的距离 6371 acos cos radians 19 83996 cos radians lat cos radians 43 94910 radians lng si
  • 如何从 preg_split 结果中删除空数组? [复制]

    这个问题在这里已经有答案了 举个例子 我有大量的正则表达式 就像我写的一样简单 php gt var dump preg split reg s reg a zA Z array 3 0 gt string 0 1 gt string 13
  • 裤子包括 OS X 特定的 Python 轮子

    TLDR Pants 获取 OS X 特定的轮子 因为我正在 Mac 上开发 我怎样才能避免这种情况 或者指定我将部署到 Ubuntu 完整故事 尝试用 Pants 打包 Python 应用程序 到目前为止进展顺利 但遇到了一个困扰我一段时
  • 以编程方式设置或查看“高级 Wifi”设置

    我需要一种以编程方式打开 高级 wifi 设置的方法 以让用户更改某些设置 或者最好以编程方式更改这些高级无线设置 到目前为止 我只能通过 startActivity new Intent Settings ACTION WIFI SETT
  • 什么样的类型定义在本地环境中是合法的?

    在伊莎贝尔的NEWS文件 我发现 命令 typedef 现在可以在本地理论上下文中工作 无需 引入对参数或假设的依赖 这不是 可以在 Isabelle Pure HOL 中实现 请注意 逻辑环境可能 包含本地 typedef 的多种解释 具
  • Kafka Connect 接收器任务忽略容差限制

    我尝试忽略接收器连接器中的错误消息errors tolerance all选项 完整的连接器配置 name crm data sink pandora config connector class io confluent connect
  • Linux下C语言读写串口

    我试图使用 FTDI 通过 USB 端口发送 接收数据 所以我需要使用 C C 处理串行通信 我正在尝试Linux 乌班图 基本上 我连接到一个正在侦听传入命令的设备 我需要发送这些命令并读取设备的响应 命令和响应都是ASCII 字符 使用
  • C++ 中的设置/获取方法

    Java 程序员和 API 似乎更喜欢显式的 set get 方法 然而我的印象是 C 社区不赞成这种做法 如果是这样 是否有特殊原因 除了更多行代码之外 为什么会这样 另一方面 为什么Java社区选择使用方法而不是直接访问 谢谢 理想情况
  • 使用 Rust 稳定通道和夜间通道并行编译代码有多困难?

    Cargo 创建的默认文件树允许并行编译发布和调试版本 因为它们位于自己的目录中 target release and target debug 分别 允许并行编译有多困难stable nightly 编译器 例如使用目录 target d
  • 在 R 中合并两个列表

    我有两个清单 first list a 1 b 2 c 3 second list a 2 b 3 c 4 我想合并这两个列表 所以最终产品是 a 1 1 2 b 1 2 3 c 1 3 4 有一个简单的函数可以做到这一点吗 如果列表始终具
  • 更新部分主键Entity Framework 4.0

    我有一个带有复合主键的表 3 列 UTP ID ITEMId UTS ID CategoryID USS ID SubCategoryID 例如 当我尝试使用 EF 4 更改 SubCategory 时 出现以下错误 utl USS ID
  • 写入帧缓冲区

    我使用的是带有内置图形加速器 GMA HD 的 i5 内核 在 RHEL 6 0 操作系统上运行 我需要测试显卡驱动程序的图形加速能力 我发现我的电脑上是i915 我使用以下代码 我从互联网上获得并进行了一些修改 来写入帧缓冲区 inclu
  • 多行搜索替换为 Perl

    我知道这类问题以前已经被问过很多次了 我再次来到这里的原因是我觉得我错过了一些简单而基本的东西 是否有可能使这种搜索替换例程变得更好 例如 无需打开同一文件两次 也欢迎与速度相关的建议 请注意 这适用于多行匹配并替换多行字符串 bin pe
  • 如何将 2D Eigen::Tensor 更改为 Eigen::Matrix

    看起来很简单 我本以为某种类型的转换是可能的 但我似乎找不到任何相关文档 虽然我在应用程序中找到了避免使用 Eigen Matrix 类的方法 但 TensorFlow 仅适用于 Eigen Tensor 而我使用的另一个库仅具有直接使用
  • Python将字符串拆分为多个字符串[重复]

    这个问题在这里已经有答案了 可能的重复 在Python中将字符串分割成列表 我有一个带有很多部分字符串的字符串 gt gt gt s str1 str2 str3 str4 现在我有一个类似以下的功能 gt gt gt def f args
  • 迁移到 rbenv 会破坏乘客 (nginx)

    我在生产服务器上从 RVM 迁移到 rbenv 我使用 rvm implode 卸载了 rvm 并安装了 rbenv ruby 1 9 2 rails passenger 和 nginx module 除了按如下方式更改 Passenger
  • 查找 numpy 数组中最接近的值

    我如何找到最接近的值在 numpy 数组中 例子 np find nearest array value import numpy as np def find nearest array value array np asarray ar
  • 如何缩短 ggplot 中的 x 轴标签文本?

    我正在 ggplot 中绘制一个图 其中 x 轴标签包含不同蛋白质的名称 但我遇到了麻烦 因为其中一些名称太长 并且标签变得太大 以至于很难看到图 有什么方法可以减少 x 轴标签中的字符数 而不是 打印 更大的图表 这是一个显示我的问题的示
  • Bash - 计算输入数字的平均值

    需要有关 Linux Bash 脚本的帮助 本质上 运行脚本时会要求用户提供三组数字 然后计算输入的数字并得出平均值 bin bash echo Enter a number read a while a do echo Enter a s
  • 如何解决 Bash 命令替换中的错误“bash: !d': event not found”[重复]

    这个问题在这里已经有答案了 我正在尝试解析 VNC 服务器启动事件的输出 但在命令替换中使用 sed 进行解析时遇到了问题 具体地 远程VNC服务器的启动方式如下 address1 email protected VNCServerResp