Bourne Shell Programming: handling argument errors - shell

I am writing a shell program that takes in three arguments:
an integer to determine the function of the program
a file used by the program
The command is of the form myProgram num file. However, I want the program to output an error if the command only has 0, 1, or more than 2 arguments. That is, if I type "myProgram", "myProgram num", or "myProgram num file anotherWord", an error will be printed to the screen. Does anyone know how I could implement this into my existing code?

In bash, when using integers, the (( )) is more intuitive :
#!/bin/bash
if (($# < 2)); then
printf >&2 "There's less than 2 arguments\n"
exit 1
fi
if (($# > 2)); then
printf >&2 "There's more than 2 arguments\n"
exit 1
fi
if ! (($1)); then
printf >&2 "First argument must be a positive integer\n"
exit 1
fi
if [[ ! -f "$2" ]]; then
printf >&2 "Second argument must be an exited file\n"
exit 1
fi
# -->the rest of the script here<--
Moreover, to respect the best practice & proper coding, when printing an error, it must be so STDERR like I do with printf >&2

The built-in variable $# contains the number of arguments that were passed to the script. You use this to check if there are enough arguments like so:
#!/bin/bash
if [ $# -ne 2 ]; then
echo "Usage: myProgram num file" >&2
exit 1
fi
# The rest of your script.

Use shell built in $# to determine the number of arguments passed into your script. Your program name is not counted.

if you are using bash, then you can approach it like this:
#!/bin/bash
if [ $# -lt 2 ] || [ $# -gt 3 ]
then
echo "You did not provide the correct parameters"
echo "Usage: blah blah blah"
fi
This is a very simple check. You can also check man pages for getopt processing which is much more powerful when evaluating command line parameters.
be well

Related

Bash while() loop :: Does this process cmd line arguments?

I'm a Bash newbie, and I'm puzzling through a Bash script where I see this:
while [ $# -ge 2 ]
if [ "x$1" = "x-a" ]; then
echo "STR_A = $2" >>tmpFile.txt
...do more stuff...
elif [ "x$1" = "x-b" ]; then
echo "STR_B = $2" >>tmpFile.txt
...do more stuff...
else
usage
done
The script takes in four (or five?) command line arguments, and that usage function is:
usage() {
echo "usage: myscript -a STR_A | -b STR_B" 1>&2
exit 1
}
So suppose I ran the script like this:
me#ubuntu1:~$./myscript -A apple -B banana
I'm guessing that this code processes the script's command line arguments. I think that the outer while() loop steps through the command line arguments after argument 1, which would be myscript. The inner if() statements check to see an -a or -b flag is used to supply arguments, and then records the text string that follows in tmpFile.txt. Anything outside of those parameters is rejected and the script exits.
A lot of these assumptions rest on the bet that the outer while() loop...
while [ $# -ge 2 ]
...means "parse the BASH argv[] after the first argument", (to put this in C terms.) If that's not a correct assumption, then I have no idea what's going on here. Any feedback is appreciated, thank you!
Some code explanation.
while [ $# -ge 2 ]
There is a missing do for the loop.
This should loop forever if there are two or more arguments, unless shift is used. If there are less than two arguments, the loop does not even start.
if [ "x$1" = "x-a" ]; then
In distant past, it was common to prevent empty strings by adding an extra letter. Nowadays you would if [ "$1" = "-a" ]; then.
else
usage
Note that the usage is called from within the loop. So, if I would call the script as myscript -a, I would not get a usage message. On the other hand, if I would myscript bla bla, I would get an endless stream of error messages, which is probably not what you want.
I would seriously edit the script; determine whether the while is indeed a loop-forever or whether it is used instead of an if, and try the getops for argument parsing.
This doesn't help understand the code you're reading, but I do option parsing like this:
# inititialize vars, not strictly required in this case
a=''
b=''
# process options
while getopts "ha:b:" opt; do
case $opt in
a) a=$OPTARG ;;
b) b=$OPTARG ;;
*) usage ;;
esac
done
# shift after the processing
shift $((OPTIND - 1))
# look for error conditions
if [[ -n $a && -n $b ]]; then
echo "only one of -a or -b should be given" >&2
exit 1
fi
getopts is a bash builtin

Simple bash shell script to ensure that provides two arguments

I'm trying to write simple greetings script in bash to ensure that the user provides at least two arguments. 1)The first argument provides how long we want to delay before the greeting prints. 2) The second argument provides what message we want to display. Sorry I'm fairly new to shell scripting.
#!/bin/sh
if[$# -ge 2]
then
sleep $1
shift
banner $*
else
echo "Usage: Greeing seconds word(s)"
fi
You should use:
#!/bin/sh
if [ $# -ge 2 ]
then
sleep $1
shift
banner $*
else
echo "Usage: Greeing seconds word(s)"
fi
You were missing:
a space after if
a space around [ and ]
If you want to ensure that 2 arguments are given, use ${2?}. For example:
echo First arg is ${1?Missing first argument}. 2nd arg is ${2?Missing 2nd argument}

Unix How to check if a specific word is entered in as an argument

I'm writing a script in Unix but I need a way to check an argument that is entered in the command line is a specific word.
So if when using the script the user types:
$ ./script hello
my script can tell that "hello" was entered as an argument and can display a message appropriately.
And if the user types something other than "hello" as an argument then my script can display another message.
Thanks.
This should work:
#!/bin/bash
if [[ $1 == hello ]];then
echo "hello was entered"
else
echo "hello wasn't entered"
fi
There are a number of ways to check positional arguments against a list. When there are a number of items in the list, you can use a case statement instead of a string of if ... elif ... elif ... fi comparisons. The syntax is as follows:
#!/bin/bash
case "$1" in
"hello" )
printf "you entered hello\n"
;;
"goodbye" )
printf "well goodbye to you too\n"
;;
* )
printf "you entered something I don't understand.\n"
;;
esac
exit 0
Output/Use
$ ./caseex.sh hello
you entered hello
$ ./caseex.sh goodbye
well goodbye to you too
$ ./caseex.sh morning
you entered something I don't understand.
In Bash arguments passed to shell scripts are stored in variables named as follows:
$0 = name of the script.
$1~$n = arguments.
$# = number of arguments.
$* = single string of all arguments: "arg1,arg2,..."
you can simply use if [ $1 == "some string" ]; then ...
You can retrieve the command line arguments with $(number)
for example the first argument would exist at $1 the second at $2 etc.
You can use conditionals in BASH (I assume you are using bash) just like any other language; however the syntax is a bit wonky :). here is a link for you
http://tldp.org/LDP/Bash-Beginners-Guide/html/chap_07.html
If you are sure about the position of the argument you can :
#!/bin/bash
if [[ $1 == SearchWord]];then
echo "SearchWord was entered"
else
echo "SearchWord wasn't entered"
fi
Incase you are not sure:
You can use $*
[ `echo $* | grep $SearchWord| wc -l` -eq 1 ] && echo "Present"|| echo "Not present"

How can I test if files given as an argument exist?

I am making a bash script that you have to give 2 files or more as arguments.
I want to test if the given files exist. I'm using a while loop because I don't know how many files are given. The problem is that the if statement sees the $t as a number and not as the positional parameter $number. Does somebody have a solution?
t=1
max=$#
while [ $t -le $max ]; do
if [ ! -f $t ]; then
echo "findmagic.sh: $t is not a regular file"
echo "Usage: findmagic.sh file file ...."
exit
fi
t=`expr $t + 1`
done
You can do it with the bash Special parameter # in this way:
script_name=${0##*/}
for t in "$#"; do
if [ ! -f "$t" ]; then
echo "$script_name: $t is not a regular file"
echo "Usage: $script_name file file ...."
exit 1
fi
done
With "$#" you are expanding the positional parameters, starting from one as separate words (your arguments).
Besides, remember to provide a meaningful exit status (e.g. exit 1 instead of exit alone). If not provided, the exit status is that of the last command executed (echo in your case, which succes, so you're exiting with 0).
And for last, instead of write the script name (findmagic.sh in your case), you can set a variable at the beginning in your script:
script_name=${0##*/}
and then use $script_name when necessary. In this way you don't need to update your script if it changes its name.

Bash, no-arguments warning, and case decisions

I am learning bash.
I would like to do a simple script that, when not arguments given, shows some message. And when I give numers as argument,s depending on the value, it does one thing or another.
I would also like to know suggestions for the best online manuals for beginners in bash
Thanks
if [[ $# -eq 0 ]] ; then
echo 'some message'
exit 0
fi
case "$1" in
1) echo 'you gave 1' ;;
*) echo 'you gave something else' ;;
esac
The Advanced Bash-Scripting Guide is pretty good. In spite of its name, it does treat the basics.
If only interested in bailing if a particular argument is missing, Parameter Substitution is great:
#!/bin/bash
# usage-message.sh
: ${1?"Usage: $0 ARGUMENT"}
# Script exits here if command-line parameter absent,
#+ with following error message.
# usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
Example
if [ -z "$*" ]; then echo "No args"; fi
Result
No args
Details
-z is the unary operator for length of string is zero.
$* is all arguments.
The quotes are for safety and encapsulating multiple arguments if present.
Use man bash and search (/ key) for "unary" for more operators like this.
Old but I have reason to rework the answer now thanks to some previous confusion:
if [[ $1 == "" ]] #Where "$1" is the positional argument you want to validate
then
echo "something"
exit 0
fi
This will echo "Something" if there is no positional argument $1. It does not validate that $1 contains specific information however.
If there is not only 1 argument, then print usage and exit
if [[ $# != 1 ]] ; then
echo 'USAGE: bin/siege COOKIE'
exit 0
fi

Resources