4

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

ls -al

!ls

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

!ls, command not found.

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

4

3 回答 3

6

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

set -o history
set -o histexpand

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

于 2012-08-23T18:42:18.653 回答
0

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

首先,一个相当简单的例子,只是想在脚本中执行 history:(bash4.x 或更高版本

#!/bin/bash -i
history

简短的回答:它有效!
Spaking new-i选项代表interactive,并且history 可以使用。但为了什么目的?

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

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

我的history缓冲区变大了,而其中一些行是单行脚本,我真的不想每次都重新输入。但有时,我也想稍微改变一下这些行,因为我可能想给出第三个参数,而我之前总共只需要两个。因此,这是使用4.0+ 功能调用的理想方式:bash 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+(但可能适用于bash3.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


PS 这个 scriptlet 实际上是一种解决方法,模拟!1234在 bash shell 上键入。由于我永远无法直接在脚本中完成工作(!永远不会让我透露历史第 1234 行的内容),因此我通过简单地ing 来解决我想要复制的行的问题。echogrep

于 2014-12-09T17:33:02.717 回答
-2

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

于 2012-08-23T19:05:34.863 回答