I have a bash script as shown below. I run this in a directory containing files such as input1.inp
and other files like coords_i.xyz
and submission.sub
in order to make some simple modifications to them:
#!/bin/bash
sed -i -e '25d' *.inp
echo "*xyz -2 2" >> *.inp
sed -n '3,7p' *_i.xyz >> *.inp
echo "Q -1 0 0 3" >> *.inp
echo "Q +1 0 0 -3" >> *.inp
echo "*" >> *.inp
sed -i -e s/"replace1"/"replace2"/g *.sub
rm *.out
If I am in this directory, and I run all the commands individually in the terminal (line by line in the script), everything works fine. However, when I try to group all these commands into the script as shown above, I get an error - essentially after the line sed -i -e '25d' *.inp
, the script stops and a file called *.inp
is created in my directory. If I try to run the echo
command separately after that, it says the command is ambiguous (presumably because of the existence of this *.inp
file).
Why don't my wildcards work the same way in the script as they did when I ran them separately and sequentially in the terminal, and what can I do so that they work properly in the script?
Using wildcards this way is hazardous; the easy advice is "don't". Evaluate them only once, and then you can check their outputs before trying to use them.
In the below, we define an assert_only_one
function that stops your script when an array -- assigned from a glob -- contains less or more than exactly one element. Consequently, we're able to write code that more clearly and explicitly describes our desired behavior.
#!/usr/bin/env bash
shopt -s nullglob # Stop *.xyz evaluating to '*.xyz' if no such files exist
assert_only_one() {
local glob; glob=$1; shift
case $# in
0) echo "ERROR: No files matching $glob exist" >&2; exit 1;;
1) return 0;;
*) echo "ERROR: More than one file matching $glob exists:" >*2
printf ' %q\n' "$@" >&2
exit 1;;
esac
}
inp_files=( *.inp ); assert_only_one '*.inp' "${inp_files[@]}"
sub_files=( *.sub ); assert_only_one '*.sub' "${sub_files[@]}"
xyz_files=( *_i.xyz )
sed -i -e '25d' "${inp_files[0]}"
{
echo "*xyz -2 2"
sed -n '3,7p' "${xyz_files[@]}"
echo "Q -1 0 0 3"
echo "Q +1 0 0 -3"
echo "*"
} >>"${inp_files[0]}"
sed -i -e s/"replace1"/"replace2"/g -- "${sub_files[@]}"
rm -- *.out