在 Mac OS X 10.8 上找不到 envsubst 命令

112

当我尝试运行包含envsubst命令的脚本时,会出现以下错误。在网上搜索,这似乎是一个标准的bash命令,所以我不知道需要安装什么才能使其工作。


4
envsubst 包含在 gettext 软件包中。您可以自行编译安装。请参见 https://dev59.com/RGUp5IYBdhLWcg3w669C - ymonad
我把它作为答案发布了。 - ymonad
6个回答

282
brew install gettext
brew link --force gettext 

这将在OS X上启用envsubst,并强制正确地链接它。 它需要安装homebrew。


1
链接 /usr/local/Cellar/gettext/0.19.8.1... 创建了194个符号链接。... 真是令人困惑,为什么会有这么多符号链接? - Alexander Mills
1
@AlexanderMills 是的,gettext 包含了大量的内容;似乎只是为了安装 envsubst 而过度杀伤力了,但这是最快和最简单的方法。 - cobberboy
似乎他们正在为每个文件创建符号链接,而不是仅为一个文件夹创建符号链接。 - Alexander Mills
1
@AlexanderMills 是的,这就是Homebrew的工作原理:它将每个可执行文件符号链接到/usr/bin中。没有一个很好的方法可以将单个目录符号链接到那里,并使每个可执行文件在shell PATH上可用。 - Peeja

158

编辑:@cobberboy的答案更正确。请给他点赞。

brew install gettext
brew link --force gettext 

以下是我的旧答案:

envsubst包含在gettext软件包中。

因此,您可以使用标准的构建工具(如make)或使用homebrew自己编译它。

但是,在安装gettext时,在MacOS上似乎存在一些问题。 有关详细信息,请参见以下网址:如何在MacOS X上安装gettext


8
虽然envsubst是gettext的一部分(通过homebrew安装),但默认情况下它没有被链接。我猜测这是因为gettext是一个专用配方。你可以告诉homebrew去链接这个专用配方,但这可能会产生意外的副作用。一个不太侵入的方法是添加alias envsubst='/usr/local/Cellar/gettext/0.19.6/bin/envsubst'到你的.profile文件中(或者等效的文件)。当然,你也可能安装了另一个版本的gettext。你可以通过运行brew info gettext来了解更多信息。 - trkoch
2
@trkoch 你可能想要为 /usr/local/opt/gettext/bin/envsubst 设置别名,这样可以在升级后保留。 - Christoph Hösler
1
虽然这是被接受的答案,请向下滚动到@cobberboy的答案,因为你可能需要强制链接。 - Big Money
我想知道为什么我突然得到了更多的赞。感谢您的慷慨 @ymonad - cobberboy
1
链接 /usr/local/Cellar/gettext/0.19.8.1...创建了194个符号链接...什么鬼,194个符号链接? - Alexander Mills

23

为了消除潜在的混淆:

  • envsubst 是一个外部可执行文件,因此不是 Bash 的一部分;外部可执行文件是与平台相关的,无论是哪个平台,可用的外部可执行文件都不同,并且它们的特定行为和支持的特定选项也不同(虽然希望基于 POSIX 规范有一个共同的子集)
  • 直接内置在 bash 中的命令称为内置命令,只能依靠它们所有平台上存在

测试给定命令是否为内置命令,请使用

type <cmdName>

针对这种情况,在 macOS 10.13 上运行type envsubst会返回-bash: type: envsubst: not found,由此可推断出:

  • envsubst不是内置命令
  • envsubst不在您系统的$PATH中(因此可能不存在于您的系统上)

(相比之下,在Ubuntu 12.04系统上运行相同的命令将返回envsubst is hashed (/usr/bin/envsubst),这表明该实用程序存在且位于哪个位置。)


一种临时替代envsubst的方法是使用eval,但通常的警告适用:仅对您控制或信任其内容的字符串使用eval

假设有一个包含未展开变量引用文本的sample.txt文件,例如:

cat > sample.txt <<'EOF'
Honey, I'm $USER
and I'm $HOME.
EOF

等同于:

envsubst < sample.txt

是:

eval "echo \"$(sed 's/"/\\"/g' sample.txt)\""

不过,有一个关键的区别:

  • envsubst 仅展开环境变量引用
  • eval 会展开shell变量引用以及嵌入的命令替换,这就是为什么使用eval存在安全问题的原因。

我喜欢“eval echo”的想法,因为我信任源代码,但它并不相同。 “envsubst < .env.example”不能仅仅是“eval echo < .env.example”。 - iRaS

4

我现在在我的bash脚本中使用这个,需要envsubst:

if ! which envsubst > /dev/null 2>&1; then
    envsubst() {
        while read line; do
            line=$( echo $line | sed 's/"/\\"/g' )
            eval echo $line
        done
    }
fi

您可以将其用作 envsubst 命令-当然它并不完全具备功能或其他特性:
envsubst <<<'Honey, I am $HOME.'
envsubst < input > output 2> corrupt

这似乎会去除所有前导和尾随的空格。 - sudo
正如所说,这只是一个不完整的解决方法。你可以尝试在$line周围添加",但我还没有尝试过。 - iRaS
这种方法似乎可以正确处理空格并且速度更快:sed 's/"/\\"/g' | eval "echo \"$(cat -)\"" - Evan Owen
@EvanOwen 不错。这会替换整个 while 循环吗?看起来是这样的...那我想更新一下答案。 - iRaS
@EvanOwen 不错。这是不是替换了整个 while 循环?看起来是这样的...那我想要更新答案了。 - undefined

3
如果你不想麻烦安装homebrew和gettext,并且看不懂perl, 一个简单的python脚本也可以解决问题:
envsubst() {
  python -c 'import os,sys;[sys.stdout.write(os.path.expandvars(l)) for l in sys.stdin]'
}

相比于使用eval解决方案,这种方法的优势在于它只处理变量替换,不会像eval一样执行任意脚本。

$ cat > sample.txt <<'EOF'
Honey, I'm $USER
and I'm $HOME.
Danger: $( echo 'eval can do evil here, but python expandvars rocks' )
EOF

$ envsubst < sample.txt

Honey, I'm mattmc3
and I'm /Users/mattmc3.
Danger: $( echo 'eval can do evil here, but python expandvars rocks' )

3

如果您不想麻烦安装homebrew和gettext,则一个单行的perl可执行文件就可以实现:

#!/usr/bin/perl -p
$_ =~ s/\Q${$1||$2}/$ENV{$1?$2:$4}/ while $_ =~ /(\$\{([^}]+)})|(\$(\w+))/g;

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