How to check the status code of a function in sh? - linux

I am converting my script from bash to dash... and have problem getting the return code of a function.
#!/bin/sh
check_ip() {
local netbit=`echo "$1" | awk -F\/ '{print $1}'`
local netmask=`echo "$1" | awk -F\/ '{print $2}'`
if case "$netbit" in
*[!.0-9]* | *.*.*.*.* | *..* | [!0-9]* | *[!0-9] ) false ;;
*25[6-9]* | *2[6-9][0-9]* | *[3-9][0-9][0-9]* | *[0-9][0-9][0-9][0-9]* ) false;;
*.*.*.* ) true ;;
* ) false ;;
esac; then
if [ ! -z "$netmask" ] ; then
if [ "$netmask" -ge 1 ] && [ "$netmask" -le 32 ] ; then
return 0
else
return 1
fi
else
return 0
fi
else
return 1
fi
}
# this is working without the [] thing.
if check_ip "$1" ; then
echo ok
else
echo no
fi
looked up the similar script from another machine, and it has no [] as #barmar suggested. all working now. thank you very much.

$? reads the exit status of the last command executed.
a() {
return 0;
}
b() {
return 1;
}
a;
echo $?;
b;
echo $?;
would return:
0
1

The if command chooses the then or else clauses based on the exit status of the command. You only use [ if you want to perform the tests defined in the test command; if you just want to test a specific command, you use it directly.
if check_ip "$ip"
then echo Good IP
else echo Bad IP
fi
It's also very unlikely that $! would be an appropriate parameter to your function. $! is the PID of the last background process that was started, not an IP.

Related

Output ordering not always the same

Context
I finally fixed my issue to have stdout & stderr to screen and a file plus having a separate file for the errors. See: Output stdout and stderr to file and screen and stderr to file in a limited environment
Issue
The problem is the ordering of lines.
Some times it is OK:
FIRST
ERROR
LAST
ERROR2
LAST2
Some times the errors are at the end:
FIRST
LAST
LAST2
ERROR
ERROR2
I can't figure out why (except maybe a semaphore underneath that... but... not sure. And if it is the case, there is no solution exception adding line numbers to each echo).
Part of the code where the problem occurs
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
Full testable code
m=0
declare -a mainArgs
if [ ! "$#" = "0" ]; then
for arg in "$#"; do
mainArgs[$m]=$arg
m=$(($m + 1))
done
fi
function containsElement()
# $1 string to find
# $2 array to search in
# return 0 if there is a match, otherwise 1
{
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
function hasMainArg()
# $1 string to find
# return 0 if there is a match, otherwise 1
{
local match="$1"
containsElement "$1" "${mainArgs[#]}"
return $?
}
function activateLogs()
# $1 = logOutput: What is the output for logs: SCREEN, DISK, BOTH. Default is DISK. Optional parameter.
{
local logOutput=$1
if [ "$logOutput" != "SCREEN" ] && [ "$logOutput" != "BOTH" ]; then
logOutput="DISK"
fi
if [ "$logOutput" = "SCREEN" ]; then
echo "Logs will only be output to screen"
return
fi
hasMainArg "--force-log"
local forceLog=$?
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
echo "Logs are configured"
elif [ "$forceLog" = "1" ] && ([ ! -t 1 ] || [ ! -t 2 ]); then
# Use external file descriptor if they are set except if having "--force-log"
echo "Logs are configured externally"
else
echo "Relaunching with logs files"
local logPath="logs"
if [ ! -d $logPath ]; then mkdir $logPath; fi
local logFileName=$(basename "$0")"."$(date +%Y-%m-%d.%k-%M-%S)
exec 4<> "$logPath/$logFileName.log" # File descriptor created only to get the underlying file in any output option
if [ "$logOutput" = "DISK" ]; then
# FROM: https://stackoverflow.com/a/45426547/214898
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
else
# FROM: https://stackoverflow.com/a/70790574/214898
{ "$0" "${mainArgs[#]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 ; } 3>&1 | tee -a "$logPath/$logFileName.log" &
fi
exit
fi
}
#activateLogs "DISK"
#activateLogs "SCREEN"
activateLogs "BOTH"
echo "FIRST"
echo "ERROR" >&2
echo "LAST"
echo "ERROR2" >&2
echo "LAST2"
Stdout 1 and errout 2 pass by different file descriptors. If is very busy then very close calls like these can get mixed up.
You could use sleep 1 between your calls which would give the system time to process each call in order, but would slow down your script.
You can sleep less than a second : for example sleep 0.5.

Port Scanner on with Bash

#!/bin/bash
host=$1
startport=$2
stopport=$3
function pingcheck
{
ping = `ping -c 1 $host | grep bytes | wc -l`
if [ $ping > 1 ]; then
echo "$host is up";
else
echo "$host is down quitting";
exit
fi
}
function portcheck
{
for ((counter=$startport; counter<=$stopport; counter++))
do
(echo > /dev/tcp/$host/$counter) > /dev/null 2>&1 && echo "$counter open"
done
}
pingcheck
portcheck
I tried testing the script by passing 127.0.0.1 1 5 to it from the terminal but all i keep getting is ping: unknown host =
127.0.0.1 is down quitting. Tried with other IP Addresses as well, I got the same output. I was following instruction from a book as I am new to shell scripting. It will be helpful if someone can tell me what I am doing wrong.
I made some comments inline:
#!/bin/bash
host=$1
startport=$2
stopport=$3
function pingcheck
{
ping=`ping -c 1 $host | grep bytes | wc -l` #Don't use spaces before and after the "="
if [ $ping -gt 1 ]; then #Don't use >, use -gt
# if [[ $ping > 1 ]]; then #Or use [[ and ]], but this won't work in all shells
echo "$host is up";
else
echo "$host is down quitting";
exit
fi
}
function portcheck
{
for ((counter=$startport; counter<=$stopport; counter++))
do
(echo > /dev/tcp/$host/$counter) > /dev/null 2>&1 && echo "$counter open"
done
}
pingcheck
portcheck
Variables in bash are always in the format:
VARNAME=VALUE
You should not put spaces in between there. VALUE could be an expression using `` or using $(). $() is usually the preferred way, because you can do $(something $(something)) and you can't do `something `something``.
The syntax of if is:
if EXPRESSION
then
something
fi
An expression is in sh always a call to an application. [ is an application usually used in ifs. You can get a really good manual of [ by doing man [. Bash has native support for [[, which isn't an application, but can do more than [.

How to return value in shell script function? [duplicate]

This question already has answers here:
Returning value from called function in a shell script
(5 answers)
Closed 6 years ago.
This is my function
validateFile()
{
echo "$1" | grep '.zip$' > /dev/null
if [ $? -eq 0 ]
then
return 1
else
return 0
fi
}
printf "\n Enter Source File1 Path: "
read file1
res=$(validateFile $file1);
echo "$res"
But nothing store in **"res"** store nothig
validateFile function verify that file is zip file or not if yes then return 2 else 0.And returned value stored in res variable. But issue is no value store in res variable.
What you want is maybe
validateFile()
{
echo "$1" | grep '.zip$' > /dev/null
if [ $? -eq 0 ]
then
# return appropriate value
return 1
else
return 0
fi
}
printf "\n Enter Source File1 Path: "
read file1
# call function
validateFile $file1
# use return code
res=$?
echo "$res"
Although shell script provides a return statement, the only thing you can return with, is the function's own exit status (a value between 0 and 255, 0 meaning "success").
Best way to return a value is to echo the same, something like below:
validateFile()
{
echo "$1" | grep '.zip$' > /dev/null
if [ $? -eq 0 ]
then
echo 1
else
echo 0
fi
}
printf "\n Enter Source File1 Path: "
read file1
res=$(validateFile $file1);
echo "$res"
$(...) invokes a subshell and captures its standard output. So you can do one of these two things:
foo() {
fooresult=1
}
foo
echo $fooresult
or this:
foo() {
echo 1
}
fooresult=$(foo)
echo $fooresult
validateFile()
{
echo "$1" | grep '.zip$' > /dev/null
if [ $? -eq 0 ]
then
echo 1
else
echo 0
fi
}
printf "\n Enter Source File1 Path: "
read file1
res=$(validateFile $file1);
echo "$res"

Bash - How to call a function in this scenario

Hello I was wondering what would be the best way break this block of code into functions and
case $# in
1) ports='1-1023'
host=$1 ;;
2) ports=$1
host=$2 ;;
*) echo 'Usage: portscan [port|range] host'
exit 1 ;;
esac
# check port range
if [ "$(echo $ports | grep '^[1-9][0-9]*-[1-9][0-9]*$')" != "" ]; then
firstport=$(echo $ports | cut -d- -f1)
lastport=$(echo $ports | cut -d- -f2)
elif [ "$(echo $ports | grep '^[1-9][0-9]*$')" != "" ]; then
firstport=$ports
lastport=$ports
else
echo "$ports is an invalid port(s) value"
exit 2
fi
# check firstport > lastport
if [ $firstport -gt $lastport ]; then
echo $firstport is larger than $lastport
exit 3
fi
**and call them in main like this****
# Check parameters and exit 1 if problem
check_parms $#
# Check port range and exit 2 if problem
check_ports $ports
# Check port order and exit 3 if problem
check_order $firstport $lastport
When you call a bash function, it too has its own $*, $#, $1, .... to reflect that invocation. So you could build a bunch small functions like
function check_parms() {
local num=$# <----------- I changed this line
case $num)
... etc
}
and then,
function check_ports() {
local ports=$1
if ...
}
etc.
I hope this gives you some ideas for refactoring. In case you were thinking of sourceing the bash script, a good idea might be to unset the functions that you defined at the end of the script.
A nice tutorial at here
In the main block, call it like any other bash function. For example:
echo "hello"
check_parms $*
check_ports $ports
echo "..."

Bash Script Variable Scope Issue

username="hello"
password="3333"
function login {
# 1 - Username
# 2 - Password
match=0
cat LoginsMaintMenu.txt | while read line; do
x=`echo $line | awk '{print $1}'`
y=`echo $line | awk '{print $2}'`
if [ "${x}" == "${1}" ] && [ "${y}" == "${2}" ]; then
echo "match"
match=1
echo $match
break
fi
done
echo $match
return $match
}
echo $username $password
login ${username} ${password}
if [ $? -eq 0 ]; then
echo "FAIL"
else
echo "success"
fi
output:
hello 3333
match
1
0
FAIL
THE PROBLEM:
I don't understand why it is echoing "fail". the "match" variable gets set to 1 inside the while loop, but for some reason once I am out of the while loop it still thinks it is the initial zero from its declaration.
I have tried doing a lot of different things, so if someone could give me something concrete to try that'd be great!
Thanks
The reason that this is not working is actually the UUOC. In bash, the right side of a pipeline is ran inside of a sub-shell. Any variables set inside of a sub shell will not be set in the parent shell. To fix this, use redirection instead of a pipeline:
username="hello"
password="3333"
function login {
# 1 - Username
# 2 - Password
match=0
while read x y _; do
if [ "${x}" == "${1}" ] && [ "${y}" == "${2}" ]; then
echo "match"
match=1
echo $match
break
fi
done < LoginsMaintMenu.txt
echo $match
return $match
}
echo $username $password
if login "${username}" "${password}"; then
echo "FAIL"
else
echo "success"
fi
The while read ... part of your code (that gets its input from the cat pipe) runs in a subshell. Changes to variables inside that are not visible outside that subshell.
To work around that, change your loop to:
while read ... ; do
...
done < LoginsMaintMenu.txt

Resources