为什么 !!里面的别名不起作用?

xan*_*adu 7 bash command-history alias history-expansion

我在系统/etc/bashrc文件中设置了这个别名:

alias root="sudo !!"
Run Code Online (Sandbox Code Playgroud)

这样做的目的是运行最后使用的命令sudo,当然。使用时,它当然似乎是在 shell 初始化时将最后一个命令替换historybashrc文件中,而不是sudo !!在交互式 shell 中运行时将获得的实际命令。我也试过alias root="sudo fc -s"没有用。

我意识到这可能与 BASH 如何执行命令替换有关,但有人可以解释为什么会这样,并提供一个可用的替换吗?

我正在运行 BASH 版本 3.2.51(1)-release (x86_64-apple-darwin13)。

phe*_*mer 8

此行为的关键部分由 bash 联机帮助页中的 2 位解释:

在该HISTORY EXPANSION部分:

读完一行后立即执行历史扩展,然后 shell 将其分解为单词。

在该ALIASES部分:

每个简单命令的第一个单词,如果没有被引用,则检查它是否有别名。

所以基本上历史扩展发生在分词之前。别名扩展发生在之后。


替代解决方案

我能想到的最好的方法是:

alias root='sudo $(fc -ln -1)'
Run Code Online (Sandbox Code Playgroud)

这适用于简单的命令,但对于更复杂的命令,我们需要对其进行调整。

alias root='sudo sh -c "$(fc -ln -1)"'
Run Code Online (Sandbox Code Playgroud)

改变的原因是因为这样的事情:

alias root='sudo $(fc -ln -1)'
Run Code Online (Sandbox Code Playgroud)

如您所见,没有完成任何外壳解析。通过将其更改为使用sh -c "...",它会执行 shell 解释。

alias root='sudo sh -c "$(fc -ln -1)"'
Run Code Online (Sandbox Code Playgroud)

另一种方式(我首先想到了这个,因此将其保留在答案中,但不如上面的好):

alias root='fc -e "sed -i -e \"s/^/sudo /\""'
Run Code Online (Sandbox Code Playgroud)

fc -e命令将运行指定的命令,并传递一个包含先前执行的命令的文件。我们只是sed在该文件上运行,在命令前加上sudo.


slm*_*slm 2

您不能以这种方式使用别名。别名不能传递诸如 之类的参数!!。为了达到你想要的效果,你可以使用函数来代替。

function root() {
  sudo $(history | tail -2 | head -1 | awk '{$1=$2=$3=""; print $0}');
}
Run Code Online (Sandbox Code Playgroud)

但这是一个粗略的想法,可能会存在一些问题。我的历史命令的输出如下所示:

$ history
1081  20131205 08:00:12  ls
1082  20131205 08:00:13  history
Run Code Online (Sandbox Code Playgroud)

所以我需要解析这个输出。在上面,我正在运行历史记录,获取最后 2 个命令,然后获取最后 2 个命令中的第一个,这是之前运行的命令。然后我用来awk删除前 4 列,留下之前运行的命令行。

作为别名?

鉴于我们使用的是 的输出history,实际上没有任何理由再使用 Bash 函数。如果您尝试将上一个命令作为参数传递,则需要这样做,但我们history现在通过该命令获取它。

$ alias root="sudo \$(history | tail -2 | head -1 | awk '{\$1=\$2=\$3=\"\"; print \$0}')"
Run Code Online (Sandbox Code Playgroud)

除了将其转换为别名所需的更棘手的转义之外,上面的工作原理与该函数类似。