5

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?

3
  • 3
    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 '20 at 15:29
7

Try:

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.

1
  • 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
3

You can still use $() for command substitution if you use sh -c and single quotes:

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

The single quotes prevent the main shell from executing the sub-command inside $() so it can instead be executed by sh -c after the find command has replaced {} with the file name. See this answer on Stack Overflow for a more thorough explanation.

Note that you can also add double quotes inside the $() to handle spaces in file names:

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

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).

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.