【问题标题】:use "!" to execute commands with same parameter in a script采用 ”!”在脚本中执行具有相同参数的命令
【发布时间】:2023-03-04 18:38:02
【问题描述】:

在 shell 中,我运行以下命令没有问题,

ls -al

!ls

ls 的第二次调用也会列出带有-al 标志的文件。但是,当我将上述脚本放入 bash 脚本时,会引发投诉,

!ls, command not found.

如何在脚本中实现同样的效果?

【问题讨论】:

  • 最好只复制上一行,而不是依赖脚本中的命令历史记录。
  • 是否可以使用不同的命令来做到这一点?例如 mkdir + cd?
  • 虽然您可以启用此功能,但这是一种不好的编程习惯。它会使您的脚本容易出错且难以理解。默认情况下禁用它是有原因的。为什么要这样做?

标签: bash scripting


【解决方案1】:

您需要在脚本中同时打开命令历史记录和 !-style 历史扩展(在非交互式 shell 中默认情况下两者都关闭):

set -o history
set -o histexpand

扩展的命令也会被回显到标准错误,就像在交互式 shell 中一样。您可以通过打开histverify shell 选项 (shopt -s histverify) 来防止这种情况发生,但在非交互式 shell 中,这似乎会使历史扩展成为空操作。

【讨论】:

  • 我也有同样的想法,但是经过测试,这并没有在脚本中提供工作历史扩展。但是,我无法想象这个用例。历史扩展是为了方便交互使用。
  • 我犯了两个错误; set -Hset -o histexpand,除了set -o history,你还需要它。
  • 在bash4中,它投诉histcommand无效...它有什么用..?
【解决方案2】:

好吧,我也想让它工作,我必须告诉大家set -o history ; set -o histexpand 方法将bash 4.x 中工作。无论如何,它并不打算在那里使用,因为有更好的方法来实现这一点。

首先,一个相当琐碎的例子,只是想在一个脚本中执行history: (bash 仅限 4.x 或更高版本

#!/bin/bash -i
history

简短的回答:它有效!
全新的-i 选项代表interactivehistory 起作用。但目的是什么?

引用 OP 中 Michael H. 的评论:
“虽然您可以启用此功能,但这是一种不好的编程习惯。它会使您的脚本 (...) 难以理解。有一个因为默认情况下它是禁用的。你为什么要这样做?"

是的,为什么?这更深层的意义是什么? 嗯,有,我将在后续部分演示。

我的history 缓冲区已经变大了,而其中一些行是单行脚本,我真的不想每次都重新输入。但有时,我也想稍微改变这些行,因为我可能想给出第三个参数,而我之前总共只需要两个。 所以这是使用bash 4.0+ 功能调用history理想方式:

$ history
 (...)
 <lots of lines>
 (...)
  1234 while IFS='whatever' read [[ $whatever -lt max ]]; do ... ; done < <(workfile.fil)
 <25 more lines>

所以 1234 来自历史正是我们想要的行。当然,我们可以用 鼠标 移动到那里,将整条线夹在主缓冲区中吗?但是我们在 *NIX 上,那么为什么我们不能让我们的生活更轻松一些呢? 这就是我写下面这个小脚本的原因。同样,这仅适用于 bash 4.0+(但可能适用于 bash 3.x 和更早的版本以及上述 set -o ... 的东西......)

#!/bin/bash -i
[[ $1 == "" ]] || history | grep "^\s*$1" | 
awk '{for (i=2; i<=NF; i++) printf $i" "}' | tr '\n' '\0'

例如,如果你把它保存为xselauto.sh,你可以调用

$ ./xselauto.sh 1234

history 第 1234 行的内容将在您的主缓冲区中,可以重复使用! 现在,如果有人仍然说“这没有 AFAICS 的目的”或“谁会需要这个功能?” - 好吧,我不在乎。但我不想再没有这个功能,因为我懒得每次都重新输入复杂的行。而且我也不想为历史中的每条标记线触摸鼠标,TBH。这就是 xsel 的用途。

顺便说一句,管道的tr 部分是一个肮脏的黑客,它将阻止命令被执行。对于“危险”的命令,在用户按下 Enter 键执行之前,始终让用户有一个查看的方式非常非常重要它。您可以省略它,但是...您已被警告。


附注这个 scriptlet 实际上是一个workaround,模拟在 bash shell 上键入的!1234。因为我永远无法让 ! 在脚本中直接工作(echo 永远不会让我透露历史行 1234 的 内容),所以我解决了这个问题只需 greping 我想复制的行。

【讨论】:

  • 您可能应该使用!1234:p 来显示历史中项目的内容。还有一些方法可以使用!?whatever?:p grep 和匹配历史记录中的行,这会在不执行的情况下显示该历史记录项的内容。
  • 还有一个更好的方法来做你所追求的事情是启用histverify,这样历史命令在使用!引用时不会执行,而是粘贴到你的提示中让你执行。
【解决方案3】:

历史扩展是 shell 的交互式命令行编辑功能的一部分,而不是脚本语言的一部分。它通常在脚本的上下文中不可用,只有在与(伪)人工操作员交互时才可用。 (伪意味着它可以与expect 或其他通常尝试扮演人类角色的击键重复自动化工具一起工作,并不意味着任何特定的操作员可能是非人类或任何东西)。

【讨论】:

  • 虽然我同意它不应该在脚本中使用,但它不可用并不是真的。
  • 这就是为什么我说“不普遍可用” - 你必须做一些额外的事情才能做到这一点......甚至这取决于什么外壳 - 有一些地方是不可能的,尽管他们这些天可能没有广泛使用......
  • @twalberg - 问题被标记为 bash,所以我认为这是唯一重要的 shell。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 1970-01-01
  • 2020-09-15
  • 2014-06-28
  • 2010-11-29
  • 2010-10-06
相关资源
最近更新 更多