Reputation: 23547
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.
Upvotes: 12
Views: 27483
Reputation: 126048
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 (i.e. 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 (e.g. ./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 e.g. ./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"
Upvotes: 6
Reputation: 37318
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 D.S. mentions, you might need eval
before either of these.
IHTH
Upvotes: 2
Reputation: 195239
you could try: (in your codes)
echo "$2 $line"|sh
or the eval
:
eval "$2 $line"
Upvotes: 2