在外壳中,我运行以下命令没有问题,
ls -al
!ls
第二次调用ls
也列出带有-al
标志的文件。但是,当我将上述脚本放入 bash 脚本时,会引发投诉,
!ls, command not found.
如何在脚本中实现相同的效果?
您需要在脚本中同时打开命令历史记录和 !-style 历史扩展(在非交互式 shell 中默认情况下两者都关闭):
set -o history
set -o histexpand
扩展的命令也会响应标准错误,就像在交互式 shell 中一样。您可以通过打开histverify
shell 选项 ( shopt -s histverify
) 来防止这种情况,但在非交互式 shell 中,这似乎会使历史扩展成为空操作。
好吧,我也想让这个工作,我必须告诉大家,这个set -o history ; set -o histexpand
方法在. 无论如何,它并不意味着在那里使用,因为有更好的方法来实现这一点。bash 4.x
首先,一个相当简单的例子,只是想在脚本中执行 history
:(bash
4.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+(但可能适用于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
PS 这个 scriptlet 实际上是一种解决方法,模拟!1234
在 bash shell 上键入。由于我永远无法直接在脚本中完成工作(!
永远不会让我透露历史第 1234 行的内容),因此我通过简单地ing 来解决我想要复制的行的问题。echo
grep
历史扩展是 shell 的交互式命令行编辑功能的一部分,而不是脚本语言的一部分。它通常在脚本的上下文中不可用,只有在与(伪)人工操作员交互时才可用。(伪意味着它可以与类似expect
或其他按键重复自动化工具一起工作,这些工具通常试图扮演人类,而不是暗示任何特定的操作员可能是非人类或任何东西)。