Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

bash variable substitution within command substitution

Tags:

bash

shell

I want to do something like the following:

#!/bin/bash

cmd="find . -name '*.sh'"
echo $($cmd)

What I expect is that it will show all the shell script files in the current directory, but nothing happened.

I know that I can solve the problem with eval according to this post

#!/bin/bash

cmd="find . -name '*.sh'"
eval $cmd

So my question is why command substitution doesn't work here and what's the difference between $(...) and eval in terms of the question?

like image 744
Wei Li Avatar asked Nov 08 '12 13:11

Wei Li


People also ask

What substitutes value of variables and commands?

This feature is called command substitution and it requires the command to use the standard output (stream).

What is substitution explain command and variable substitution with example?

Command substitution is generally used to assign the output of a command to a variable. Each of the following examples demonstrates the command substitution − #!/bin/sh DATE=`date` echo "Date is $DATE" USERS=`who | wc -l` echo "Logged in user are $USERS" UP=`date ; uptime` echo "Uptime is $UP"

What does $() mean bash?

Example of command substitution using $() in Linux: Again, $() is a command substitution which means that it “reassigns the output of a command or even multiple commands; it literally plugs the command output into another context” (Source).


2 Answers

Command substitution works here. Just you have wrong quoting. Your script find only one file name! This one with single quotes and asteriks in it:

'*.sh'

You can create such not usual file by this command and test it:

touch "'*.sh'"

Quoting in bash is different than in other programming languages. Check out details in this answer.

What you need is this quoting:

cmd="find . -name *.sh"
echo $($cmd)
like image 104
Tomek Wyderka Avatar answered Oct 16 '22 11:10

Tomek Wyderka


Since you are already including the patter *.sh inside double quotes, there's no need for the single quotes to protect the pattern, and as a result the single quotes are part of the pattern.

You can try using an array to keep *.sh quoted until it is passed to the command substitution:

cmd=(find . -name '*.sh')
echo $("${cmd[@]}")

I don't know if there is a simple way to convert your original string to an array without the pattern being expanded.

Update: This isn't too bad, but it's probably better to just create the array directly if you can.

cmd="find . -name *.sh"
set -f
cmd=($cmd)
set +f
echo $("${cmd[@]}")
like image 23
chepner Avatar answered Oct 16 '22 11:10

chepner