GETOPTS command does not work in my shell script - shell

Can anybody help me please?
i wrote a script and in my script i used GETOPTS to make options but it does not work
it had some error and i check it in shellcheck.net and fixed them but it's not working
#!/bin/bash
while getopts 'n:c2rFt' option; do
case "$option" in
n) export Field="$OPTARG"
;;
c) #Question 1
cat "$1" | awk '{print $1}' | sort | uniq -c | sort -nrk 1,1 > file1
awk 'NR=="$Field" {print}' file1
;;
2) #Question 2
cat "$1" | awk '{ if($9 == 200) print $1,$9 }' | sort | uniq -c | sort -nrk 1,1 > file1
awk 'NR=="$Field" {print}' file1
;;
r) #Question 3
cat "$1" | awk '{print $1,$9}' | sort | uniq -c | sort -nrk 1,1 > file1
awk 'NR=="$Field" {print}' file1
;;
F) #Question 4
cat "$1" | awk '{if($9 >= 400 && $9 <= 451)} {print $1,$9}' | sort | uniq -c | sort -nrk 1,1 > file1
awk 'NR=="$Field" {print}' file1
;;
t) #Question 5
cat "$1" | awk '{print $1,$10}' | sort | uniq -c | sort -nrk 3,3 > file1
awk 'NR=="$Field" {print}' file1
;;
?)
echo "You used wrong option"
echo "USAGE: log_sum.sh [-n N] (-c|-2|-r|-F|-t|-f) <filename>"
echo " -n: Limit the number of results to N"
echo " -c: shows th IP address makes the most number of connection attempts"
echo " -2: shows th most number of seccessful attempts "
echo " -r: shows th most common result codes and their IP addresses"
echo " -F: shows the most common result codes that indicate failure"
echo " -t: shows the IP addresses that get the most bytes sent to them"
exit 1
;;
esac
done

You have your "business logic" in the wrong place: your code assumes that the user will provide the -n option first. That's not required by getopts. You have to write this kind of program with 3 stages: option parsing, validation and actions:
#!/bin/bash
usage() {
local program=$(basename "$0")
cat <<END_USAGE >&2
USAGE: $program -n N (-c|-2|-r|-F|-t|-f) <filename>
-n: Limit the number of results to N
-c: shows th IP address makes the most number of connection attempts
-2: shows th most number of seccessful attempts
-r: shows th most common result codes and their IP addresses
-F: shows the most common result codes that indicate failure
-t: shows the IP addresses that get the most bytes sent to them
END_USAGE
}
# Option parsing
while getopts ':n:c2rFt' option; do
case "$option" in
n) num_results=$OPTARG ;;
c) show_connections=yes ;;
2) show_successful=yes ;;
r) show_common_results=yes ;;
F) show_common_failures=yes ;;
t) show_most_bytes=yes ;;
?) echo "Error: unknown option $OPTARG"; usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
filename=$1
# Validation
if [[ -z $num_results ]]; then
echo "Error: you must provide the -n option" >&2
usage >&2
exit 1
fi
if [[ -z $filename ]]; then
echo "Error: you must provide a filename" >&2
usage >&2
exit 1
fi
# Actions
# helper function to encapsulate repeated code
top_results() { sort | uniq -c | sort -nrk 1,1 | sed "${num_results}q"; }
if [[ $show_connections == yes ]]; then
awk '{print $1}' "$filename" | top_results
fi
if [[ $show_successful == yes ]]; then
awk '$9 == 200 {print $1,$9}' "$filename" | top_results
fi
if [[ $show_common_results == yes ]]; then
awk '{print $1,$9}' "$filename" | top_results
fi
if [[ $show_common_failures == yes ]]; then
awk '$9 >= 400 && $9 <= 451 {print $1,$9}' "$filename" | top_results
fi
if [[ $show_most_bytes == yes ]]; then
awk '{print $1,$10}' "$filename" | top_results
fi

Related

Is there any way in Linux to show the output of a command being used multiple times side by side?

I am a noob in Linux and Shell Scripting and I was trying to write a shell script that would display a directory listing based on the command line argument being passed.
It would accept the 4 positional arguments namely F, D, T and P and display accordingly.
Sample Input and Output:
$ ./list.sh F D T P
File name date time permission
------------- ------ ----- ---------------
Filename1 date time permission
Filename2 date time permission
Total no. of files : <total number>
Total no of normal file : <number>
Total no of directory : <number>
The arguments could be shuffled. I tried to write a shell script which is able to do the work but not able to display as I want.
#!/bin/bash
args=("$#") # Storing all the positional args into an array
n=0
x=0
while [ $n -ne 4 ]
do
case "${args[$n]}" in
F) argsVal[$x]=9 ;;
D) argsVal[$x]=6 ;;
T) argsVal[$x]=8 ;;
P) argsVal[$x]=1 ;;
*) echo "Invalid Option" ;;
esac
n=`expr $n + 1`
x=`expr $x + 1`
done
n=0
while [ $n -ne 4 ]
do
case "${argsVal[$n]}" in
1) echo -e "Permissions(P)\t\c" ;;
6) echo -e "Date(D)\t\c" ;;
8) echo -e "Time(T)\t\c" ;;
9) echo -e "FileNames(F)\t\c" ;;
*) ;;
esac
n=`expr $n + 1`
done
echo -e "\n"
n=0
while [ $n -ne 4 ]
do
case "${argsVal[$n]}" in
1)
awk '{print $1}' listout ;;
6)
awk '{print $6,$7}' listout ;;
8)
awk '{print $8}' listout ;;
9)
awk '{print $9}' listout ;;
*) ;;
esac
n=`expr $n + 1`
done
The output is :
Permissions(P) Date(D) Time(T) FileNames(F)
-rw-r--r--
-rwxr--r--
-rwxr--r--
-rwxr--r--
-rwxr--r--
Jun 18
Jun 19
Jun 6
Jun 18
Jun 6
22:04
12:04
20:45
22:32
21:17
listout
list.sh
script.sh
test1.sh
validvoter.sh
Change the last while loop code
n=0
while [ $n -ne 4 ]
do
case "${argsVal[$n]}" in
1)
awk '{print $1}' listout ;;
6)
awk '{print $6,$7}' listout ;;
8)
awk '{print $8}' listout ;;
9)
awk '{print $9}' listout ;;
*) ;;
esac
n=`expr $n + 1`
done
to
awk -v a="${argsVal[0]}" -v b="${argsVal[1]}" -v c="${argsVal[2]}" -v d="${argsVal[3]}" '{printf("%s\t%s\t%s\t%s\n", $a,$b,$c,$d)}' listout
One awk command will scan the hole file, and it can print the columns what you want.
Reference man awk we get some usage like that:
-v var=val
--assign var=val
Assign the value val to the variable var, before execution of the program begins. Such variable values are available to the BEGIN rule of an AWK program.
printf fmt, expr-list Format and print. See The printf Statement, below.
Use column to format columns:
stat -c "%n %y %A" * | column -t
Try with printf command:
#!/bin/bash
while [ $# -gt 0 ] ; do
case $1 in
F)
arg="$arg\$9, "
tit=("${tit[#]}" "File name (F)")
unl=("${unl[#]}" "-------------")
fmt="$fmt%-35s"
;;
P)
arg="$arg\$1, "
tit=("${tit[#]}" "Permissions (P)")
unl=("${unl[#]}" "---------------")
fmt="$fmt%-17s"
;;
T)
arg="$arg\$8, "
tit=("${tit[#]}" "Time (T)")
unl=("${unl[#]}" "--------")
fmt="$fmt%-10s"
;;
D)
arg="$arg\$6\" \"\$7, "
tit=("${tit[#]}" "Date (D)")
unl=("${unl[#]}" "--------")
fmt="$fmt%-10s"
;;
*)
echo "invalid option $1"
exit 1
esac
shift
done
if [ -z "$arg" ] ; then
echo "missing options"
exit 2
fi
arg=${arg%, } # remove trailing comma and space from $arg
printf "$fmt\\n" "${tit[#]}"
printf "$fmt\\n" "${unl[#]}"
ls -l | grep -v '^total' > listout
grep -v "^total" listout |
awk "{ printf \"$fmt\\n\", $arg}"
tf=$(cat listout | wc -l)
tnf=$(grep '^-' listout | wc -l)
td=$(grep '^d' listout | wc -l)
echo
echo "Total number of files: $tf"
echo "Total number of normal files: $tnf"
echo "Total number of directories: $td"

How to break pipe if stdin is empty?

I want to break the whole pipe if the stdin is empty. I try to combined xargs -r and tee, which means not print and write if stdin is empty, but it failed
...| upstream commands | xargs -r tee output.txt | downstream commands | ...
Any feedback appreciated.
There is no way you can actually terminate a bash pipe conditionally. All commands in a pipeline are started simultaneously. There is however a tool available that would assist you with creating a conditional pipeline. In moreutils you can find the tool ifne which executes a command if and only if the input /dev/stdin is not empty. So you could write something like:
$ command1 | ifne command2 | ifne command3 | ifne command4
Here all commands ifne and command1 are started simultaniously. Only if ifne receives input via /dev/stdin, it will start its respective commandx
Pipe'll break if command failed. You can add grep in between to achieve this. An example:
$ echo ok | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
ok 1 2 3
Now add grep:
$ echo ok | grep -Ei '^.+$' | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
ok 1 2 3
And test empty echo:
$ echo | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
1 2 3
$ echo | grep -Ei '^.+$' | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
Looks like this works but it doesn't, interesting indeed, well then obvy pipes don't fit here, try this approach:
#!/bin/bash
set -x
fun(){
data=$(echo "$1"); [[ $data ]] && data=$(awk '{print $0,1}' <<< "$data") || return 1; [[ $data ]] && data=$(awk '{print $0,2}' <<< "$data") || return 1; [[ $data ]] && data=$(awk '{print $0,3}' <<< "$data") || return 1; echo "$data"
}
fun ok
fun
Testing:
$ ./test
+ fun ok
++ echo ok
+ data=ok
+ [[ -n ok ]]
++ awk '{print $0,1}'
+ data='ok 1'
+ [[ -n ok 1 ]]
++ awk '{print $0,2}'
+ data='ok 1 2'
+ [[ -n ok 1 2 ]]
++ awk '{print $0,3}'
+ data='ok 1 2 3'
+ echo 'ok 1 2 3'
ok 1 2 3
+ fun
++ echo ''
+ data=
+ [[ -n '' ]]
+ return 1
More readable variant:
#!/bin/bash
set -x
fun(){
data=$(echo "$1")
[[ $data ]] && data=$(awk '{print $0,1}' <<< "$data") || return 1
[[ $data ]] && data=$(awk '{print $0,2}' <<< "$data") || return 1
[[ $data ]] && data=$(awk '{print $0,3}' <<< "$data") || return 1
echo "$data"
}
fun ok
fun

how to create multi user paramerter in zabbix from a script

This is a shell script return 2 values one for packet loss percentage and another for True or False :
SERVER_IP=$1
checkip=`ping -c 2 -W 2 $SERVER_IP | grep "packet loss" | cut -d " " -f 6 | cut -d "%" -f1`
test1=$?
echo $checkip
if [ $test1 -eq 0 ]; then
echo "1"
else
echo "0"
fi
in zabbix when you create an item you enter only one parameter with value but i have 2 values one packet loss and second for ping result (0 and 1)
how can i create two items 1 for packet lost percentage and second for ping health check with only this script? i dont want to create another one
Thanks to Andre
try this script this will guide you to what exactly you want :
#!/bin/bash
case $1 in
packetloss) ping -c2 -W1 -q 8.8.8.8 | grep -oP '\d+(?=% packet loss)' ;;
timeout) ping -c2 -q 8.8.8.8 | grep 'time' | awk -F',' '{ print$4}' | awk '{print $2}' | cut -c 1-4 ;;
*) echo "Use: packetloss , timeout";;
esac
try (im in zsh):
zabbix_agentd -t ping.loss\[timeout\]
ping.loss[timeout] [t|1000]
or in zabbix server use get ( im also in zsh here too):
zabbix_get -s 172.20.4.49 -k ping.loss\[timeout\]
1001
now create items with these keys.
UserParameter=key[*],/path_of_script.sh $1
At the GUI:
Key: key[Server_IP]
Another example:
UserParameter=general[*],/usr/local/etc/scripts/general.sh $1 $2 $3 $4 $5 $6 $7 $8 $9
$ cat general.sh
#!/bin/bash
case $1 in
ddate) ddate;;
minute) echo "`date +%M`%2" | bc;;
files) ls -l $2 | grep ^- | wc -l;;
size.dir) du -s $2 | cut -f1;;
script) /bin/bash /usr/local/etc/scripts/script.sh;;
*) echo "Use: ddate, minute, files <parameters>, size.dir <parameters> or script";;
esac
$ zabbix_get -s Server_IP -k general[minute]

Receiving conditional operator expected error in bash

Please excuse this extremely inefficient script, I am new to shell scripting. I am receiving an error near the if clause in the function matchFS(). I have posted the error down below. Can anyone offer me some guidance?
#!/bin/bash
function matchFS() {
usage=$(df -h | tail -n +2 | awk '{print $5}' | sed 's/%//g')
usagearr=( $usage )
for i in "${usagearr[#]}"
do
if [[ $1 eq "${usagearr[$i]}" ]]; then
# print matching row from df -h
fi
done
}
usage=$(df -h | tail -n +2 | awk '{print $5}' | sed 's/%//g')
usagearr=( $usage )
len=${#usagearr[#]}
for (( i=0; i<$len; i++ )) # we have to use (( )) here to represent the c style for loop
do
if [ "${usagearr[$i]}" -gt "10" ]; then
matchFS ${usagearr[$i]}
fi
done
Error: line 13: conditional binary operator expected
line 13: syntax error near eq'
line 13: if [[ $1 eq "49 ]]; then'
If you look at help test you'll quickly realize that eq is not one of the choices. At least, not without adding something else to it.
#!/bin/bash
function matchFS() {
### duplicate definition, these are already known to the function.
usage=$(df -h | tail -n +2 | awk '{print $5}' | sed 's/%//g')
usagearr=( $usage )
### you probably did want to use another variable here,
### because the "i" is also shared with the caller
for i in "${usagearr[#]}"
do
### -eq instead of eq
if [[ $1 -eq "${usagearr[$i]}" ]]; then
### the if statement can not be empty
# print matching row from df -h
fi
done
}
usage=$(df -h | tail -n +2 | awk '{print $5}' | sed 's/%//g')
usagearr=( $usage )
len=${#usagearr[#]}
for (( i=0; i<$len; i++ )) # we have to use (( )) here to represent the c style for loop
do
if [ "${usagearr[$i]}" -gt "10" ]; then
matchFS ${usagearr[$i]}
fi
done

Trying to add options to this script, not quite working

I started with this script called wd:
cat "$#" | tr -cs '[:alpha:]' '\n' | tr '[:upper:]' '[:lower:]'
| sort | uniq -c | sort -n | awk '{print $2 " " $1}' | sort
That takes any number of files as input and prints a distribution of the words in the file like this:
wd file1 file2
blue 2
cat 3
the 5
yes 1
Now I'm trying to add 2 options to it: s and t. s causes the script to take an input file called stopwords, and deletes those words from the input file before making the distribution. t takes a number n as an argument and only outputs the top n words. Default is all words.
So, so far I have this script. Currently, my problem is when I try to use a -t 10 option for example, it tells me it cannot find the file 10, but it should be a number anyway, not a file. And, when I try to use the -s option, it simply does nothing, but does not output any error. I know this question isn't very specific, but I would appreciate any ideas on what's wrong.
#!/bin/bash
stopwords=FALSE
stopfile=""
topwords=0
while getopts s:t: option
do
case "$option"
in
s) stopwords=TRUE
stopfile="$OPTARG";;
t) topwords=$OPTARG;;
\?) echo "Usage: wd [-s stopfile] [-t n] inputfile"
echo "-s takes words in stopfile and removes them from inputfile"
echo "-t means to output only top n words"
exit 1;;
esac
done
if [ "stopwords" = FALSE ]
then
cat "$#" | tr -cs '[:alpha:]' '\n' | tr '[:upper:]' '[:lower:]'
| sort | uniq -c | sort -nr | head -n $topwords | awk '{print $2 " " $1}' | sort
else
cat "$#" | grep -v -f "$stopfile" | tr -cs '[:alpha:]' '\n' | tr '[:upper:]' '[:lower:]'
| uniq -c | sort -nr | head -n $topwords | awk '{print $2 " " $1}' | sort
fi
Usually after the while getopts loop you need to shift $((OPTIND - 1)). Following is an example I wrote before for both ksh and bash:
PROGNAME=$0
function _echo
{
printf '%s\n' "$*"
}
function usage
{
cat << END
usage: $PROGNAME [-a] [-b arg] [-h] file...
END
exit $1
}
function parseargs
{
typeset opt v
[[ $# = 0 ]] && usage 1
while getopts ":ab:h" opt "$#"; do
case $opt in
a) _echo -$opt ;;
b) _echo -$opt $OPTARG ;;
h) usage ;;
:) _echo "! option -$OPTARG wants an argument" ;;
'?') _echo "! unkown option -$OPTARG" ;;
esac
done
shift $((OPTIND - 1))
for v in "$#"; do
_echo "$v"
done
}
parseargs "$#"

Resources