Bash 隐藏命令 - 与命令同名的函数

jer*_*ile 7 bash alias command

我有一个功能.bashrc可以自动 sudo 打开我不能写的文件:

vim() {
    if [ -w "$1" ]; then
        \vim "$1"
    else
        sudo env HOME="$HOME" \vim -u ~/.vimrc "$1"
    fi
}
Run Code Online (Sandbox Code Playgroud)

当文件需要 sudo 时,它工作正常。如果没有,它会递归调用此函数并使用 100% 的 1 个 CPU,直到 I CC。

从这个答案我看到有几个选项,所有这些我都尝试过。一个确实有效:

'vim' "$1" #fails
\vim "$1" #fails
command vim "$1" #Works!
Run Code Online (Sandbox Code Playgroud)

为什么其他选项不能像我期望的那样工作?

(我知道这是重复的,但是很难用当前的问题标题在 SO/SE 上找到我的答案,所以我想发布一个问题,标题是我和其他人可以通过谷歌搜索找到的)

jer*_*ile 10

问题是前两个选项仅用于处理别名。它们不是特殊的重定向运算符,它们可以感知您是否有同名的函数或命令。他们所做的只是防止扩展,这就是别名试图做的

alias v='sudo vim'
v x.txt
#automatically expands "v" to "sudo vim" and then does the rest of the command
# v x.txt ==> sudo vim x.txt
Run Code Online (Sandbox Code Playgroud)

Bash 只是尝试使用它知道的别名列表来扩展命令的第一个单词(您可以使用 获得alias)。别名不带参数,并且只能是命令中的第一个单词(空格分隔;vim x.txt 不会扩展为sudo vimim x.txt使用上面的别名)以正确扩展。

但是,扩展永远不会发生在单引号中:echo '$USER'将打印出文字$USER而不是变量代表的内容。此外,bash 扩展\xx(大部分)。这些不是专门用于转义别名的额外附加方法,它们只是编写 bash 扩展的一部分。因此,您可以使用这些方法让别名隐藏命令,并且仍然可以访问实际命令。

alias ls='ls -la'
ls foo.txt     #will expand into ls -la foo.txt
\ls foo.txt    #will expand into ls foo.txt
'ls' foo.txt   #same as above
'ls foo.txt'   #same as above
Run Code Online (Sandbox Code Playgroud)

但是,它们不会停止功能。一个函数不需要扩展就可以工作,它是用它的名字来调用的。

ls () {
    echo "not an alias"
}
alias ls='echo "an alias"'

ls foo.txt          #will echo "an alias"
\ls foo.txt         #will echo "not an alias"
'ls' foo.txt        #will echo "not an alias"
command ls foo.txt  #will actually run `ls`
Run Code Online (Sandbox Code Playgroud)