Bash find 或 xargs 只对变量和子 shell 求值一次

Bash find or xargs evaluates variables and subshells only once

我注意到 find ... -exec ... {} \;xargs -i ... {} 似乎只评估变量或子 shell(如 $RANDOM$(uuidgen))一次,即使命令被执行多次。

例如:

$ find . -type f -name \*.txt -exec echo "$RANDOM {}" \;
28855 ./foo/bar.txt
28855 ./foo/bar1.txt
28855 ./foo/bar2.txt
28855 ./foo/bar3.txt
28855 ./foo/bar4.txt

$ grep -lr SOME_TEXT --include=\*.txt | xargs -i echo "$RANDOM {}"
6153 ./foo/bar.txt
6153 ./foo/bar1.txt
6153 ./foo/bar2.txt
6153 ./foo/bar3.txt
6153 ./foo/bar4.txt

有没有办法得到如下结果?

1543 ./foo/bar.txt
543 ./foo/bar1.txt
57224 ./foo/bar2.txt
3525 ./foo/bar3.txt
18952 ./foo/bar4.txt

是的。变量扩展在行被接受之后执行,但在执行之前执行。这意味着最终被执行的命令是

'/usr/bin/find' '.' '-type' 'f' '-name' '*.txt' '-exec' 'echo' '28855 {}' ';'

解决这个问题的两种基本方法:

  • 使用另一个会延迟执行的bash:

    find . -type f -name \*.txt -exec bash -c 'echo "$RANDOM {}"' \;
    
  • 使用循环:

    for file in $(find . -type f -name \*.txt -print)
    do
      echo "$RANDOM $file"
    done
    
  • 如果你的文件有空格,你必须做一些不同的事情来保留它们:

    mapfile -d '' files < <(find . -type f -name \*.txt -print0)
    for file in "${files[@]}"
    do
      echo "$RANDOM $file"
    done