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
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 have a shell script that takes parameters, below is the code..
Right now it will only accept parameters if passed if called like this: script.sh --mode=load (or -m=load)
Is there a way to modify this so that it can be called with or without the "=" sign, so that I can call: script.sh --mode load (or -m load)
Ideally needs to work in pure bash as I don't have access to install additional tools, etc.
for i in "$#"
do
case $i in
-m=*|--mode=*)
MODE="${i#*=}"
if [[ $MODE =~ ^(dump|load)$ ]]; then
echo "" > /dev/null
else
bark "Invalid --mode set, set this to dump or load.";
exit 1
fi
;;
-p=*|--db-path=*)
DBPATH="${i#*=}"
;;
-d=*|--dump-dir=*)
DUMPDIR="${i#*=}"
;;
-l=*|--list-file=*)
TABLES="${i#*=}"
# check if file exists on disk
if [ -e $TABLES ]
then
echo "" >> /dev/null
else
bark "Table file not found!";
exit 1
fi
;;
-t=*|--tenant-name=*)
TENANT="${i#*=}"
# check if tenant is correct
if [[ $TENANT =~ ^($TENANT_LIST)$ ]]; then
echo "" >> /dev/null
else
bark "Tenant name does not match, aborting.";
exit 1
fi
;;
-s|--shared)
SHARED=YES
;;
*) usage # unknown option
;;
esac
done
My bash version:
bash --version
GNU bash, version 4.3.22(1)-release (powerpc-ibm-aix5.1.0.0)
Loop on $#. When $1 is "-m", do a shift. So in the next loop $1 will now be the argument to the -m option.
script.sh --mode load
# FIRST LOOP
$# is "--mode load"
$1 is "--mode"
shift
# SECOND LOOP
$# is "load"
$1 is "load"
This is also useful if you can specify many arguments instead of just one like you have right now. Error checking should be done to validate your argument values, and if a user did script.sh --mode with no other argument.
Don't reinvent the wheel.
If you're OK with just 1 character options, use the bash builtin getopts
#!/bin/bash
while getopts :m:p:d:l:t:s opt; do
case $opt in
m) mode=$OPTARG ;;
p) dbpath=$OPTARG ;;
d) dumpdir=$OPTARG ;;
l) tables=$OPTARG
# test file existence
;;
t) tenant=$OPTARG
# test tenant
;;
s) shared=YES ;;
:) echo "Missing argument for option -$OPTARG" >&2
exit 2
;;
*) echo "Invalid option -$OPTARG" >&2
exit 2
;;
esac
done
shift $((OPTIND - 1))
cat << SHOW_VARS
I have:
mode=$mode
dbpath=$dbpath
dumpdir=$dumpdir
tables=$tables
tenant=$tenant
shared=$shared
rest of args=$*
SHOW_VARS
Otherwise, you may be able to use the external getopt program to help parse your args. I don't have an AIX box to test on, so YMMV
tempargs=$(
getopt \
-o m:d:l:t:s \
--long mode:,db-path:,dump-dir:,list-file:,tenant-name:,shared \
-- "$#"
)
if [[ $? -ne 0 ]]; then echo "Error..." >&2; exit 2; fi
eval set -- "$tempargs"
while true; do
case $1 in
-m|--mode) mode=$2; shift 2;;
-p|--db-path) dbpath=$2; shift 2;;
-d|--dump-dir) dumpdir=$2; shift 2;;
-l|--list-file) tables=$2
# test file existence
shift 2
;;
-t|--tenant-name) tenant=$2
# test tenant
shift 2
;;
-s|--shared) shared=YES; shift;;
--) shift; break ;;
*) echo "Error..." >&2; exit 2 ;;
esac
done
I am writing a bash wrapper for scp'ing into and from a certain host with a certain username, like:
johny#bonjour:~/bin$ cat scpphcl
#!/bin/bash
download=false
upload=false
local=""
remote=""
usage()
{
echo "Usage: $0 -d[-u] -l <LocalPath> -r <RemotePath>"
exit 1
}
while getopts "h?dul:r:" opt; do
case "$opt" in
h|\?)
usage
;;
d)
download=true
upload=false
;;
u)
download=false
upload=true
;;
l)
local=$OPTARG
;;
r)
remote=$OPTARG
;;
esac
done
if [[ -z $local || -z $remote ]]; then
echo "Need to provide local and remote path."
usage
fi
if $download; then
scp somebody#somehost:"$remote" $local
elif $upload; then
scp $local somebody#somehost:"$remote"
else
echo "Neither download nor upload?"
exit 1
fi
if [[ $? -ne 0 ]]; then
echo "Something wrong happened in the scp process."
exit 1
fi
exit 0
It works well with the usual filenames, but if there is any wildcard in the local filename field, it will not work right.
johny#bonjour:~/test$ scpphcl -u -l * -r /u/somebody/temp
Need to provide local and remote path.
Usage: /Users/johny/bin/scpphcl -d[-u] -l <LocalPath> -r <RemotePath>
There is a walkaround, using sinqle quotes around the local file argument if there is a wildcard in it:
johny#bonjour:~/test$ scpphcl -u -l '*' -r /u/somebody/temp
But even this walkaround will not work, if the command is issued outside the folder test:
johny#bonjour:~/test$ cd ..
johny#bonjour:~$ scpphcl -u -l 'test/*' -r /u/somebody/temp
This doesn't work and will hang in the scp process.
Any help in how to pass the wildcard in local filenames with the bash wrapper?
It's probably best not to require your users to quote wildcard patterns. I'd instead change the interface of your program to accept any number of local paths, after the option arguments:
echo "Usage: $0 [-d|-u] [-r <RemotePath>] <LocalPath>..."
When reading options, consume them with shift:
while getopts "h?dur:" opt; do
case "$opt" in
h|\?)
usage
exit 0
;;
d)
download=true
upload=false
;;
u)
download=false
upload=true
;;
r)
remote="$OPTARG"
;;
*)
usage >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
Now the remaining positional arguments are the local filenames (and can be accessed with "$#" - note the all-important double-quotes there):
if test -z "$*" # no LocalPath arguments!
then usage >&2; exit 1
elif $download
then exec scp somebody#somehost:"$remote" "$#"
elif $upload
then exec scp "$#" somebody#somehost:"$remote"
fi
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
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