If I just use basename {} .txt, it will work:

find . -iname "*.txt" -exec basename {} .txt \;

It will just print xxx instead of ./xxx.txt

if I want use $(basename {} .txt) in the -exec option, it will fail:

find . -iname "*.txt" -exec echo "$(basename {} .txt)" \;

It will just print ./xxx.txt

How can I solve this problem? I hope I can use $(basename {} .txt) as parameters for other cmd. Do I have to do sh -c or pipe -exec basename {} \; with xargs?

  • 2
    Yes, you'll have to use sh or pipe it out. Process substitution takes place before find starts. – muru Dec 25 '14 at 6:03
  • 1
    Is there any more elegant way to do this? – Wang Dec 26 '14 at 3:03
  • If other reading this don't need the suffix-stripping capabilities of basename, you can use -execdir instead of -exec as that holds the current file (not path) in {} (source) – Sam May 18 at 15:29


find -iname "*.txt" -exec sh -c 'for f do basename -- "$f" .txt;done' sh {} +

Your first command failed, because $(...) run in subshell, which treat {} as literal. so basename {} .txt return {}, your find became:

find . -iname "*.txt" -exec echo {} \;

which print file name matched.

| improve this answer | |
  • I think you're missing a path on that proposed solution. Perhaps it should start find . -iname. Thanks! – neillb Feb 29 '16 at 0:56

Instead, there is another way by using bash pipelines and xargs command:

    find . -iname '*.txt' | xargs -L1 -I{} basename "{}"

In the xargs command above:

The -L1 argument means executing the output of the find command line by line.

The -I{} argument is intended to wrap the output of the find command with double quotes to avoid bash word splitting (when filenames contain whitespace).

| improve this answer | |

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.