I'm trying to write one line of code that finds all .sh
files in the current directory and its subdirectories, and print them without the .sh
extension (preferably without the path too).
I think I got the find
command down. I tried using the output of
find . -type f -iname "*.sh" -print
as input for echo
, and formatting it along these lines
echo "${find_output%.sh}"
However, I cannot get it to work in one line, without variable assigment. I got inspiration from this answer on stackoverflow https://stackoverflow.com/a/18639136/15124805 to use this line:
echo "${$( find . -type f -iname "*.sh" -print)%.sh}"
But I get this error:
ash: ${$( find . -type f -iname "*.sh" -print)%.sh}: bad substitution
I also tried using xargs
find . -type f -iname "*.sh" -print |"${xargs%.sh}" echo
But I get a "command not found error" -probably I didn't use xargs
correctly, but I'm not sure how I could improve this or if it's the right way to go.
How can I make this work?
CodePudding user response:
That's the classic useless use of echo
. You simply want
find . -type f -iname "*.sh" -exec basename {} .sh \;
If you have GNU find
, you can also do this with -printf
.
However, basename
only matches .sh
literally, so if you really expect extensions with different variants of capitalization, you need a different approach.
For the record, the syntax you tried to use for xargs
would attempt to use the value of a variable named xargs
. The correct syntax would be something like
find . -type f -iname "*.sh" -print |
xargs -n 1 sh -c 'echo "${1%.[Ss][Hh]}"' _
but that's obviously rather convoluted. In some more detail, you need sh
because the parameter expansion you are trying to use is a feature of the shell, not of echo
(or xargs
, or etc).
(You can slightly optimize by using a loop:
find . -type f -iname "*.sh" -print |
xargs sh -c 'for f; do
echo "${f%.[Ss][Hh]}"
done' _
but this is still not robust for all file names; see also https://mywiki.wooledge.org/BashFAQ/020 for probably more than you realized you needed to know about this topic. If you have GNU find
and GNU xargs
, you can use find ... -print0 | xargs -r0
)
CodePudding user response:
Here's a robust and somewhat efficient way that works on any UNIX/Linux:
find . -type f -iname '*.sh' -exec sh -c '
for f in "${@##*/}"
do
printf %s\\n "${f%.[sS][hH]}"
done
' _ {}
And a less robust but faster solution:
find . -type f -iname '*.sh' |
sed 's,\(.*/\)\?\([^/]*\)\.[sS][hH]$,\2,'