如何在不使用`!!`的情况下运行最后一个命令?

时间:2014-12-15 23:03:46

标签: shell zsh zshrc

我试图将_!替换为sudo最后一个命令,但我遇到了障碍。 !!似乎无法在我的.zshrc文件中运行,并且sed给了我一再重复的问题。我尝试使用以下命令及其中的几个变体,但无济于事。

history | tail -1 | sed -e 's/[^0-9\*\ ]+/\0/g'

但是,这仍然将管道输入解释为文件,而不是文本字符串。我还尝试了使用awk的变体:

history | tail -1 | awk '{ gsub("/[^0-9\*\ ]+", "") ; system( "echo" $1 ) }'

我确定我在将命令正确输入时遇到了一些麻烦,但我们会感激一些帮助。

3 个答案:

答案 0 :(得分:6)

您可以使用fc内置程序以编程方式访问历史记录。例如,我相信这会按照您的意愿行事:

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

没有参数,fc("修复命令"的缩写)会激活前一个命令的$EDITOR并运行编辑结果。您可以使用-e选项指定其他编辑器;在这里,我以sed命令的形式指定一个非交互式命令,它将sudo插入命令行前面。

该命令假定GNU为sed。如上所述,它也适用于现代BSD / macOS上的版本,但是通过hackcident:它将-e视为-i的参数而不是新选项。由于-e是可选的,只有一个表达式,这样可以正常工作,但这意味着sed在最后使用-e对临时文件进行备份,这将在命令完成。您可以在这些系统上使用此备用版本来使其更清洁:

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

赢得' 使用GNU sed,它将空字符串参数视为要操作的文件名...)

在较旧的系统上,更便携的解决方案可以使用ed

alias _!="fc -e 'ed -s <<<$'\''s/^/sudo /\nw\nq'\'"

你可以经常使用更简单的东西,比如sudo $(fc -ln -1)-l =列表命令,-n =没有数字,-1 =只有最后一个命令),但是一般情况下,您会遇到引用问题,因为命令是按fc输入的方式输出的:

% touch '/etc/foo bar'
touch: /etc/foo bar: Permission denied
% sudo $(fc -ln -1)
touch: '/etc/foo: No such file or directory

以上都不限于zsh,btw; fc与ksh的原始版本保持一致,因此它也适用于bash等。

答案 1 :(得分:3)

fc命令将始终在zshbash中提供最近执行的命令:

fc -ln -1

根据help fc

 -l (letter el) list lines instead of editing
 -n omit line numbers when listing

-1(减1)获得刚刚执行的命令。

答案 2 :(得分:1)

为sudo找到了一个惊人的小部件:

sudo-command-line() {
    [[ -z $BUFFER ]] && zle up-history
    [[ $BUFFER != sudo\ * ]] && {
      typeset -a bufs
      bufs=(${(z)BUFFER})
      if (( $+aliases[$bufs[1]] )); then
        bufs[1]=$aliases[$bufs[1]]
      fi
      bufs=(sudo $bufs)
      BUFFER=$bufs
    }
    zle end-of-line
}
zle -N sudo-command-line
bindkey "\e\e" sudo-command-line

作者:lilydjwg


以下是在命令中运行最后一个命令的方法:


fc -ln -1是最简单的方法,但有一个问题,当在命令开头运行某些空格时,此命令将不会显示在历史记录中,基于历史记录的任何内容都不会正常工作

因此我们需要ZLE(Zsh Line Editor)手动存储命令。

Store_Your_Command () {
    if [[ -z $BUFFER ]]
    then
        # If nothing input, just clear the screen
        zle clear-screen
    else
        zle accept-line
        # Remember the last command, useful in some alias
        # Add space at the beginning of a command, this command wont
        # show up in history, so use variables to store the command
        LAST_COMMAND=$CURRENT_COMMAND
        CURRENT_COMMAND=$BUFFER
    fi
}
# Create a user-defined widget
zle -N Store_Your_Command
# Bind it to the **Enter** key
bindkey "^M" Store_Your_Command

然后每当我们按Enter键运行命令时,此命令将存储在$ CURRENT_COMMAND中,最后一个命令将存储在$ LAST_COMMAND中。

想要运行最后一个命令?只需运行eval $LAST_COMMAND,您也可以将其添加到别名中。


当在最后一个命令中使用某个别名时,zsh不会正确运行最后一个命令,因此我们需要扩展别名:当我们输入别名时,在内置zle的帮助下将别名替换为原始命令/内容: _expand_alias。

首先,删除我们刚刚添加的小部件。 将它们添加到.zshrc:

# When input space, expand alias -----------------------------------{{{
expand_alias_space () {
    zle _expand_alias
    zle self-insert
}
zle -N expand_alias_space
bindkey " " expand_alias_space
# }}}
# When input enter, expand alias -----------------------------------{{{
expand_alias_enter () {
    if [[ -z $BUFFER ]]
    then
        zle clear-screen
    else
        zle _expand_alias
        zle accept-line
        # Remember the last command, useful in some alias
        # Add space at the beginning of a command, this command won't
        # show up in history, so use variables to store the command
        LAST_COMMAND=$CURRENT_COMMAND
        CURRENT_COMMAND=$BUFFER
    fi
}
zle -N expand_alias_enter
bindkey "^M" expand_alias_enter
# }}}

现在我们可以通过按Space键将别名扩展到原始命令/内容,或者只需按Enter键即可运行命令,并使用eval $LAST_COMMAND运行最后一个命令而不执行任何操作问题。


但是,当运行命令使用eval $LAST_COMMAND两次时,它会调用另一个问题:

zsh: job table full or recursion limit exceeded

我们需要将eval $LAST_COMMAND替换为真实命令,因为$LAST_COMMAND总是会改变。 我们写一个函数运行像这样的最后一个命令

# Echo the last command 
fun()
{
    # The command we need to run in this function
    CURRENT_COMMAND="echo \[`echo $LAST_COMMAND`\]"
    # run the command 
    eval $CURRENT_COMMAND
}

$CURRENT_COMMAND中存储的命令不会像eval $LAST_COMMAND那样改变。 问题解决了。


我希望没有更多问题