此代码在 Linux 上运行良好,但在 Mac OS 上运行不佳:
#!/usr/bin/env bash
foo=$(cat <<EOF
"\[^"\]+"
EOF
)
printf "%s" "$foo"
在 Mac 上失败
./test.sh: line 6: unexpected EOF while looking for matching `"'
./test.sh: line 7: syntax error: unexpected end of file
如果我这样做cat <<EOF
而不是foo=$(cat <<EOF
,它工作得很好。有没有一种可移植的方法可以将 heredocs (或多行字符串) 放入变量中,而无需使用文件作为中间体?
编辑:我想使用 heredoc,因为我有一个带有 和 的多行字符串"
。'
我的实际示例如下:
EXPECTED_ERROR=$(cat <<EOF
Set
: Type
File "/tmp/tmp[A-Za-z0-9_/]\+\.v", line 1\(0\|1\), characters 0-15:
Error: The command has not failed\s\?!
.\?Does this output display the correct error? \[(y)es/(n)o\]\s
I think the error is 'Error: The command has not failed\s\?!
.\?'\.
The corresponding regular expression is 'File "\[^"\]+", line (\[0-9\]+), characters \[0-9-\]+:\\\\n(Error:\\\\s+The\\\\s+command\\\\s+has\\\\s+not\\\\s+failed.*
EOF
)
这个特定的 bash 错误在 4.0 中得到了修复,该版本发布于 15 年前,但 macos 仍然发布 3.2.x 版本。由于 Apple 显然不会升级他们的版本
bash
(sh
据我所知,他们的版本仍然基于bash
),我建议您向他们报告这个错误,以便他们将修复程序移植到他们的版本中。它似乎会影响命令替换中使用的 here-documents,因此您可以使用以下命令将 here-doc 移出命令替换:
或者使用辅助函数:
或者更有用的:
(上面也使用
'EOF'
而不是EOF
因为后者进行扩展并意味着对反斜杠的额外处理)。或者通过使用以下方式延迟此处文档的解释
eval
:虽然我怀疑您首先使用此处的文档是为了避免担心引用嵌套,但这
eval '...'
可能无济于事。术语说明:
这里,问题仅仅与 bash 的版本有关,您会在 2009 年之前的任何包含 bash 的系统中遇到同样的错误。
我不清楚为什么需要 heredoc:
应该在 Linux 和 macOS 上的 bash 中运行。
如果需要多行字符串,请在多行中用引号声明它:
提供这个答案是为了提供完整性,因为所有答案都包含正确答案。这只是为原帖者添加了另一个“正确”选项。
历史回顾
Darwin基于 BSD,MacOS 的核心在 Darwin8 之前都是免费和开源的。Darwin8 仍然可用,将为您提供非常基本的 OSX 10.4。该安装中将没有 GUI。在 Darwin8 之后,Apple 选择让操作系统闭源。这个 Darwin8 还包含 Apple BASH 的最后一个 GPL 版本 - 3.2.x,这就是为什么没有用户会看到高于 3.2.x 的原生 Apple BASH 版本
Apple 使用NeXTStep和 Darwin8中的图形对象构成了自 10.4 以来所有现代 OSX 的基础,并且源代码仍然是闭源的,部分代码在Apple 的开发者网站上共享。Darwin(不附加 Apple)仍然可以使用,并且今天在PureDarwin等项目中可以看到。请注意,这里仍然没有原生的 NeXTStep 类型 GUI 或任何原生 GUI,但系统将使用 MacPorts 运行像 XFCE 这样的桌面。下面讨论的 Homebrew 是一个软件包管理器,主要用于用 Linux 版本替换 Apple/Darwin 版本的程序和库(不删除 Apple 提供的版本)。MacPorts 的工作原理大致相同,但不仅包含 Linux 二进制文件,还包含图形应用程序。
问题
抛开这段小历史不谈,多个网站都围绕着这样一个事实:如果 BASH 的更新版本超过 Darwin8 中的版本,Apple 将违反 GPLv3 许可证。报告错误可能没有用,因为需要将修复程序反向移植才能继续避免违规。如果修复程序包含在 BASH4 中,正如 Stephane 的回答所述,Apple 现在就陷入了“先有鸡还是先有蛋”的困境。为了将 BASH 升级到高于 3.2 的版本,所有源代码(包括闭源 NeXTStep 代码)都需要重新发布,这将满足 GPL 许可证。Apple 不想让公众看到该代码。请参阅下面的讽刺部分,了解我个人对此的看法。
之前链接的搜索结果中的最佳答案包含以下内容:
致力于提供 Zsh Shell只是一种花哨的说法,我们有很多律师告诉我们违反 GPL 是不好的,所以让我们找到一个可行的替代方案,其许可证不是 GPLv3。ZSH 恰好就是那个合适的替代方案。
我的解决方案
Apple 系统用户说只需切换终端解释器就可以了,但作为两个系统(PC 和 Mac)的用户,我发现安装 Homebrew 版本的 BASH 可以修复 Apple 选择的设计决策。
这样做现在可以使几乎所有 Linux 发行版和 Mac 之间的 BASH 脚本兼容,并且如果需要,还可以为用户提供一种继续使用他们习惯的 BASH shell 的方法。
退税
如果用户需要的是可移植性(从一台 Mac 转移到另一台 Mac),而不是兼容性,则不建议使用上述方法。在可移植性方面,我们有两个选择:
在我拥有的最旧的 bash 中(未达到 3.2)可以使用一种非命令替换方法: