在bash脚本中设置别名

3
我正在尝试创建一个脚本别名,如下所示:

我正在尝试在脚本中创建一个别名,代码如下:

#!/bin/bash
shopt -s expand_aliases

#Currently just using 'git' to try and get it working
#It'll be replaced with the following line when it works
#compgen -c | while read x;
echo 'git' | while read x;
  do
    a=`echo $x | tr '[:lower:]' '[:upper:]'`;
    echo $a;
    echo $x;
    if [ "$a" != '' ]
    then
      echo 'got here';
      alias $a=$x;
      alias;
      GIT;
    fi
done

虽然一个简单的测试脚本可以工作(testme已经添加到当前shell的别名中):

#!/bin/bash
shopt -s expand_aliases
alias testme="echo It Worked"
alias
testme

第一个脚本的输出:
GIT
git
got here
alias GIT='git'
alias grep='grep --colour=auto'
alias ls='ls --color=auto'
GIT: command not found

GIT 出现在别名中,但无法执行。我尝试使用 . ./script.shsource script.sh 调用它。我做错了什么?


1
你在脚本中不应该依赖别名。你应该直接使用变量。但如果你仍然想使用别名,也许这个链接可以帮到你:https://dev59.com/e2435IYBdhLWcg3wvyw5 - Lynch
整个脚本的重点是设置别名,而不是运行它。虽然有点无用,但很有趣! - SomeKittens
1个回答

8
很可能是别名扩展的时间问题。Bash手册中有以下说明:
“关于别名的定义和使用的规则有些令人困惑。Bash在执行该行上的任何命令之前,总是至少读取完整的一行输入。别名在读取命令时扩展,而不是在执行命令时扩展......”
手册中还有更多信息,但当执行类似于您的while循环中的列表这样的复合语句时,扩展将在首次读取输入时发生,而不是在执行时发生。由于在读取输入时未定义GIt,因此会出现错误。
以下代码片段显示了该问题:
#!/bin/bash
shopt -s expand_aliases

while read x; do
    a=`echo $x | tr '[:lower:]' '[:upper:]'`
    alias $a=$x
    GIT   # <--- this fails to execute git
done < <(echo 'git')

GIT # <--- this executes git

一旦while循环执行完毕,别名就变得可用了。手册中的细节有点模糊,但似乎函数定义的别名注释也适用于此。
此外,在使用echo foo | while read x; do stuff; done等语法时要小心。while循环将在子shell中执行,子shell中所做的任何更改都不会持续到while循环之外。上面的示例展示了一种方法,使while循环在当前shell中执行,但从执行的命令中获取其输入。
更新:
如果以上脚本以其原始形式编写,则如下所示:
#!/bin/bash
shopt -s expand_aliases

#Currently just using 'git' to try and get it working
#It'll be replaced with the following line when it works
#compgen -c | while read x;

echo 'git' | while read x; do
    a=`echo $x | tr '[:lower:]' '[:upper:]'`
    alias $a=$x
    GIT   # <--- this fails to execute git
done

GIT # <--- this also fails executes git because the alias' were set in a subshell.

在这种情况下,别名永远不会被执行。原因是管道符“|”启动了一个新的子shell,并将进程连接在一起。echo命令在一个进程中运行,而while read ...在另一个进程中运行,通过管道连接在一起。当设置别名时,它们是在运行while循环的子shell进程的环境中设置的。然而,当while循环结束时,该进程终止并丢弃其环境,包括对其环境进行的任何更改,例如添加别名。结果,第二次尝试执行GIT时将失败,因为别名信息已被丢弃。
当您使用第一个脚本中显示的拓扑结构时,while循环在当前脚本中运行,因此对环境所做的任何更改都会在循环结束后持续存在。
如果您执行脚本而不是将其作为源文件运行,则还会注意到这种行为。如果您希望能够在此类脚本中配置一组别名,运行脚本并在环境中使用它们,则会遇到同样的问题,因为您的脚本在与父Shell分开的进程中运行,创建了别名,但随后在脚本退出时被丢弃。
$ ./create_aliases
$ alias  # <--- Ooops, no aliases defined.

您需要像这样引用您的脚本,这将在当前进程中运行脚本内容,从而修改当前环境。
$ source ./create_aliases
$ alias # <--- Yay, new aliases present.

这就是为什么bash会读取文件,如 ~/.bashrc, 这样你在该文件中所做的更改将存在于当前环境中。

太好了!我认为可能与子Shell有关,但无法弄清楚(对bash脚本非常新)。您可以进一步解释一下 < <(echo 'git') 如何导致 while 在当前Shell中执行吗? - SomeKittens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接