我正在尝试使用一行命令来定义和alias
使用。bash -c
命令:
bash -c "eval $'df'"
工作正常,但是:
bash -c "eval $'alias df5=df\ndf5 -h'"
没有。为什么以及如何通过一行命令定义和使用alias
内部?bash -c
“The
$'...'
是一个“C 字符串”,在将其传递给 之前bash
会将其中的 扩展\n
为文字换行符eval
。
因此我的理解是必须'
在eval
.此外,由于bash手册说:
将字符括在单引号 (
'
) 中可保留引号内每个字符的字面值。单引号之间不能出现单引号,即使前面有反斜杠也是如此。
我的理解是,应该"
在 the 之外使用,因为里面eval
有.'
eval
评论:
- 看如何将
eval
包含新行的命令写入一行?解释使用\n
代替;
- 这个问题背后的动机是,在我实际的整个命令中,我将别名命令用于其他事情(该命令看起来像
docker run -it ubuntu:18.04 bash -c "eval $'alias pip=pip3\nsource blah.sh; exec bash"
)。哪里blah.sh
使用pip
.完整的实际命令是:docker run --interactive --tty ubuntu:18.04 bash -c "apt update; apt install -y git nano wget htop python3 python3-pip unzip; git clone https://github.com/KhalilMrini/LAL-Parser; cd LAL-Parser/; alias pip=pip3; source requirements.sh; apt-get install -y libhdf5-serial-dev; alias python=python3 ; source parse.sh; exec bash"
,但我需要eval
添加alias
.该命令启动 Docker 容器,安装一些要求并运行一些 Python 代码。 - 该命令面向非技术同事:我希望他们只运行一行命令,以便他们易于使用。因此,我不希望命令需要一个文件(例如,
Dockerfile
或脚本)来工作。bash
以下命令:
bash -c " eval 'alias df5=df df5 -h' "
以及命令
bash -c " alias df5=df df5 -h "
也不行。错误
bash: line 2: df5: command not found
。结果似乎问题是alias
在bash -c
.我不知道为什么,想知道是否有一些解决方法。
答案1
编辑后的问题:
对于编辑后的问题,现在“在 exec bash 之后不需要交互式别名”:
是的,bash -c 失败:
$ bash -c "
alias df5=df
df5 -h
"
bash: line 2: df5: command not found
但不是因为别名设置失败:
$ bash -c "
alias df5=df
alias df5
"
alias df5=df
但是因为在非交互式 shell(主要是脚本)中,默认情况下不会扩展别名,因此您需要执行以下操作:
bash -c "
alias df5=df
shopt -s expand_aliases
df5 -h
"
这是使用函数而不是别名的重要原因之一。
如果这就是您需要克服的全部,那么使用:
docker run --interactive --tty ubuntu:18.04 \
bash -c "
shopt -s expand_aliases;
apt update;
apt install -y git nano wget htop python3 python3-pip unzip;
git clone https://github.com/KhalilMrini/LAL-Parser;
cd LAL-Parser/;
alias pip=pip3;
source requirements.sh;
apt-get install -y libhdf5-serial-dev;
alias python=python3;
source parse.sh;
exec bash
"
它应该作为单行代码(删除换行符)。如果没有请评论。
编辑前的问题:
之前的答案解决了以下问题:在 docker 启动的交互式 shell 中,如果末尾有exec bash
.
阻止在新的交互式 shell 中使用别名的实际问题是整个命令:
docker run --interactive --tty ubuntu:18.04 \
bash -c "
apt update;
apt install -y git nano wget htop python3 python3-pip unzip;
git clone https://github.com/KhalilMrini/LAL-Parser;
cd LAL-Parser/;
alias pip=pip3;
source requirements.sh;
apt-get install -y libhdf5-serial-dev;
alias python=python3;
source parse.sh;
exec bash
"
结束于exec bash
.这将启动一个新的、干净的 bash 实例,没有别名,也没有执行脚本中的函数。
一种可能的解决方案是将 exec bash 替换为:
bash --rcfile <(echo '. ~/.bashrc; alias pip=pip3; alias python=python3')
这将使用户(运行 docker 命令的人)处于定义了两个别名的交互式 shell 中。
注意:这个想法尚未经过测试,因为它意味着安装了很多软件包,但应该可以工作。
至少,它指向真实的你的命令有问题。
答案2
使用 时bash
,不扩展别名的原因是bash
(与其他 shell 不同)不在交互模式下时不扩展别名。
除了这个 bash 特定的偏差之外,对于别名扩展还有更多重要的影响。 POSIX 标准的下一版本将包含相关描述,解释当别名未被授予工作权限时的痛苦。
别名在解析器的词法分析器部分内展开,并且只有在 shell 已知别名并且使用相关文本片段调用词法分析器时才会展开别名后shell 已经意识到该别名。
因此,为了更好地理解别名扩展,了解 shell 是如何进行解析的也很重要。
shell 通常会解析整行输入,然后解释结果(这是识别别名命令时的情况)并继续解析下一行。
这解释了为什么在最好的情况下,别名会首先在下一行代码中展开。
Bourne Shell
对于像和 这样的传统 shell ksh
,这在某些情况下仍然不起作用。原因是传统的 shell 在开始解释结果之前,不是逐行而是逐块地解析一些输入。以下结构作为整体进行块式解析:
来自 的整个 cmd-arg
shell -c "cmd-arg"
,无论其大小如何eval
命令参数命令替换
shell 启动时执行的点脚本
.
通过buitlin 命令执行的脚本
如果在这种情况下命令在子 shell 中运行,则定义的别名永远不会生效。在其他情况下,别名在执行已解析块并解析下一个输入后生效。
想要验证这一点的人可能喜欢检查NLFLG
Bourne Shell 或 ksh 源。如果此标志用于解析器,则处理换行符的方式与;
看到 a 的方式相同。
正如您所看到的,在脚本中不使用别名会引起强烈的反响,即使当前使用的 shell 可以按预期方式工作,其他 shell 也不会表现出相同的行为。