1

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,252
  • 3
  • 15
  • 30

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.

George Vasiliou
  • 7,803
  • 3
  • 18
  • 42
  • 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](https://www.in-ulm.de/~mascheck/various/argmax#results), 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](https://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/man/man2/exec.2).) – G-Man Says 'Reinstate Monica' Jun 18 '20 at 05:05