开发者

Word splitting in bash with input from a file

I'm having some trouble getting bash to play nicely with parsing words off the command line. I find it easiest to give an example, so without further ado, here we go.

This is the script test.sh:

#!/bin/bash
echo "inside test with $# arguments"

if [[ $# -eq 0 ]] then data=cat data.txt echo ./test $data ./test $data else for arg in "$@" do echo "Arg is \"$arg\"" done fi

And here is the file data.txt:

"abc 123" 1 开发者_如何学运维2 3 "how are you"

The desired output of

$ test.sh

is


inside test with 0 arguments
./test "abc 123" 1 2 3 "how are you"
inside test with 5 arguments
Arg is "abc 123"
Arg is "1"
Arg is "2"
Arg is "3"
Arg is "how are you"

But instead, I'm getting


inside test with 0 arguments
./test "abc 123" 1 2 3 "how are you"
inside test with 8 arguments
Arg is ""abc"
Arg is "123""
Arg is "1"
Arg is "2"
Arg is "3"
Arg is ""how"
Arg is "are"
Arg is "you""

The really annoying thing is that if I execute the command which is dumped from line 7 of test.sh, I do get the desired output (sans the first two lines of course).

So in essence, is there any way to get bash to parse words if given input which has been read from a file?


You can use eval for this:

eval ./test "$data"

You must be careful to know that you can trust the contents of the file when you use eval. To demonstrate why, add ; pwd at the end of the line in your data file. When you run your script, the current directory will be printed. Imagine if it was something destructive.

It might be better if you can choose a delimiter other than a space between fields in your data file. For example, if you use tabs, you could do:

while IFS=$'\t' read -r -a array
do
    for item in "${array[@]}"
    do
        something with "$item"
    done
done < data.txt

You wouldn't need to quote fields that contain spaces.

This is a correction to what I presume was a typo in your question:

data=$(cat data.txt)


No need to call the script twice.

If you find there are no arguments, you can use set to change them to something else, e.g.:

#!/bin/bash

if [ $# -eq 0 ]
then
    echo "inside test with $# arguments"
    eval set -- $(<data.txt)
fi

echo "inside test with $# arguments"
for arg in "$@"
do
    echo "Arg is \"$arg\""
done


Yes, simply replace

./test $data

with

eval ./test $data
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜