use basename in find -exec?

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.


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


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)"' \;

Tags:

Find

Basename