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
精彩评论