Bash 中是否可以将命令的输出作为实际的命令行来运行

ada*_*ncy 12 linux bash readline

让我用一个例子来解释一下:

我有一行在我的中声明了别名~/.bashrc

> grep lsdir ~/.bashrc

alias lsdir='ls -d */'
Run Code Online (Sandbox Code Playgroud)

我刚刚将此行添加到我的 bashrc 中,因此别名尚未添加到我当前的会话环境中。由于某些配置原因,我也不想重新获取所有 bashrc 的源代码,这就是为什么我想简单地单独运行这个 grep 行。

出于好奇和教育的目的,我尝试着不用手动写下来,但没有成功。

我试过这个:

> $(grep lsdir ~/.bashrc)
bash: alias: -d: not found
bash: alias: */': not found
Run Code Online (Sandbox Code Playgroud)

或这个:

> "$(grep lsdir ~/.bashrc)"
bash: alias lsdir='ls -d */': No such file or directory
Run Code Online (Sandbox Code Playgroud)

甚至这个:

> $("grep lsdir ~/.bashrc")
bash: grep lsdir ~/.bashrc: No such file or directory
Run Code Online (Sandbox Code Playgroud)

但没有一个奏效。有什么办法可以实现这一点吗?

jes*_*e_b 23

您可以使用Process Substitution来仅获取匹配的行:

source <(grep lsdir ~/.bashrc)
Run Code Online (Sandbox Code Playgroud)


ada*_*ncy 13

我找到了答案:

关键是使用内置的eval. 这样做将实现所需的行为(更准确地说,在我们登录的实际 shell 中运行命令,而不是在子 shell 中):

> eval "$(grep lsdir ~/.bashrc)"
> type lsdir
lsdir is aliased to `ls -d */'
Run Code Online (Sandbox Code Playgroud)

  • 您应该双引号命令替换(即`eval "$(grep lsdir ~/.bashrc)"`)——没有它,命令在`eval`ed之前会经历分词和通配符扩展,这实际上可以有奇怪的效果。 (6认同)
  • 尽管分词在这里有点奇怪,因为“eval”在运行命令之前确实用空格连接了它获得的任何参数,所以看起来它可以工作。但它会因为像“eval $(printf 'echo foo\necho bar\n')”这样的东西而崩溃,其中换行符变成了一个空格,整个事情作为一个命令运行,而不是像如果中间换行符完好无损。 (2认同)

Dmi*_*yev 10

虽然现有的答案提供了可行的解决方案,但它们都没有解释为什么原始命令$(grep lsdir ~/.bashrc)不起作用。

事实上,shell 在替换期间对命令执行了有限的工作:您得到了单词分割,但没有引号处理:

pi@raspberrypi:~ $ echo 'a'
a
pi@raspberrypi:~ $ echo "echo 'a'"
echo 'a'
pi@raspberrypi:~ $ $(echo "echo 'a'")
'a'
Run Code Online (Sandbox Code Playgroud)

正如您在最后一行中看到的,在命令 ( ) 和参数 ( )echo 'a'中正确分割,但参数本身没有经过引号处理,因此引号保持不变。顺便说一句,当您尝试将 shell 命令存储在变量中时,几乎会发生同样的事情。echo'a'

在你的例子中,alias lsdir='ls -d */'被分成命令,alias它的参数,以及两个意想不到的lsdir='ls附加参数(-d*/') 。alias

允许重新运行所有命令处理(包括引号处理)的一种自然解决方案是使用eval, 并且答案之一已经提到。