I would like to evaluate a basename expression given an argument from xargs.

I tested:

find . -name '*.txt' | xargs -I f cp f DIR_OUT/copied_$(basename f)

which gives no file or directory because $(basename f) was not evaluated correctly.

I may split it two steps: copying and changing the filename, but I would like to learn how to evaluate an expression with an argument from xargs.


The $(basename f) command substitution that you use in your command would be evaluated exactly once (yielding the string f), before the xargs command is even started. This is why your code fails to copy the files to the correct names.

The "No such file or directory" error that you get is due to the f in DIR_OUT/copied_f being replaced by the pathnames of the files found by find.

A more robust way of doing the same thing,

find . -type f -name '*.txt' -exec sh -c '
    for pathname do
        cp "$pathname" "DIR_OUT/copied_${pathname##*/}"
    done' sh {} +

Or, with xargs while taking care to pass the found pathnames between find and xargs as nul-terminated strings:

find . -type f -name '*.txt' -print0 |
xargs -0 sh -c '
    for pathname do
        cp "$pathname" "DIR_OUT/copied_${pathname##*/}"
    done' sh

In both of these variations, we call a small in-line shell script that loops over the given pathnames and copies them to new names under the directory DIR_OUT. The ${pathname##*/} may be replaced by $( basename "$pathname" ).

Note that -print0 (in find) and -0 (in xargs) are non-standard, but commonly implemented.


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.