2

I want to incorporate something like:

for f in */*; do mv "$f" "${f%/*}/foo.${f##*.}"; done  

Into my find x -exec y {} \;-style workflow.

The for loop construct & "$f" variable will likely be omitted; the loop will be substituted by standard -exec iterating behaviour, and the variable (containing the filename of the current iteration), by {}.
But it seems like these two different syntaxes are incompatible/problematic. Especially because of the conflicting braces and semicolons, etc.

voices
  • 1,272

1 Answers1

2

You can use -exec bash -c 'your code here using $0'.

In your case, this one

find */* -exec bash -c 'mv "$0" "${0%/*}/foo.${0##*.}"' {} \;

is very similar to your for.

for f in */*; do        mv "$f" "${f%/*}/foo.${f##*.}"; done  

Tip 1: You can also use -exec sh -c '.....' in case you are not using bash.

Tip 2: You can combine more than one -exec under the same find:

find ./* -type f -exec echo {} \; -exec cat {} \;

Tip 3: For simple constructs this syntax is valid:

find . -type f  ! -name "*.txt" -exec mv -v '{}' '{}'.txt \;

Tip 4: Instead of -exec you can use -ok which asks for user confirmation before running the command (useful for testing)

Above works in GNU find (I have version 4.6.0), but I am not sure if all those tips work in other find implementations.

  • You do not seem to know that {} needs to be the whole argument in order to be expanded. You cannot append or prepend text to that argument. See your tip #3 – schily Oct 20 '18 at 20:43
  • @schily My gnu find 4.6.0 works perfectly with synthax provided in tip 3. For example ths works fine : find . -maxdepth 1 -exec echo '{}' '{}'.txt \;. Maybe you are using a non gnu find. – George Vasiliou Oct 20 '18 at 22:27
  • @schily even this one works ok: find . -maxdepth 1 -exec echo newtexthere'{}'.txt \; – George Vasiliou Oct 20 '18 at 22:31
  • GNU find is not find...(it gnu find e.g. more than 20 years to implement the standard -exec + feature) other find implementations expand only separate {} args. – schily Oct 20 '18 at 22:54
  • @schily ok. Sorry, i have only gnu find. – George Vasiliou Oct 20 '18 at 23:01
  • (1a) It should be noted that, if you say -exec prog1 {} ';' -exec prog2 {} ';', then prog2 will be executed only if prog1 exits with status 0. (1b) I don’t understand why you’re even mentioning -exec echo {} \; -exec cat {} \;, as I don’t see how you’re using it to solve this problem (renaming). (1c) -exec echo {} \; is equivalent to -print. (2a) To search the current directory and all sub-directories under it, recursively, it’s best to say simply find .. … (Cont’d) – G-Man Says 'Reinstate Monica' Jun 18 '20 at 05:05
  • (Cont’d) …  When you specify a glob as the first argument (e.g., find ./* or find */*), you’re forcing the shell to expand that glob. Like ls *, this is redundant when you’re invoking a program that already knows how to enumerate directories. (2b) Unless you have set dotglob, find ./* (and find */*) will overlook top-level files and directories whose names begin with .. (2c) Whenever you use a glob, you run the risk of generating a command line that’s longer than the maximum.  … (Cont’d) – G-Man Says 'Reinstate Monica' Jun 18 '20 at 05:05
  • (Cont’d) … This is a fairly low risk now that ARG_MAX is commonly 10000 or more, sometimes as high as 100000 or 1000000, so commands like cat * are probably safe from this pitfall. But when you’re invoking a program that already knows how to enumerate directories (e.g., find, ls, chmod, cp, grep, rm, …), why take the risk? (Note that some of us can remember when ARG_MAX was 512.) – G-Man Says 'Reinstate Monica' Jun 18 '20 at 05:05