bash execute functions based on certain word in arguments - bash

i have the following bash script contains multiple functions
#!/usr/bin/bash
#checks if the arguments is directory and extract the domain name from it
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
for x in "$#" ;do
example_fun1 $x
example_fun2 $x
done
and run as following
./script.sh ./pathtofolder/example.com/ ./pathtofolder/test.com/
Or
./script.sh example.com test.com
and working probably BUT i need to add more feature which is check if certain word based in arguments like fun1 it will execute function example_fun1 only
desired execution
./script.sh fun1 ./pathtofolder/example.com/ ./pathtofolder/test.com/
OR
./script.sh fun1 example.com test.com
Thanks

Try the following
#!/usr/bin/bash
function=""
if [[ $( echo $1 | grep fun1 ) ]]
then
function="example_fun1"
shift
elif [[ $( echo $1 | grep fun2 ) ]]
then
function="example_fun2"
shift
fi
#checks if the arguments is directory and extract the domain name from it
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
if [[ "$function" != "" ]]
then
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
"$function"
shift
done
else
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1
example_fun2
shift
done
fi
This way you can pass fun1 and execute only fun1
Or if you don't pass any of these for example both of them will be executed

Assign the first parameter to a variable, then use that when calling the function.
func="example_$1"
shift
for x in "$#"; do
"$func" "$x"
done
And your functions need to use their parameters, not a variable that's set in the main script:
example_fun1(){
ping -c 4 "$1"
}
example_fun2(){
nslookup "$1"
}

Related

Invoking the function in bash does not give the proper result

I took the code from this nice article: https://catonmat.net/tcp-port-scanner-in-bash
It uses the function scan. When I copy the code, it doesn't work for me.
I had similar issues with bash functions before and could not find the solution either.
I try to invoke the function this way directly in the script:
#!/bin/bash
scan() {
if [[ -z $1 || -z $2 ]]; then
echo "Usage: $0 <host> <port, ports, or port-range>"
return
fi
local host=$1
local ports=()
case $2 in
*-*)
IFS=- read start end <<< "$2"
for ((port=start; port <= end; port++)); do
ports+=($port)
done
;;
*,*)
IFS=, read -ra ports <<< "$2"
;;
*)
ports+=($2)
;;
esac
for port in "${ports[#]}"; do
alarm 1 "echo >/dev/tcp/$host/$port" &&
echo "port $port is open" ||
echo "port $port is closed"
done
}
scan
I also tried $(scan) - did not work as well.
The result - it does not want to scan anything ( I think there is something I should run in order to invoke the function - the code seems fine):
$ ./scan.sh google.com 22-99
Usage: ./scan.sh <host> <port, ports, or port-range>
$ ./scan.sh scan google.com 22-99
Usage: ./scan.sh <host> <port, ports, or port-range>
What am I missing? It is not the first time I've had a similar problem.
P.S. As was already suggested, scan "$1" "$2" did the job alright. However, now I encountered a similar issue when invoking this function from a different function:
function dosmth {
while test $# -gt 0; do
case "$1" in
-t)
shift
scan "$1" "$2"
shift
;;
*)
exit 1
;;
esac
done
}
dosmth $1
I also tried dosmth "$1" "$2" - the issue persists.
Right now dosmth function works alright but scan has the similar result as before it does not scan anything.
$ ./dosmt.sh -t google.com 80
Usage: -t ./dosmt.sh <host> <port, ports, or port-range>
My solution may help somebody - I changed to:
dosmth "$1" "$2" "$3"
If function run from a script, you need to pass args from script to the function
...
scan "$1" "$2"
Same goes when function run from another function, you have to pass args from "parent" function to "child" function.
fun1() {
echo "fun1 $1 $2"
}
fun2() {
fun1 "$1" "$2"
}
fun2 "$1" "$2"
Also it's a good practice to create vars with appropriate names from this args($1, $2, $3 ...) to use in functions.
#!/bin/bash
script=$0
host=$1
port_range=$2
ports=()
scan() {
if [[ -z $host || -z $port_range ]]; then
echo "Usage: $script <host> <port, ports, or port-range>"
return
fi
case $port_range in
*-*)
IFS=- read start end <<< "$port_range"
for ((port=start; port <= end; port++)); do
ports+=($port)
done
;;
*,*)
IFS=, read -ra ports <<< "$port_range"
;;
*)
ports+=($port_range)
;;
esac
for port in "${ports[#]}"; do
alarm 1 "echo >/dev/tcp/$host/$port" &&
echo "port $port is open" ||
echo "port $port is closed"
done
}
scan

Bash - build URL query string by parsing script command arguments

I don't use Bash very frequently but I need to work on a bit of Bash that has to make a curl request to a web service with a query string tail built from command arguments contained in the script command arguments variable $#. So if the command argument string is something like -e something -v -s somethingelse, then the query string that must be produced is e=something&v=blank&s=somethingelse.
At the moment my test script looks like this:
#!/bin/bash
set -e
echo "${#}"
query_string=""
for arg in "${#}" ; do
if [[ "${arg}" =~ "-" ]] ; then
query_string+="${arg}="
else
if [ -z "${arg}" ] ; then
query_string+="blank&"
else
query_string+="${arg}&"
fi
fi
done
echo "${query_string}" | tr -d '-'
and produces the incorrect output
e=something&v=s=somethingelse&
I'm not sure where I'm going wrong. Any suggestions would be appreciated.
How about:
#!/bin/bash
set -e
echo "${#}"
option=true
query_string=""
for arg in "${#}" ; do
if $option ; then
query_string+="${arg}="
option=false
else
if [[ "${arg}" =~ "-" ]] ; then
query_string+="blank&${arg}="
else
query_string+="${arg}&"
option=true
fi
fi
done
echo "${query_string::-1}" | tr -d '-'
Every iteration you have to check the previous arg to see if it was a switch, not a value:
#!/bin/bash
set -e
echo "${#}"
prev_arg=""
query_string=""
for arg in "${#}" ; do
if [[ $arg == -* ]] ; then
# Current arg is a switch and the previous one was also a switch
# which means the value for it is blank
if [[ $prev_arg == -* ]] ; then
query_string+="blank&"
fi
query_string+="${arg}="
else
query_string+="${arg}&"
fi
prev_arg="$arg"
done
echo "${query_string::-1}" | tr -d '-'
This produces the following output:
something -v -s somethingelse
e=something&v=blank&s=somethingelse

bash- reading file from stdin and arguments

So I have googled this and thought I found the answers, but it still doesnt work for me.
The program computes the average and median of rows and columns in a file of numbers...
Using the file name works:
./stats -columns test_file
Using cat does not work
cat test_file | ./stats -columns
I am not sure why it doesnt work
#file name was given
if [[ $# -eq 2 ]]
then
fileName=$2
#file name was not given
elif [[ $# -eq 1 ]]
then
#file name comes from the user
fileName=/dev/stdin
#incorrect number of arguments
else
echo "Usage: stats {-rows|-cols} [file]" 1>&2
exit 1
fi
A very simple program that accepts piped input:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
stdin
Test is as follows:
echo "This is piped output" | stdin
To put that into a script / utility similar to the one in the question you might do this:
#!/bin/sh
stdin(){
while IFS= read -r i
do printf "%s" "$i"
done
}
rowbool=0
colbool=0
for i in $#
do case "$i" in
-rows) echo "rows set"
rowbool=1
shift
;;
-cols) echo "cols set"
colbool=1
shift
;;
esac
done
if [[ $# -gt 0 ]]
then
fileName=$1
fi
if [[ $# -eq 0 ]]
then fileName=$(stdin)
fi
echo "$fileName"

getops still performs default actions when arguments are provided

I've recently started working with the getopts command in bash. I am confused as to why my script runs the dafult action "cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl" when arguments have been provided. I only want that to run if no arguments were passed to the shell script. I've used getopts:Std in perl where I was able to code somthing like:
unless ($opts{d}) {
do something...}
How would I code something like that in a shell script? Also, how would I code logic such as this:
if ($opts{c}) {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -c
}
elsif ($opts{d} {
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
My code:
#!/bin/sh
while getopts cd name
do
case $name in
c)copt=1;;
d)dopt=1;;
*)echo "Invalid arg";;
esac
done
if [[ ! -z $copt ]] #Specifies what happens if the -c argument was provided
then
echo "CSV file created!"
cat "~/bin/Temp/log.txt" | ~/bin/Scripts/vpnreport/report.pl -c
fi
if [[ ! -z $dopt ]] #Specifies what happens if the -d argument was provided
then
echo "Debug report and files created"
cat ~bin/Temp/mag.txt | ~bin/Scripts/report.pl -d
fi
if [[ ! -z $name ]] #Specifies what happens if no argument was provided
then
echo "Running standard VPN report"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl
fi
shift $(($OPTIND -1))
My Output:
[~/bin/Scripts/report]$ sh getoptstest.sh
Running standard report
[~/bin/Scripts/report]$ sh getoptstest.sh -d
Debug report and files created
Running standard report
[~/bin/Scripts/report]$
The two getopts commands are vasty different from bash to perl and I just can't seem to get the hang of the bash varient even after reading several tutorials. Any help would be greatly appreciated!
On the final run of getopts, your variable (name) will be set to "?".
#!/bin/bash
while getopts abc foo; do :; done
echo "<$foo>"
Output of the above:
$ ./mytest.sh
<?>
$ ./mytest.sh -a
<?>
Insead, use elif, which is like Perl's elsif:
if [[ ! -z $copt ]]
then
# ...
elif [[ ! -z $dopt ]]
then
# ...
else
# ...
fi
Or test if [[ -z $copt && -z $dopt ]], or so forth. Other notes:
See the official if and case documentation in the Bash manual under "Conditional Constructs".
[[ ! -z $name ]] means the same as the more-direct [[ -n $name ]].
Use #!/bin/bash instead of #!/bin/sh, or switch off of [[ in favor of [. The double square bracket (and your use thereof) is specific to bash, and rarely works with sh.
I took Jeff's answer and rewrote my script so it works now:
#!/bin/bash
while getopts cd name
do
case $name in
c)carg=1;;
d)darg=1;;
*)echo "Invalid arg";;
esac
done
#Specifies what happens if the -c argument was provided:
if [[ ! -z $carg ]]
then
if [[ -z $darg ]]
then
echo "CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -c
else
echo "Debug CSV created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -cd
fi
fi
#Specifies what happens if the -d argurment was provided:
if [[ ! -z $darg ]]
then
echo "Debug report created"
cat ~bin/Temp/log.txt | ~bin/Scripts/report.pl -d
#Specifies what happens if no argument was provided:
else
echo "Standard report created"
cat ~bin/Temp/logs.txt | ~bin/Scripts/report.pl
fi
Thank you again for your assistance!

unary operator expected with more than 1 argument

for var in "$#"
do
if test -z $var
then
echo "missing operand"
elif [ -d $var ]
then
echo "This is a directory"
elif [ ! -f $var ]
then
echo "The file does not exist"
else
basename=$(basename $var)
dirname=$(readlink -f $var)
inodeno=$(ls -i $var| cut -d" " -f1)
read -p "remove regular file $#" input
if [ $input = "n" ]
then exit 1
fi
mv $var "$var"_"$inodeno"
echo "$basename"_"$inodeno":"$dirname" >> $HOME/.restore.info
mv "$var"_"$inodeno" $HOME/deleted
fi
done
**Hello, the above code is trying to mimic the rm command in unix. Its purpose is to remove the file .
Eg if I type in bash safe_rm file1 , it works however if type in
bash safe_rm file1 file 2 , it prompts me to remove file 1 twice and gives me a unary operater expected for line 27(if [ $input = "n" ]).
Why does it not work for two files, ideally I would like it to prompt me to remove file1 and file 2.
Thanks
read -p "remove regular file $#" input
should probably be
read -p "remove regular file $var" input
That's the basic.
And this is how I'd prefer to do it:
for T in "$#"; do
if [[ -z $T ]]; then
echo "Target is null."
elif [[ ! -e $T ]]; then
echo "Target does not exist: $T"
elif [[ -d $T ]]; then
echo "Target can't be a directory: $T"
else
BASE=${T##*/}
DIRNAME=$(exec dirname "$T") ## Could be simpler but not sure how you want to use it.
INODE_NUM=$(exec stat -c '%i' "$T")
read -p "Remove regular file $T? "
if [[ $REPLY == [yY] ]]; then
# Just copied. Not sure about its logic.
mv "$T" "${T}_${INODE_NUM}"
echo "${BASE}_${INODE_NUM}:${DIRNAME}" >> "$HOME/.restore.info"
mv "${T}_${INODE_NUM}" "$HOME/deleted"
fi
fi
done

Resources