4

I'm trying to write a script to unify two separate commands I run by hand into a single bash script I can then cron.

The first command is a simple find on files with a certain name and size

find /some/path -type f -name file.pl -size +10M

This will produce several matching files and their full path. I then copy these paths by hand into a for loop as arguments to the next script.

for path in /some/path/1/file.pl /some/path/2/file.pl /some/path/3/file.pl ; do perl /my/script.pl $path ; done

Seems like it should be easy to get this into a single shell script but finding it a struggle.

  • 2
    Note $(command) and `command` are equivalent, but I think $(command) is more readable. – ctrl-alt-delor Aug 14 '14 at 11:23
5

That's what the -exec predicate is for:

find /some/path -type f -name file.pl -size +10M -exec perl /my/script.pl {} \;

If you do want to have your shell run the commands based on the output of find, then that will have to be bash/zsh specific if you want to be reliable as in:

  • zsh:

    IFS=$'\0'
    for f ($(find /some/path -type f -name file.pl -size +10M -print0)) {
      /my/script.pl $f
    }
    

    though in zsh, you can simply do:

    for f (./**/file.pl(.LM+10)) /my/script.pl $f
    
  • bash/zsh

    while IFS= read -rd '' -u3 file; do
      /my/script.pl "$file"
    done 3< <(find /some/path -type f -name file.pl -size +10M -print0)
    

Whatever you do, in bash or other POSIX shells, avoid:

for file in $(find...)

Or at least make it less bad by fixing the field separator to newline and disable globbing:

IFS='
'; set -f; for file in $(find...)

(which will still fail for file paths that contain newline characters).

3

If you are using the GNU tools, the following should also work:

find /some/path -type f -name file.pl -size +10M -print0 | xargs -0 -n 1 -r perl /my/script.pl

Explanation:

  • The option -print0 causes GNU find to separate the file names with \0 bytes. Since \0 bytes cannot be part of the file name, this uniquely separates the file names.
  • The option -0 tells GNU xargs to read standard input as \0-separated list of file names.
  • The option -n 1 enforces that no more than one file name is passed to your script (if your script can handle a complete list of files as argument, just omit that).
  • Finally, -r is another GNU extension which prevents your program from being run if no file names are supplied.
  • That has the side effect of affecting perl's stdin. I can't see any good reason of preferring that over the standard -exec ... variant. – Stéphane Chazelas Aug 15 '14 at 14:32
  • @StéphaneChazelas: For this specific case, it probably has no advantage. But in a more general case, you might want to modify the list of file names before passing it to the commands. With the pipe form, you can do that by inserting an additional command in the pipeline. For example, if for some reason you want to process the files in sorted order, you can just put a sort -z in the pipeline between find and xargs. Or select a subset using regexps by inserting a grep -z. – celtschk Aug 15 '14 at 16:06
-3

This should do it

for path in `find /some/path -type f -name file.pl -size +10M`; do perl /my/script.pl $path ;done

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.