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
How to Get the Current Network Interface Throughput Statistics on Linux/Unix
How to Enable Scrolling in Tmux Panels with Mouse Wheel
Home/End Keys Do Not Work in Tmux
Does Linux Kill Background Processes If We Close the Terminal from Which It Has Started
How to Delete a User in Linux When the System Says Its Currently Used in a Process
Check If Service Exists in Bash (Centos and Ubuntu)
Linux: How to Force a Specific Network Interface to Be Used
Install Packages in Alpine Docker
Why Linux Kernel Zone_Normal Is Limited to 896 Mb
Can Clang Compile Code with Gcc Compiled .A Libs
Is There a Winscp Equivalent for Linux
Shell Script to Count Files, Then Remove Oldest Files
Linux: Merging Multiple Files, Each on a New Line
How to Open a "-" Dashed Filename Using Terminal
How to Check If a Process Is in Hang State (Linux)