Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle "--" in the shell script arguments?

Tags:

bash

shell

sh

zsh

This question has 3 parts, and each alone is easy, but combined together is not trivial (at least for me) :)

Need write a script what should take as its arguments:

  1. one name of another command
  2. several arguments for the command
  3. list of files

Examples:

./my_script head -100 a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' *.txt

and so on.

Inside my script for some reason I need distinguish

  • what is the command
  • what are the arguments for the command
  • what are the files

so probably the most standard way write the above examples is:

./my_script head -100 -- a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' -- *.txt

Question1: Is here any better solution?

Processing in ./my_script (first attempt):

command="$1";shift
args=`echo $* | sed 's/--.*//'`
filenames=`echo $* | sed 's/.*--//'`

#... some additional processing ...

"$command" "$args" $filenames #execute the command with args and files

This solution will fail when the filenames will contain spaces and/or '--', e.g.
/some--path/to/more/idiotic file name.txt

Question2: How properly get $command its $args and $filenames for the later execution?

Question3: - how to achieve the following style of execution?

echo $filenames | $command $args #but want one filename = one line (like ls -1)

Is here nice shell solution, or need to use for example perl?

like image 346
jm666 Avatar asked May 25 '11 11:05

jm666


1 Answers

First of all, it sounds like you're trying to write a script that takes a command and a list of filenames and runs the command on each filename in turn. This can be done in one line in bash:

$ for file in a.txt b.txt ./xxx/*.txt;do head -100 "$file";done
$ for file in *.txt; do sed -n 's/xxx/aaa/' "$file";done

However, maybe I've misinterpreted your intent so let me answer your questions individually.

Instead of using "--" (which already has a different meaning), the following syntax feels more natural to me:

./my_script -c "head -100" a.txt b.txt ./xxx/*.txt
./my_script -c "sed -n 's/xxx/aaa/'" *.txt

To extract the arguments in bash, use getopts:

SCRIPT=$0

while getopts "c:" opt; do
    case $opt in
        c)
            command=$OPTARG
            ;;
    esac
done

shift $((OPTIND-1))

if [ -z "$command" ] || [ -z "$*" ]; then
    echo "Usage: $SCRIPT -c <command> file [file..]"
    exit
fi

If you want to run a command for each of the remaining arguments, it would look like this:

for target in "$@";do
     eval $command \"$target\"
done

If you want to read the filenames from STDIN, it would look more like this:

while read target; do
     eval $command \"$target\"
done
like image 127
Tom Shaw Avatar answered Sep 17 '22 16:09

Tom Shaw