2

I want to list a bunch of filenames via find, pipe them through a utility (let's call this util) which outputs a new name for each input name, and then rename each file from the old name to the new.

The most basic solution would be this:

find . -print0 | while IFS= read -d '' -r old_name; do
    new_name="$(echo "$file" | util)"
    mv "$old_name" "$new_name"
done

The problem with this approach is that util is too slow to fire up for each filename separately. So the solution is to launch util only once and pipe all the filenames through this single process:

find . -print0 >old_names
util <old_names >new_names

exec {old_fd}<old_names
exec {new_fd}<new_names

while IFS= read -d '' -r old_name <&$old_fd &&
      IFS= read -d '' -r new_name <&$new_fd; do
    mv "$old_name" "$new_name"
done

This will launch util only once, on the other hand it's no longer a pipeline: we have to list all the files into a tmp file, run util on this tmp file to get another tmp file, and only then do we actually start the renaming...

I've tried the following to do it in a pipelined way:

mkfifo old_names new_names
find . -print0 | tee old_names | util >new_names &

exec {old_fd}<old_names
exec {new_fd}<new_names

while IFS= read -d '' -r old_name <&$old_fd &&
      IFS= read -d '' -r new_name <&$new_fd; do
    mv "$old_name" "$new_name"
done

Unfortunately this can deadlock depending on how util does input/output buffering...

So my question is: what's the proper way of doing this in bash?

  • Depending on the complexity of the transform (util), you may be able to use rename (the one by Larry Wall). – ctrl-alt-delor Feb 2 at 20:15
  • To avoid deadlock, don't over complicate it. – ctrl-alt-delor Feb 2 at 20:21
1

A solution without joining pipes:

find . -print > infiles
cat infiles | util > outfiles
parallel mv ::::+ infiles outfiles

Pro: Extremely simple. Con: renaming only starts after util is done. 2 tempfiles.

find . -print > infiles
cat infiles | util | parallel -j1 mv ::::+ infiles -

Pro: Renaming will start when util starts giving names. Con: 1 tempfile.

find . -print | util | parallel -j1 mv ::::+ <(find . -print) -

Pro: Renaming will start when util starts giving names. Con: requires no change to the current directory (otherwise the two finds may not give the same.)

0

Depending on the complexity of the transform (util), you may be able to use rename (the one by Larry Wall).

Alternatively

Arrange util to output name pairs: old-name new-name, then

find . -print0 | util | xargs -n2 mv -T

Alternatively

Try something like this.

function a() { 
    echo -n "$1 "
    echo "$1" | sed -e s/a/b/g
}

… | while read arg; do
a "$arg"
done
  • 1. Thanks, but unfortunately in the real script it's not that simple as simply mv-ing the file to its new location, so rename wouldn't cut it. 2. I have unfortunately no control over the output format of util. – Tamás Zahola Feb 2 at 20:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.