I have seen many examples for how to use getopts. But I know very basic of bash and I was not able to to implement it in my situation. I really appreciated if anyone expert can show me the template.
I have a script with minimum 6 and maximum 10 input. Here is a brief description:
script.sh -P passDir -S shadowDir -G groupDir -p password -s shadow
User must provide argument for -P -S -G and if not I must display usage and close the program. If argument are provided I need them to be saved into an appropriate variable.
But -p and -s is optional. However, if there is no -p I should do some tasks and if there is no -s I should do some other tasks and if none of them is there I should do some other tasks.
Following is what I have written so far but it stock in the for loop.
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "Only root may add a user to system"
exit 2
else
usage() { echo "Usage: $0 [-P <password file path>] [-S <shadow file path>] [-G <group path>]" 1>&2; exit 1; }
passDir=""
shadowDir=""
groupDir=""
while getopts ":P:S:G:" inp; do
case "${inp}" in
P)
$passDir = ${OPTARG};;
S)
$shadowDir = ${OPTARG};;
G)
$groupDir = ${OPTARG};;
*)
usage;;
esac
done
echo "${passDir}"
echo "${shadowDir}"
echo "g = ${groupDir}"
fi
At the moment is user does not enter arguments nothing will be shown and if there is arguments it goes inside a loop!
As I understand it, you are just missing some if statements to handle missing arguments. Consider:
usage() { echo "Usage: $0 [-P <password file path>] [-S <shadow file path>] [-G <group path>]" 1>&2; exit 1; }
if [ "$(id -u)" != "0" ]; then
echo "Only root may add a user to system"
exit 2
fi
passDir=""
shadowDir=""
groupDir=""
while getopts "P:S:G:" inp; do_
case "${inp}" in
P)
passDir=${OPTARG};;
S)
shadowDir=${OPTARG};;
G)
groupDir=${OPTARG};;
*)
usage;;
esac
done
if [ -z "$passDir" ] && [ -z "$shadowDir" ]
then
# if none of them is there I should do some other tasks
echo do some other tasks
elif ! [ "$passDir" ]
then
# if there is no -p I should do some tasks_
echo do some tasks
elif ! [ "$shadowDir" ]
then
#if there is no -s I should do some other tasks
echo do some other tasks
fi
I fixed a couple of things in your script. This works for me:
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "Only root may add a user to system"
exit 2
fi
usage() { echo "Usage: $0 [-P <password file path>] [-S <shadow file path>] [-G <group path>]" 1>&2
exit 1
}
passDir=""
shadowDir=""
groupDir=""
while getopts ":P:S:G:" inp; do
case "${inp}" in
P)
passDir=${OPTARG};;
S)
shadowDir=${OPTARG};;
G)
groupDir=${OPTARG};;
*)
usage;;
esac
done
echo "p = $passDir"
echo "s = $shadowDir"
echo "g = $groupDir"
Assignments must not contain spaces: a=1 works, a = 1 doesn't
The variable name should not be prefixed with a $ in an assignment
If your if branch contains an exit statement, there's no need to put the rest of your code in the else branch
Related
I need to pass defaults to the -c -t -u flags.
in the -c i need that to be infinite
in the -t i need that to be 1 sec
and in the -u the defauls is ANY user
#!/bin/bash
set -u
print_usage(){
echo "usage: script[-c] [-t] [-u] exe-name"
}
if [[ $# -eq 0 ]]; then
print_usage
exit 1
fi
while getopts :c:t:u: flag; do
case $flag in
c) counts=$OPTARG;;
t) timeout=$OPTARG;;
u) user_name=$OPTARG;;
esac
done
if [ $counts==true ]
then
top -n ${counts}
fi
if [ $timeout==true ]
then
top -d ${timeout}
fi
if [ $user_name==true ]
then
top -u ${user_name}
fi
I tried to put something like that in the biginning but it doesn't work:
counts=
timeout=1
user_name=.
Assign default values to the variables before the while loop.
Use arrays for variables that can hold multiple arguments. See Setting an argument with bash for the reasons.
You can then execute a single top command that has all the arguments combined.
#!/bin/bash
set -u
print_usage(){
echo "usage: script [-c repetitions] [-t timeout] [-u username] exe-name"
}
if [[ $# -eq 0 ]]; then
print_usage
exit 1
fi
counts=()
timeout=(-d 1)
user_name=()
while getopts :c:t:u: flag; do
case $flag in
c) counts=(-n "$OPTARG");;
t) timeout=(-d "$OPTARG");;
u) user_name=(-u "$OPTARG");;
esac
done
top "${counts[#]}" "${timeout[#]}" "${user_name[#]}"
I want a script to take in two options, both are required. if I pass one in, the script doesn't print an error requesting you to pass in a second one.
-bash-4.2$ bash test.sh -b
Invalid option: b requires an argument
-bash-4.2$ bash test.sh -p
Invalid option: p requires an argument
-bash-4.2$ bash test.sh -b sdfsfd
-bash-4.2$ bash test.sh -p sdfsfd
-bash-4.2$ bash test.sh -b sdfsfd -s sfd
Invalid option: s
Code
showHelp()
{
cat << EOF
Find files in client's folder and upload to S3 bucket.
Usage: $(basename $0) [-p PATH_TO_SEARCH] [-b S3 bucket]
OPTIONS:
-h Show this help message
-p Path to search
-b S3 Bucket
EOF
exit 1
}
while getopts ":p:b:h" o; do
case "${o}" in
h)
showHelp
;;
p)
p=${OPTARG}
;;
b)
b=${OPTARG}
;;
\? )
echo "Invalid option: $OPTARG";;
: )
echo "Invalid option: ${OPTARG} requires an argument";;
esac
done
shift $((OPTIND-1))
if [ -z "${p}" ]; then
showHelp
fi
if [ -z "${b}" ]; then
showHelp
fi
If you want to ensure you get both options, you can use something like:
no_p=1
no_b=1
while getopts ":p:b:h" o; do
case "${o}" in
h)
showHelp
;;
p)
p=${OPTARG}
no_p=0
;;
b)
b=${OPTARG}
no_b=0
;;
\? )
echo "Invalid option: $OPTARG";;
: )
echo "Invalid option: ${OPTARG} requires an argument";;
esac
done
[[ $no_p -eq 1 ]] && echo "No -p provided" && exit 1
[[ $no_b -eq 1 ]] && echo "No -b provided" && exit 1
I'm trying to put together a script to monitor files and im having a hard tiem with getopts. If I run more than once the shell values dont changes... There's a ton of clean up but I've been stuck on this one issue for a couple hours so ...
#!/bin/bash
function printUsage {
echo "Usage: $0 [-e environment] [-r region] [-l logtype]"
echo "Requirements:"
echo " -e environment to look in; prod or dev"
echo " -r 3 digit region designation"
echo " -l log type to find; soap, message, main, service, or detail"
echo " example: $0 -e dev -r 111 -f soap"
exit 0
}
if [ $# -lt 1 ]; then
printUsage
fi
while getopts "e:r:l:" opt; do
case $opt in
e)
environment="$OPTARG"
;;
r)
region="$OPTARG"
;;
l)
logtype="$OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG"
printUsage;;
:)
echo "-$OPTARG requires an argument"
printUsage;;
esac
done
#build array of directories to search based on argument
function buildProductionArray {
# assume these are here
logpaths=( )
}
#build array of directories to search based on argument
function buildDevArray {
# assume these are here
logpaths=( )
}
function validateArguments {
#validate the environment
if [ "$(echo "$environment" | tr "[:lower:]" "[:upper:]")" == "PROD" ]; then
buildProductionArray
elif [ "$(echo "$environment" | tr "[:lower:]" "[:upper:]")" == "DEV" ]; then
buildDevArray
else
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid environment" >&2; exit 1
fi
#validate the region
if ! [ "$region" -eq "$region" ] 2>/dev/null; then
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid region" >&2; exit 1
fi
#validate the logtype
function in_array() {
elements=${1}
element=${2}
for i in "${elements[#]}" ; do
if [ "$i" == "$element" ] ; then
return 1
fi
done
return 0
}
logvalues=(soap message service main detail)
log="$(echo "$logtype" | tr '[:upper:]' '[:lower:]')"
echo "$log"
if in_array logvalues "${log}"; then
if [[ "$log" == "service" || "$log" == "detail" ]]; then
log="-$log-time-"
else
log="-$log-"
fi
else
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid environment" >&2; exit 1
fi
}
validateArguments
for i in "${logpaths[#]}"
do
#assume this is here
done
I'm writing a script to elaborate many text file.
I need to pass N text files to my bash script.
The script invocation is like this:
:~$ ./bristat [-u user] [-p] [-m] file1.log...fileN.log
The script elaborate the logfile(s) following arguments -u -m -p.
args -u -m -p are optional (i can invoke the script with none, any or all of these args);
file1.log...fileN.log are necessary for the execution ( 0 < files <= N )
logfiles have all the suffix .log
My question is: how to identify and check these logfiles in the command line?
I Don't care (now) about content of the files and what to do, I just need the script recognise them, do the attributes checking, and then process them (but how to process is not what I ask here).
I don't know if I was clear. Ask for better clarifications.
This is my code without files checking. I need to integrate here.
#!/bin/bash
if [ $# == 0 ]; then
echo "No argument passed:: ERROR"
exit
fi
usage="Usage: bristat [-u args] [-p] [-m] logfile1...logfileN"
params=":u:pm"
U=0 P=0 M=0
while getopts $params OPT; do
case $OPT in u)
case ${OPTARG:0:1} in
-)
echo "Invalid argument $OPTARG" >&2
exit
esac
echo "[-u] User = $OPTARG" >&2
U=$((++U))
;; p)
echo "[-p] Number of lost games = " >&2
P=$((++P))
;; m)
echo "[-m] Average of total points = " >&2
M=$((++M))
;; \?)
echo $usage >&2
exit
;; :)
echo "Option [-$OPTARG] requires an argument" >&2
exit
;;
esac
done
#check for duplicate command in option line
if [ "$U" -gt "1" ]; then
echo "Duplicate option command line [-u]"
exit
fi
if [ "$P" -gt "1" ]; then
echo "Duplicate option command line [-p]"
exit
fi
if [ "$M" -gt "1" ]; then
echo "Duplicate option command line [-m]"
exit
fi
shift $[$OPTIND -1] # Move argument pointer to next.
For more clarity, the script examine the logfile to return statistics:
-u check if user is an authorized name
-m returns the average of total points about a game
-p returns the number of lost match about a game
Edit
If I want to call the arguments in random position? I mean that (i.e.):
:~$ ./bristat [-u user] [-p] [-m] file1.log file2.log file3.log
:~$ ./bristat [-m] file1.log file2.log [-u user] [-p] file3.log
:~$ ./bristat [-m] file1.log [-p] file2.log [-u user] file3.log
could be the same invocations. How can I change my code? Any suggestions?
You want to iterate your list of filenames with shift
after you get your arguments,
shift $(( OPTIND-1 ))
while [ -f $1 ]
do
#do whatever you want with the filename in $1.
shift
done
I am trying to write a bash script that takes in an option.
Lets call these options A and B.
In the script A and B may or may not be defined as variables.
I want to be able to check if the variable is defined or not.
I have tried the following but it doesn't work.
if [ ! -n $1 ]; then
echo "Error"
fi
Thanks
The "correct" way to test whether a variable is set is to use the + expansion option. You'll see this a lot in configure scripts:
if test -s "${foo+set}"
where ${foo+set} expands to "set" if it is set or "" if it's not. This allows for the variable to be set but empty, if you need it. ${foo:+set} additionally requires $foo to not be empty.
(That $(eval echo $a) thing has problems: it's slow, and it's vulnerable to code injection (!).)
Oh, and if you just want to throw an error if something required isn't set, you can just refer to the variable as ${foo:?} (leave off the : if set but empty is permissible), or for a custom error message ${foo:?Please specify a foo.}.
You did not define how these options should be passed in, but I think:
if [ -z "$1" ]; then
echo "Error"
exit 1
fi
is what you are looking for.
However, if some of these options are, err, optional, then you might want something like:
#!/bin/bash
USAGE="$0: [-a] [--alpha] [-b type] [--beta file] [-g|--gamma] args..."
ARGS=`POSIXLY_CORRECT=1 getopt -n "$0" -s bash -o ab:g -l alpha,beta:,gamma -- "$#"`
if [ $? -ne 0 ]
then
echo "$USAGE" >&2
exit 1
fi
eval set -- "$ARGS"
unset ARGS
while true
do
case "$1" in
-a) echo "Option a"; shift;;
--alpha) echo "Option alpha"; shift;;
-b) echo "Option b, arg '$2'"; shift 2;;
--beta) echo "Option beta, arg '$2'"; shift 2;;
-g|--gamma) echo "Option g or gamma"; shift;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
echo Remaining args
for arg in "$#"
do
echo '--> '"\`$arg'"
done
exit 0
Don't do it that way, try this:
if [[ -z $1 ]]; then
echo "Error"
fi
The error in your version is actually the lack of quoting.
Should be:
if [ ! -n "$1" ]; then
echo "Error"
fi
But you don't need the negation, use -z instead.
If you work on Bash, then use double brackets [[ ]] too.
from the man bash page:
-z string
True if the length of string is zero.
-n string
True if the length of string is non-zero.
Also, if you use bash v4 or greater (bash --version) there's -v
-v varname
True if the shell variable varname is set (has been assigned a value).
The trick is "$1", i.e.
root#root:~# cat auto.sh
Usage () {
echo "error"
}
if [ ! -n $1 ];then
Usage
exit 1
fi
root#root:~# bash auto.sh
root#root:~# cat auto2.sh
Usage () {
echo "error"
}
if [ ! -n "$1" ];then
Usage
exit 1
fi
root#root:~# bash auto2.sh
error