When sysadminning, you sometimes want to run find on a remote machine with fancy stuff like -exec. This should be easy (once you get the find syntax right), but typically, incorrect quoting gives you errors such as

find: incomplete statement or Argument for -c ends in backslash. Here's an example of how to do it with some Perl variable substitutions thrown in. Nothing Earth-shattering, but if it saves someone five minutes, it's worth posting.

# in my actual code, I had "rm" instead of "ls -l", but I figured this + would be safer in an example. system "/usr/local/bin/ssh $remote \"find $base_dir -mtime +$days -exe +c ls -l \\{} \\;\"" and warn "system: ssh: $!";

Replies are listed 'Best First'.
Re: Run system find -exec on remote machine
by Aristotle (Chancellor) on Dec 21, 2004 at 04:07 UTC

    You don't need to quote the command argument to ssh(1). Using xargs(1) as you should makes this trivial.

    $ ssh $remote find $base_dir -mtime +$days -print0 \| xargs -0 ls -l

    Makeshifts last the longest.

      Indeed. Apart from nicer syntax, xargs is much faster because it doesn't fork the command for each file. The only nuisance I've encountered with xargs is portably coping with space-riddled file names. As Aristotle points out, GNU find/xargs work well. Lacking GNU utilities, I've used this:

      find . -print0 | xargs -0 ls -l (GNU only) find . -print | sed 's/ /\\ /g' | xargs ls -l (works everywhere)

        The BSD find(1)/xargs(1) also understand the extra switches. Your other version unfortunately still breaks on embedded newlines, which are the most important reason to use the switches. Absent of a way to make find(1) separate filenames with nulls (such as on Solaris), I'd rather fall back on -exec.

        Makeshifts last the longest.

        Your suggestions (@you > 1) make sense, and I use xargs a lot on local machines; no reason to avoid it on remote ones.

        In regard your comment about xargs bunching up args and thus forking off less: sure, but sometimes that's not what you want (some commands really do need to run one file at a time). So just for the sake of completeness (I know you may already know this): the -n switch to xargs accepts a number of arguments to bunch up to every forked command. The two following lines are roughtly equivalent:

        find . -exec somecmd \{} \; find . -print0 | xargs -0 -n 1 somecmd

        BTW, some versions of xargs have a nifty concurrency switch, -P on the GNU variant. This too is sometimes very much not what you want, though :)