简体   繁体   中英

pass a command as an argument to bash script

How do I pass a command as an argument to a bash script? In the following script, I attempted to do that, but it's not working!

#! /bin/sh

if [ $# -ne 2 ]
then
    echo "Usage: $0 <dir> <command to execute>"
    exit 1;
fi;

while read line
do
    $($2) $line
done < $(ls $1);

echo "All Done"

A sample usage of this script would be

./myscript thisDir echo

Executing the call above ought to echo the name of all files in the thisDir directory.

your command "echo" command is "hidden" inside a sub-shell from its argments in $line.

I think I understand what your attempting in with $($2) , but its probably overkill, unless this isn't the whole story, so

 while read line ; do
    $2 $line
 done < $(ls $1)

should work for your example with thisDir echo . If you really need the cmd-substitution and the subshell, then put you arguments so they can see each other:

   $($2 $line)

And as DS mentions, you might need eval before either of these.

IHTH

you could try: (in your codes)

echo "$2 $line"|sh

or the eval :

eval "$2 $line"

First big problem: $($2) $line executes $2 by itself as a command, then tries to run its output (if any) as another command with $line as an argument to it. You just want $2 $line .

Second big problem: while read ... done < $(ls $1) doesn't read from the list of filenames, it tries to the contents of a file specified by the output of ls -- this will fail in any number of ways depending on the exact circumstances. Process substitution ( while read ... done < <(ls $1) ) would do more-or-less what you want, but it's a bash-only feature (ie you must start the script with #!/bin/bash , not #!/bin/sh ). And anyway it's a bad idea to parse ls , you should almost always just use a shell glob ( * ) instead.

The script also has some other potential issues with spaces in filenames (using $line without double-quotes around it, etc), and weird stylistic oddities (you don't need ; at the end of a line in shell). Here's my stab at a rewrite:

#! /bin/sh

if [ $# -ne 2 ]; then
    echo "Usage: $0 <dir> <command to execute>"
    exit 1
fi

for file in "$1"/*; do
    $2 "$file"
done

echo "All done"

Note that I didn't put double-quotes around $2 . This allows you to specify multiword commands (eg ./myscript thisDir "cat -v" would be interpreted as running the cat command with the -v option, rather than trying to run a command named "cat -v" ). It would actually be a bit more flexible to take all arguments after the first one as the command and its argument, allowing you to do eg ./myscript thisDir cat -v , ./myscript thisDir grep -m1 "pattern with spaces" , etc:

#! /bin/sh

if [ $# -lt 2 ]; then
    echo "Usage: $0 <dir> <command to execute> [command options]"
    exit 1
fi

dir="$1"
shift

for file in "$dir"/*; do
    "$@" "$file"
done

echo "All done"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM