Bash:给定以换行符分隔的参数列表,如何将所有参数传递给另一个进程?

时间:2018-08-24 03:55:38

标签: bash command-line xargs

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

当前,我正在尝试制作一个预提交脚本,该脚本检查哪些文件已更改并将其传递给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/^(.*)$/\"\1\"/g" | tr "\n" " ")

那里有报价以确保正确处理带有空格的文件。但这根本不起作用。

我尝试的另一件事是更改IFS

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

那对我也不起作用。

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

1 个答案:

答案 0 :(得分:1)

如果您使用的是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\0' "$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[@]}"