Bash Shell Script - Check for a Flag and Grab Its Value

Bash Shell Script - Check for a flag and grab its value

You should read this getopts tutorial.

Example with -a switch that requires an argument :

#!/bin/bash

while getopts ":a:" opt; do
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done

Like greybot said(getopt != getopts) :

The external command getopt(1) is never safe to use, unless you know
it is GNU getopt, you call it in a GNU-specific way, and you ensure
that GETOPT_COMPATIBLE is not in the environment. Use getopts (shell
builtin) instead, or simply loop over the positional parameters.

How to get arguments with flags in Bash

This is the idiom I usually use:

while test $# -gt 0; do
case "$1" in
-h|--help)
echo "$package - attempt to capture frames"
echo " "
echo "$package [options] application [arguments]"
echo " "
echo "options:"
echo "-h, --help show brief help"
echo "-a, --action=ACTION specify an action to use"
echo "-o, --output-dir=DIR specify a directory to store output in"
exit 0
;;
-a)
shift
if test $# -gt 0; then
export PROCESS=$1
else
echo "no process specified"
exit 1
fi
shift
;;
--action*)
export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'`
shift
;;
-o)
shift
if test $# -gt 0; then
export OUTPUT=$1
else
echo "no output dir specified"
exit 1
fi
shift
;;
--output-dir*)
export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'`
shift
;;
*)
break
;;
esac
done

Key points are:

  • $# is the number of arguments
  • while loop looks at all the arguments supplied, matching on their values inside a case statement
  • shift takes the first one away. You can shift multiple times inside of a case statement to take multiple values.

bash script; searching files for threshold value in table by row and print out to file or stdout

with a lot of help I finally have a solution:

#! /bin/bash
cutoff=5.0
for i in sim_results_rep*; do

while read -r LINE; do
echo $LINE | grep 'First atom' > /dev/null
if [ $? -ne 0 ]
then
continue
else
read -r LINE
while true; do
read -r LINE
echo $LINE | grep 'Total distance penalty' > /dev/null
if [ $? -ne 0 ]
then
x=`echo "$LINE" | awk '{print $11}'`
if [ `echo "$x > $cutoff" | bc` == 1 ]
then
echo "In file $i, the follwing distance has a penalty higher than $cutoff"
echo $LINE
else
continue

fi

else
break
fi

done
break
fi

done < $i/*.nmrout

done

Direct wrong flag input

while getopts ahs: option
do
case "$option"
in
$FLAG_HELP) code/function;;
$FLAG_TARLogs) code/function;;
$FLAG_TARResrc) code/function ;;
$FLAG_DELLogs) code/function;;
$FLAG_TAREncod) code/function;;
$FLAG_DELResrc) code/function;;
$FLAG_DELEncod) code/function;;
h) DisplayUsage
exit 0;;
?) DisplayUsage
exit 1;;
esac
done 2>/dev/null

Display function contains the options and how they are used..

DisplayUsage()
{
echo
echo
echo "Options are "
echo
echo
echo
echo
}

getops $OPTARG is empty if flag value contains brackets

Short summary: Double-quote your variable references. And use shellcheck.net.

Long explanation: When you use a variable without double-quotes around it (e.g. echo ${str}), the shell tries to split its value into words, and expand anything that looks like a wildcard expression into a list of matching files. In the case of [0.0.0.0], the brackets make it a wildcard expression that'll match either the character "0" or "." (equivalent to [0.]). If you had a file named "0", it would expand to that string. With no matching file(s), it's normally left unexpanded, but with the nullglob set it expands to ... null.

Turning off nullglob solves the problem if there are no matching files, but isn't really the right way do it. I remember (but can't find right now) a question we had about a script that failed on one particular computer, and it turned out the reason was that one computer happened to have a file that matched a bracket expression in an unquoted variable's value.

The right solution is to put double-quotes around the variable reference. This tells the shell to skip word splitting and wildcard expansion. Here's an interactive example:

$ str='[0.0.0.0]'    # Quotes aren't actually needed here, but they don't hurt
$ echo $str # This works without nullglob or a matching file
[0.0.0.0]
$ shopt -s nullglob
$ echo $str # This fails because of nullglob

$ shopt -u nullglob
$ touch 0
$ echo $str # This fails because of a matching file
0
$ echo "$str" # This just works, no matter whether file(s) match and/or nullglob is set
[0.0.0.0]

So in your script, simply change the last line to:

echo "${str}"

Note that double-quotes are not required in either case $opt in or str=$OPTARG because variables in those specific contexts aren't subject to word splitting or wildcard expansion. But IMO keeping track of which contexts it's safe to leave the double-quotes off is more hassle than it's worth, and you should just double-quote 'em all.

BTW, shellcheck.net is good at spotting common mistakes like this; I recommend feeding your scripts through it, since this is probably not the only place you have this problem.

How do I grab an INI value within a shell script?

How about grepping for that line then using awk

version=$(awk -F "=" '/database_version/ {print $2}' parameters.ini)

Propagate all arguments in a Bash shell script

Use "$@" instead of plain $@ if you actually wish your parameters to be passed the same.

Observe:

$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $@

$ cat quotes.sh
#!/bin/bash
echo_args.sh "$@"

$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:

$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:

$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:

Getting error while executing a shell script to check for a particular character in each line at a specific position and if found replace it with X

Well, I'll show 2 examples hopefully they can give an idea.
Example 1

filename = "/ray/test/raymond/test2.data"
if [ -f "$filename" -a -r "$filename" ]; then
while read -r line; do
if [ "${line:421:1}" = 'D' ]; then
printf '%s%s%s\n' "${line:0:420}" 'X' "${line:422}" >> "$new_file"
else
printf '%s\n' "$line" >> "$new_file"
fi
done < "$filename"
elif [ ! -f "$filename" ]; then
echo 'ERROR: Not a file'
return 1
elif [ ! -r "$filename" ]; then
echo 'ERROR: Can not read the file'
return 1
fi

Example 2

filename = "/ray/test/raymond/test2.data"
new_file="$filename"'_new.txt'
if [ -f "$filename" -a -r "$filename" ]; then
output="$new_file" awk 'BEGIN { output=ENVIRON["output"] }
{
if(substr($0, 422, 1) == "D") {
$0=sprintf("%s%s%s", substr($0, 1, 421), "X", substr($0, 423))
}
print $0 >> output
}' "$filename"
elif [ ! -f "$filename" ]; then
echo 'ERROR: Not a file'
return 1
elif [ ! -r "$filename" ]; then
echo 'ERROR: Can not read the file'
return 1
fi


Related Topics



Leave a reply



Submit