195

I have a Perl script that gives me a defined list of random numbers that correspond to the lines of a file. Next I want to extract those lines from the file using sed.

#!/bin/bash
count=$(cat last_queries.txt | wc -l)
var=$(perl test.pl test2 $count)

The variable var returns an output like: cat last_queries.txt | sed -n '12p;500p;700p'. The problem is that I can't run this last command. I tried with $var, but the output is not correct (if I run manually the command it works fine, so no problem there). What is the correct way to do this?

P.S: Sure I could do all the work in Perl, but I'm trying to learn this way, because it could help me in other situations.

0

7 Answers 7

249

You just need to do:

#!/bin/bash
count=$(cat last_queries.txt | wc -l)
$(perl test.pl test2 $count)

However, if you want to call your Perl command later, and that's why you want to assign it to a variable, then:

#!/bin/bash
count=$(cat last_queries.txt | wc -l)
var="perl test.pl test2 $count" # You need double quotes to get your $count value substituted.

...stuff...

eval $var

As per Bash's help:

~$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

    Combine ARGs into a single string, use the result as input to the shell,
    and execute the resulting commands.

    Exit Status:
    Returns exit status of command or success if command is null.
Sign up to request clarification or add additional context in comments.

3 Comments

Without eval this won't work as-expected. Try var='printf "%s\n" "first item" "second item" "third item" and compare $var to eval "$var". The first is simply wrong, for the reasons described in BashFAQ #50.
...that is to say: $(perl test.pl test2 $count) should be eval "$(perl test.pl test2 $count)" to avoid the bugs that BashFAQ #50 documents.
That works (I also had trouble with a command line containing "|", like in the question). If the output (to standard output) needs to be captured, then it can be done by someoutput=$(eval $var) instead of just eval $var. Perhaps extend the answer a little bit?
192

You are probably looking for eval $var.

1 Comment

This worked when $($cmd)) failed. It always says command not found. Thanks!
4

There're 2 basic ways of executing a string command in a shell script whether it's given as parameter or not here's.

COMMAND="ls -lah"
$(echo $COMMAND)

or

COMMAND="ls -lah"
bash -c $COMMAND

2 Comments

Thank you, this helped a bit. But do you have an idea why bash -c "cd /home/user/dev/" is not working?
@andylib93 cd only affects the current process, if you run bash -c "cd /home/user/dev/", then it will start a new bash process and run cd within that process. The child process will change directory just fine, but that will have no effect on the shell where you ran the bash command.
3
line=$((${RANDOM} % $(wc -l < /etc/passwd)))
sed -n "${line}p" /etc/passwd

just with your file instead.

In this example I used the file /etc/password, using the special variable ${RANDOM} (about which I learned here), and the sed expression you had, only difference is that I am using double quotes instead of single to allow the variable expansion.

Comments

3

In the case where you have multiple variables containing the arguments for a command you're running, and not just a single string, you should not use eval directly, as it will fail in the following case:

function echo_arguments() {
  echo "Argument 1: $1"
  echo "Argument 2: $2"
  echo "Argument 3: $3"
  echo "Argument 4: $4"
}

# Note we are passing 3 arguments to `echo_arguments`, not 4
eval echo_arguments arg1 arg2 "Some arg"

Result:

Argument 1: arg1
Argument 2: arg2
Argument 3: Some
Argument 4: arg

Note that even though "Some arg" was passed as a single argument, eval read it as two.

Instead, you can just use the string as the command itself:

# The regular bash eval works by jamming all its arguments into a string then
# evaluating the string. This function treats its arguments as individual
# arguments to be passed to the command being run.
function eval_command() {
  "$@";
}

Note the difference between the output of eval and the new eval_command function:

eval_command echo_arguments arg1 arg2 "Some arg"

Result:

Argument 1: arg1
Argument 2: arg2
Argument 3: Some arg
Argument 4:

2 Comments

To avoid the evaluation of each element after space, as in your first example with eval, wrap it with single quotes: eval 'echo_arguments arg1 arg2 "Some arg"'. Then the output will be as in your second example.
This should be the accepted answer. "$whatever" is shorter and more reliable than eval "$whatever".
0

Better ways to do it

Using a function:

# define it
myls() {
    ls -l "/tmp/test/my dir"
}

# run it
myls

Using an array:

# define the array
mycmd=(ls -l "/tmp/test/my dir")

# run the command
"${mycmd[@]}"

Comments

0
cmd="ls -atr ${HOME} | tail -1" <br/>
echo "$cmd"  <br/>
VAR_FIRST_FILE=$( eval "${cmd}" )  <br/>

or

cmd=("ls -atr ${HOME} | tail -1")  <br/>
echo "$cmd"  <br/>
VAR_FIRST_FILE=$( eval "${cmd[@]}" )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.