I noticed that find ... -exec ... {} \; or xargs -i ... {} seems to evaluate variables or subshells (like $RANDOM or $(uuidgen)) only once, even the command was executed mutiple times.
For example:
$ 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
Is there a way to get a result like below?
1543 ./foo/bar.txt
543 ./foo/bar1.txt
57224 ./foo/bar2.txt
3525 ./foo/bar3.txt
18952 ./foo/bar4.txt
>Solution :
Yes. The variable expansion is performed after the line has been accepted, but before it has been executed. This means that the command that ends up being executed is
'/usr/bin/find' '.' '-type' 'f' '-name' '*.txt' '-exec' 'echo' '28855 {}' ';'
Two basic ways around this:
-
Use another
bashthat will delay the execution:find . -type f -name \*.txt -exec bash -c 'echo "$RANDOM {}"' \; -
Use a loop:
for file in $(find . -type f -name \*.txt -print) do echo "$RANDOM $file" done -
If your files have spaces, you have to do something different to preserve them:
mapfile -d '' files < <(find . -type f -name \*.txt -print0) for file in "${files[@]}" do echo "$RANDOM $file" done