bash: interpret string variable as file name/path

2019-03-09 15:08发布

My bash script receives a filename (or relative path) as a string, but must then read from that file. I can only read from a filename if I declare it as a literal directly in the script (without quotes)...which is impossible for arguments since they are implicitly strings to begin with. Observe:

a="~/test.txt"
#Look for it
if [[ -a $a ]] ; then
    echo "A Found it"
else
    echo "A Error"
fi
#Try to use it
while read line; do
    echo $line
done < $a

b='~/test.txt'
#Look for it
if [[ -a $b ]] ; then
    echo "B Found it"
else
    echo "B Error"
fi
#Try to use it
while read line; do
    echo $line
done < $b

c=~/test.txt
#Look for it
if [[ -a $c ]] ; then
    echo "C Found it"
else
    echo "C Error"
fi
#Try to use it
while read line; do
    echo $line
done < $c

YIELDS:

A Error
./test.sh: line 10: ~/test.txt: No such file or directory
B Error
./test: line 12: ~/test.txt: No such file or directory
C Found it
Hello

As stated above, I can't pass a command line argument to the routines above since I get the same behavior that I get on the quoted strings.

1条回答
女痞
2楼-- · 2019-03-09 15:39

This is part of the rules of ~-expansion. It is clearly stated in the Bash manual that this expansion is not performed when the ~ is quoted.

Workaround 1

Don't quote the ~.

file=~/path/to/file

If you need to quote the rest of the filename:

file=~/"path with spaces/to/file"

(This is perfectly legal in a garden-variety shell.)

Workaround 2

Use $HOME instead of ~.

file="$HOME/path/to/file"

BTW: Shell variable types

You seem to be a little confused about the types of shell variables.

Everything is a string.

Repeat until it sinks in: Everything is a string. (Except integers, but they're mostly hacks on top of strings AFAIK. And arrays, but they're arrays of strings.)

This is a shell string: "foo". So is "42". So is 42. So is foo. If you don't need to quote things, it's reasonable not to; who wants to type "ls" "-la" "some/dir"?

查看更多
登录 后发表回答