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

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

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

alias root="sudo !!"

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

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

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

答案1

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

在本HISTORY EXPANSION节中:

读取完整行后,在 shell 将其分解为单词之前,立即执行历史扩展。

在本ALIASES节中:

每个简单命令的第一个单词(如果未加引号)将被检查以查看它是否有别名。

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


替代解决方案

我能想到的最好方法如下:

alias root='sudo $(fc -ln -1)'

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

alias root='sudo sh -c "$(fc -ln -1)"'

改变的原因是这样的:

# alias root='sudo $(fc -ln -1)'
# echo "I AM $(whoami)"
I AM patrick
# root
"I AM $(whoami)"

如您所见,没有完成 shell 解析。通过将其改为 use sh -c "...",它可以进行 shell 解释。

# alias root='sudo sh -c "$(fc -ln -1)"'
# echo "I AM $(whoami)"
I AM patrick
# root
I AM root

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

alias root='fc -e "sed -i -e \"s/^/sudo /\""'

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

答案2

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

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

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

$ history
1081  20131205 08:00:12  ls
1082  20131205 08:00:13  history

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

作为别名?

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

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

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

相关内容