bash 脚本中的内置“read -r”在 Mac 上的行为有所不同

bash 脚本中的内置“read -r”在 Mac 上的行为有所不同

我有这个脚本:

#!/bin/bash
function main {
  while read -r file; do
    do_something "$file"
  done <<< $(find . -type f 2>/dev/null)
}
function do_something{
   echo file:$@
}

在 Linux 上,它工作正常,但在 Mac(Bash 版本 5.2)上,它将找到的所有文件视为一项,并将整个字符串不带换行符传递到do_something.如果我运行这个:

  while read -r file; do
    echo file:"$file"
  done <<< $(find . -type f 2>/dev/null)

直接在 Mac 上的 Bash 终端中也可以正常工作。那么出了什么问题呢?

答案1

在旧版本的 bash 中,<<< $param$((arith))$(cmdsubst)where<<<是从 zsh 复制的这里字符串运算符,此类未加引号的扩展会受到$IFS-splitting 的影响,并且生成的单词会与空格连接起来,并将结果存储在构成重定向目标的临时文件中。

这个问题在 4.4 中得到了修复。看CWRU/变更日志中的相应条目:

2015-09-02

redir.c
- write_here_string:不要对此处字符串文档进行分词。 bash 文档总是说这种情况不会发生,尽管 bash 多年来一直这样做,并且实现此处字符串的其他 shell 不执行任何分词。实际效果是 IFS 字符序列被折叠为空格。修复了 Clint Hepner 报告的错误 <[电子邮件受保护]>

但 macOS 仍然使用古老版本的 bash。您可能在其他地方安装了较新的 bash,但据我所知,您的 shebang 中使用的 /bin/bash 是 3.2.x,而不是 5.2。

虽然引用 the可以解决该特定问题,但这里迭代的输出$(find...)绝对是错误的方法。find

查看原因和正确的替代方案为什么循环查找的输出是不好的做法?

也就是说,如果您必须使用bash循环(也可以在 中使用zsh):

while IFS= read -rd '' -u3 file; do
  something with "$file"
done 3< <(find . -type f -print0 2> /dev/null)

(进程替换,,,,,-r所有从ksh复制的都是在-u2.05b-d之前或在与2.05b相同的版本中引入的<<<,所以应该在macos中可用/bin/bash

由于 macos 已经预装了 zsh,你也可以切换到 zsh,然后编写:

for file (**/*(ND.)) something with $file

也可以看看:

相关内容