Monday, March 28, 2011

xargs doesn't recognize bash aliases

I'm trying to run the following command:

find . -iname '.#*' -print0 | xargs -0 -L 1 foobar

where "foobar" is an alias or function defined in my .bashrc file (in my case, it's a function that takes one parameter). Apparently xargs doesn't recognize these as things it can run. Is there a clever way to remedy this?

From stackoverflow
  • I usually use find like this:

    find . -iname '' -exec cmd '{}' \;
    

    '{}' will get replaced with the filename, and \; is necessary to terminate the execution chain. However, if that doesn't work with your function, you might need to run it through bash:

    find .. |sed -e "s/.*/cmd '&'/"|bash
    

    Find prints each file on a line, sed just prefixes this with your command, and then pipe it to bash for execution. Skip the |bash first to see what will happen.

    ephemient : If you're using a newish GNU find, use `-exec cmd '{}' +` instead: this acts more like xargs in that it will batch arguments together into one run instead of exec'ing a new cmd for every file found.
    Alnitak : I rather think if his function was capable of taking multiple parameters he wouldn't be supplying the -L 1 options to xargs...
    ephemient : Oh, that's what -L does? I didn't look it up, and I use -n for that purpose.
    IanGreenleaf : Sadly, these methods still don't work with aliases and functions. Probably because .bashrc only gets invoked for interactive shells. But the first one IS a neat trick.
  • This doesn't work because xargs expects to be able to exec the program given as its parameter.

    Since foobar in your case is just a bash alias or function there's no program to execute.

    Although it involves starting bash for each file returned by find, you could write a small shell script thus:

    #!/bin/bash
    . $(HOME)/.bashrc
    func $*
    

    and then pass the name of that script as the parameter to xargs

  • Since only your interactive shell knows about aliases, why not just run the alias without forking out through xargs?

    find . -iname '.#*' -print0 | while read -d $'\0' i; do foobar "$i"; done
    

    $'\0' requires shopt -s extquote, which is on by default. If you're sure that your filenames don't have newlines in them (ick, why would they?), you can simplify this to

    find . -iname '.#*' -print | while read i; do foobar "$i"; done
    

    or even just find -iname '.#*' | ..., since the default directory is . and the default action is -print.

    One more alternative:

     IFS=$'\n'; for i in `find -iname '.#*'`; do foobar "$i"; done
    

    telling Bash that words are only split on newlines (default: IFS=$' \t\n'). You should be careful with this, though; some scripts don't cope well with a changed $IFS.

  • Using Bash you may also specify the number of args being passed to your alias (or function) like so:

    alias myFuncOrAlias='echo'  # alias defined in your ~/.bashrc, ~/.profile, ...
    echo arg1 arg2 | xargs -n 1 bash -cil 'myFuncOrAlias "$1"' arg0
    echo arg1 arg2 | xargs  bash -cil 'myFuncOrAlias "$@"' arg0
    

0 comments:

Post a Comment