Bash:给定一个由换行符分隔的参数列表,如何将它们全部传递给另一个进程?

Bash: given a list of arguments separated by newline, how to pass all of them to another process?

一些背景故事,因为也许我的方法从根本上是错误的:

目前我正在尝试制作一个预提交脚本来检查更改了哪些文件并将它们传递给 linter。这是我目前拥有的:

STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep -E "\.js$")
echo "${STAGED}" | xargs -I {} eslint {}

这确实有效,但它有一个缺陷:它为每个文件启动一个新进程,正如您可以想象的那样,在给定多个文件时延迟明显很大(是的,在整个文件夹上启动这个进程手动不需要在单个文件上启动它 20 次以上)。

我尝试将 -n $(echo ${STAGED} | wc -l) 传递给 xargs,但没有任何改变。

我也试过这样做:

eslint $(echo ${STAGED} | sed -E "s/^(.*)$/\"\"/g" | tr "\n" " ")

引号是为了确保正确处理带有空格的文件。但这根本不起作用。

我尝试的另一件事是改变 IFS:

OLD_IFS="${IFS}"
IFS=$'\n'
eslint "${STAGED}"
IFS="${OLD_IFS}"

那对我也完全不起作用。

这个问题有好的解决办法吗?

如果您在使用 GNU grep 的平台上,请考虑:

git diff --cached --name-only --diff-filter=ACM -z |
  grep -zZ '[.]js$' |
  xargs -0 eslint

...在非 GNU 平台上,这可能是:

git diff --cached --name-only --diff-filter=ACM -z |
  while IFS= read -r -d '' name; do [[ $name = *.js ]] && printf '%s[=11=]' "$name"; done |
  xargs -0 eslint

这里最重要的变化是不使用xargs -I,这意味着-n 1

使用 git diff -zgrep -zZxargs -0 用 NUL 字节分隔文件名,这意味着即使是带有换行文字的名称也会被正确处理。 (git 可能不允许这些,但它们在整个 UNIX 系统上通常是允许的,因此养成安全处理它们的习惯是很好的卫生习惯)。


另一种方法是将命令的结果读入数组。假设 bash 的一个足够新的 (4.4) 版本,并且您的文件列表不大于单个命令行(需要 xargs):

readarray -d '' changed_files < <(git diff --cached --name-only --diff-filter=ACM -z)
eslint "${changed_files[@]}"