Home > database >  Find command output to echo without variable assignment, in one line
Find command output to echo without variable assignment, in one line

Time:12-05

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,'
  • Related