Reputation: 395
I'm new to bash and I'm working on script that traverses the tar.gz file archive and in each file changes a string specified to an another string. Args of script: name of archive, searched string, target word.
My problem is that when archive name contains a space (e.g. I run script with following args: > change_strings.sh "/tmp/tmp.m7xYn5EQ2y/work/data txt" a A) I have following error:
on line if [ ! -f $filename ] ; then
[: data: binary operator expected, dirname: extra operand `txt'.
Here is my code:
#!/bin/bash
filename="${1##*/}"
VAR="$1"
DIR=$(dirname ${VAR})
cd "$DIR"
if [ ! -f $filename ] ; then
echo "no such archive" >&2
exit 1
fi
if ! tar tf $filename &> /dev/null; then
echo "this is not .tar.gz archive" >&2
exit 1
fi
dir=`mktemp -dt 'test.XXXXXX'`
tar -xf $filename -C $dir #extract archive to dir
cd $dir #go to argument directory
FILES=$dir"/*"
for f in $FILES
do
sed -i "s/$2/$3/g" "$f"
done
tar -czf $filename * #create tar gz archive with files in current directory
mv -f $filename $cdir"/"$filename #move archive
rm -r $dir #remove tmp directory
Upvotes: 0
Views: 1156
Reputation: 123410
Welcome to stackoverflow!
For the convenience of current and future readers, here's a small, self contained example showing the problem:
filename="my file.txt"
if [ ! -f $filename ]
then
echo "file does not exist"
fi
Here's the output we get:
$ bash file
file: line 2: [: my: binary operator expected
And here's the output we expected to get:
file does not exist
Why are they not the same?
Here's what shellcheck has to say about it:
$ shellcheck file
In file line 2:
if [ -f $filename ]
^-- SC2086: Double quote to prevent globbing and word splitting.
and indeed, if we double quote it, we get the expected output:
$ cat file
filename="my file.txt"
if [ ! -f "$filename" ]
then
echo "file does not exist"
fi
$ bash file
file does not exist
You should be double quoting all your variables.
However, you have to take care with $FILES
because it contains a glob/wildcards that you want to expand along with potential spaces that you don't want to wordsplit on. The easiest way is to just not put it in a variable and instead write it out:
for f in "$dir"/*
do
...
Upvotes: 1
Reputation: 3704
The proper way to handle this is to surround your variables with double quotes.
var=/foo/bar baz
CMD $var # CMD /foo/bar baz
The above code will execute CMD on /foo/bar and baz
CMD "$var"
This will execute CMD on "/foo/bar baz". It is a best practice to always surround your variables with double quotes in most places.
Upvotes: 1