Change exit code on a single line using Bash? - linux

I am using the savscan command but this returns 3 instead of 1 when a malware is detected, and I need to get 1 if a malware is detected, I tried the following:
$ bash -c "savscan -f -archive infectedfile.exe && if [ $? -eq 3 ]; then exit 1 ; fi"
$ echo $?
$ 0
$ bash -c "savscan -f -archive infectedfile.exe ; if [ $? -eq 3 ]; then exit 1 ; fi"
$ echo $?
$ 0
but I still get the exit code 0, I also need to run everything in one line

Personally, I'd use a function wrapper for this:
savscan() {
local retval
command savscan "$#"; retval=$?
(( retval == 3 )) && retval=1
return "$retval"
}
savscan -f -archive infectedfile.exe
...as adding more rules about how to mutate your exit status is as simple as adding additional commands inspecting and modifying retval, as you see fit.
If you for some reason insist on defining and invoking this function in a single line, this could look like:
savscan() { local retval; command savscan "$#"; retval=$?; (( retval == 3 )) && retval=1; return "$retval"; }; savscan -f -archive infectedfile.exe

Why not test the return status of bash command and adjust as needed.
bash -c "savscan -f -archive infectedfile.exe" || [ $? -ne 3 ]
My tests:
$ bash -c "savscan -f -archive infectedfile.exe" || [ $? -ne 3 ]
$ echo $?
1
$ bash -c "savscan -f -archive okfile.exe" || [ $? -ne 3 ]
$ echo $?
0

Related

Bash script - test not working as expected [duplicate]

This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
using OR in shell script
(2 answers)
Closed 7 months ago.
here is my simple script prova.sh:
#!/bin/bash
echo "\$# = [$#]"
echo "\$1 = [$1]"
if [ $# -ge 2 || $1="-h" ]
then
echo "#########################################################################"
echo -e "usage: $0 \t launches the program only"
echo -e " $0 -s\t shows the command fired and launches the program"
echo -e " $0 -so\t shows only the command fired"
echo -e " $0 -h\t shows this guide"
echo "#########################################################################"
exit 0
fi
command="ls -la"
if test "$1"="-so" || "$1"="-s"
then
echo "Fired command:"
echo $command
fi
if test "$1"!="-so"
then
$command
fi
here is the output:
$> ./prova.sh -h
$# = [1]
$1 = [-h]
./prova.sh: row 6 : [: "]" missing
./prova.sh: row6: -h=-h: command not found
Fired command:
ls -l
totale 4
-rwxrw-r--. 1 user user 632 20 lug 16.13 prova.sh
I expected just the help
you need to close every test using single brackets, the shell way, if [ $# -ge 2 ] || [ $1 = "-h" ]; then ... this will fix it. If you still use single brackets way, is safe to enclose you variables with double quotes, so if they are empty, you are safe, like this if [ "$#" -ge 2 ] || [ "$1" = "-h" ]; then ... or you can just put them inside a double bracket, and bash will handle this for you if [[ $# -ge 2 || $1 = "-h" ]]; then ...

Bash script: too many arguments in [ test ]

I have the bash script below:
#!/bin/bash
#
[ $# -eq 1 -a $1 = "--help" -o $# -eq 0 ] && {
echo Help will come here
}
When I run it:
$ ./script
./script: line 3: [: too many arguments
$ ./script --help
Help will come here
As you can see, when I don't pass parameters ( $# -eq 0 ) it fails with "too many arguments".
So, I tested it directly in terminal:
$ a=1;b=2;c=3
$ [ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 0 -o $c -eq 3 ] && echo ok
ok
$ [ $a -eq 0 -a $b -eq 0 -o $c -eq 0 ] && echo ok
$ [ $a -eq 0 -a $b -eq 2 -o $c -eq 0 ] && echo ok
$ [ $a -eq 1 -a $b -eq 2 -o $c -eq 0 ] && echo ok
ok
So, if it works perfectly in terminal why doesn't it work passing parameters?
Thanks,
Express your condition like this :
[ $# -eq 1 ] && [ "$1" = "--help" ] || [ $# -eq 0 ]
Actually, [ is a command, and the following elements in the commands are subject to word splitting. If an argument is empty (or contains whitespace and is unquoted), you can run into surprises. Using -a and -o is deprecated.
Please note that, if you want to use the && logical operator (instead of an if statement) before your echo statement, you will need to enclose the above inside braces, else the operator precedence (coupled with lazy evaluation) may yield incorrect results.
{ [ $# -eq 1 ] && [ "$1" = "--help" ] || [ $# -eq 0 ] ; } && { echo...
If you do not mind using Bash-specific syntax, you could also write :
[[ $# -eq 1 && $1 = "--help" || $# -eq 0 ]]
Note that in this case, double-quoting $1 is not required, because the [[ ]] construct is special shell syntax, not a command, and what is inside is not subject to word splitting. Because there is a single test, you do not need to enclose it inside braces before your && { echo....
Your entire expression can be simplified to:
function help () {
printf "%s\n" "help is on it's way."
}
[[ $# -eq 0 || "$*" = "--help" ]] && help ; echo "done." && exit 0 ;
This checks if the total sum of arguments is zero, or the argument(s) equals "--help". If either of those two things are true then it proceeds to the help function, otherwise echo "done" and exit.
When you are executing the script without parameter, you are getting the error because your condition is matching with blank character, see below -
$sh -x kk.sh
+ '[' 0 -eq 1 -a = --help -o 0 -eq 0 ']'
kk.sh: line 3: [: too many arguments
As you can see that there is no value to match.
When you will execute below command in your terminal -
$[ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
-bash: [: too many arguments
$a=1;b=2;c=3
$[ $a -eq 1 -a $b -eq 2 -o $c -eq 3 ] && echo ok
ok ####it is printing this value bcoz you have set the variable to match, it doesn't matter condition is wrong or right but there is something to match.
To resolve this issue you can use one if condition in the beginning to assign a dummy value if there is no value.
Try this :-
[ $# -eq 1 ] || [ $1 = "--help" ] || [ $# -eq 0 ]
This way it will automatically display if --help is provided or 1 is typed.
I think $# is creating the problem as both there is $# for first and second conditions

Can't parse a string with brace expansion operations into a command

have some problem with shell script.
In our office we set up only few commands, that available for devs when they are trying ssh to server. It is configured with help of .ssh/authorized_keys file and available command for user there is bash script:
#!/bin/sh
if [[ $1 == "--help" ]]; then
cat <<"EOF"
This script has the purpose to let people remote execute certain commands without logging into the system.
For this they NEED to have a homedir on this system and uploaded their RSA public key to .ssh/authorized_keys (via ssh-copy-id)
Then you can alter that file and add some commands in front of their key eg :
command="/usr/bin/dev.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
The user will do the following : ssh testuser#server tail testserver.example.com/2017/01/01/user.log
EOF
exit 0;
fi
# set global variable
set $SSH_ORIGINAL_COMMAND
# set the syslog path where the files can be found
PATH="/opt/syslog/logs"
# strip ; or any other unwanted signs out of the command, this prevents them from breaking out of the setup command
if [[ $1 != "" ]]; then
COMMAND=$1
COMMAND=${COMMAND//[;\`]/}
fi
if [[ $2 != "" ]]; then
ARGU1=$2
ARGU1=${ARGU1//[;\`]/}
fi
if [[ $3 != "" ]]; then
ARGU2=$3
ARGU2=${ARGU2//[;\`]/}
fi
if [[ $4 != "" ]]; then
ARGU3=$4
ARGU3=${ARGU3//[;\`]/}
fi
# checking for the commands
case "$COMMAND" in
less)
ARGU2=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
exit 1;
fi
#echo " --------------------------------- LESS $FILE"
/usr/bin/less $FILE
;;
grep)
if [[ $ARGU2 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
if [[ $ARGU1 == "" ]]; then
echo "Pls give a string to search for"
exit 1
fi
ARGU2=${ARGU2//\.\./}
FILE=$PATH/$ARGU2
/usr/bin/logger -t restricted-command -- "------- $USER Executing grep $ARGU1 \"$ARGU2\" $FILE"
if [ ! -f $FILE ]; then
echo "File doesn't exist"
/usr/bin/logger -t restricted-command -- "$USER Executing $#"
exit 1;
fi
/bin/grep $ARGU1 $FILE
;;
tail)
if [[ $ARGU1 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
ARGU1=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
/usr/bin/logger -t restricted-command -- "$USER Executing $# ($FILE)"
exit 1;
fi
/usr/bin/tail -f $FILE
;;
cat)
ARGU2=${ARGU1//\.\./}
FILE=$PATH/$ARGU1
if [ ! -f $FILE ]; then
echo "File doesn't exist"
exit 1;
fi
/bin/cat $FILE
;;
help)
/bin/cat <<"EOF"
# less LOGNAME (eg less testserver.example.com/YYYY/MM/DD/logfile.log)
# grep [ARGUMENT] LOGNAME
# tail LOGNAME (eg tail testserver.example.com/YYYY/MM/DD/logfile.log)
# cat LOGNAME (eg cat testserver.example.com/YYYY/MM/DD/logfile.log)
In total the command looks like this : ssh user#testserver.example.com COMMAND [ARGUMENT] LOGFILE
EOF
/usr/bin/logger -t restricted-command -- "$USER HELP requested $#"
exit 1
;;
*)
/usr/bin/logger -s -t restricted-command -- "$USER Invalid command $#"
exit 1
;;
esac
/usr/bin/logger -t restricted-command -- "$USER Executing $#"
The problem is next:
when i try to exec some command, it takes only first argument, if i do recursion in files by using {n,n1,n2} - it doesn't work:
[testuser#local ~]$ ssh testuser#syslog.server less srv1838.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
0
[testuser#local ~]$ ssh testuser#syslog.server less srv2010.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
11591
[testuser#local ~]$ ssh testuser#syslog.server less srv{1838,2010}.example.com/2017/02/10/local1.log |grep 'srv2010' | wc -l
0
[testuser#local ~]$ ssh testuser#syslog.server less srv{2010,1838}.example.com/2017/02/21/local1.log |grep 'srv2010' | wc -l
11591
Could someone help me, how can i parse\count command arguments to make it work?
Thank you and have a nice day!
The number of arguments for a bash script would be $#. As a quick example:
#!/bin/bash
narg=$#
typeset -i i
i=1
while [ $i -le $narg ] ; do
echo " $# $i: $1"
shift
i=$i+1
done
gives, for bash tst.sh a b {c,d}
4 1: a
3 2: b
2 3: c
1 4: d
In your script, the command to execute (cat, less, ...) gets explicitly only the second argument to the script. If you want to read all arguments, you should do something like this (note: only a hint, removed all sorts of checks etc..)
command="$1"
shift
case $command in
(grep) pattern="$1"
shift
while [ $# -gt 0 ] ; do
grep "$pattern" "$1"
shift
done
;;
esac
note: added some quotes as comment suggested, but, being only a hint, you should carefully look at quoting and your checks in your own script.
Less command working now:
case "$COMMAND" in
less)
if [[ $ARGU1 == "" ]]; then
echo "Pls give a filename"
exit 1
fi
FILES_LIST=${#:2}
FILE=(${FILES_LIST//\.\./})
for v in "${FILE[#]}";do
v=${v//[;\']/}
if [ ! -f $v ]; then
echo "File doesn't exist"
fi
/usr/bin/less $PATH/$v
done;;
tail command works too with 2 and more files, but i can't execute tail -f command on two files unfortunately.

Error in shell script for detecting packages on system

I wrote a shell script for detecting whether a package is installed or not. My script should write its name and status if it's installed. I can't figure out any problem with my code but when I run it, it doesn't execute the commands under if [ $? == 0 ] condition.
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
But the most weird thing to me is that it works if I add an echo after if statement. Looks like that:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
echo hi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
fi
So if I add an echo -n to right position in my code it will work as I want. But I just want to know what is wrong with first one?
I think in general you could be more deliberate about your return code handling. You are making assumptions about what $? is referring to that may not be valid depending on your program flow, and regardless, make the program harder to read and understand.
#!/bin/bash
dpkg -s $# &> /dev/null
installed=$?
if [ $installed -eq 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi
$? is the return status of the last executed command. 0 is successful, 1 or anything else is an error. Note:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
[ #? == 0 ] # false # returns 1 (error)
When you put echo, it works:
dpkg -s python &> /dev/null # returns 0 (OK, true)
# $? equals 0 now
[ #? == 1 ] # false # returns 1 (error)
# $? equals 1 now
echo hi # returns 0 (OK)
# $? equals 0 now
[ #? == 0 ] # true # returns 0 (OK)
You could save $? to a variable, but you don't really need the if inside the else since you already checked if #? == 1 so just put your code inside else:
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 1 ]; then
echo -e "Package \033[0;31mNOT\033[0m found." >&2
else
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
fi
If you are worried of other possible return statuses of $? (greater than one). You could rewrite your script to
#!/bin/bash
if [ "$1" == "" ]; then
echo "Please hold the line."
else
dpkg -s $# &> /dev/null
fi
if [ $? == 0 ]; then
for i in $#; do
dpkg -s $i | grep Package
dpkg -s $i | grep Status
done
else
echo -e "Package \033[0;31mNOT\033[0m found." >&2
fi

Grep for a dollar sign within backticks

I have a file like this
File name : hello.txt
{1:ABC}{2:BCD}{3:{108:20140619-2}}{4:
:97A::Hi//12345
:97A::Hi//12345
:93B::Hello//FAMT/00000,
:16S:FIN
-}{5:{CHK:BDG6789}}{S:{ABC:}{DEF:S}{WOM:ONHGRT}}
Now basically i'm checking for the existence of $ symbol and as well as :97A: AND im using the below if statement.
if [ `grep -c '\$' hello.txt` -gt 0 ] && [ `grep -c ":97A:" hello.txt` -gt 1 ]
then
echo "condition satisfied"
else
echo "condition not satisfied"
fi
if i execute this im getting condition satisifed echo statement. But id should be the other way round :( since im putting AND condition. Please help on this.
I also don't understand what you're asking, but from your code I conclude that you have troubles grepping for the dollar sign. I guess you need to escape the backslash as well if you use backticks:
$ echo 'foo$bar' > dollar.txt
$ echo 'foo_bar' > no_dollar.txt
$ [ `grep -c '\$' dollar.txt` -gt 0 ] && echo 1
1
$ [ `grep -c '\$' no_dollar.txt` -gt 0 ] && echo 1
1
$ [ `grep -c '\\$' dollar.txt` -gt 0 ] && echo 1
1
$ [ `grep -c '\\$' no_dollar.txt` -gt 0 ] && echo 1
$ [ `grep -c '\\$' no_dollar.txt` -gt 0 ] || echo 0
0
alternatively, use $() instead of backticks
Replace grep -c '\$' hello.txt by grep -c '\\$' hello.txt Then it will work as desired.
ex:
bash -x test.sh
++ grep -c '\$' hello.txt
+ '[' 0 -gt 0 ']'
+ echo 'condition not satisfied'
condition not satisfied
PS: bash -x is your friend :)
I recommend executing your grep commands in a subshell with the $ syntax, then doing the comparison. In my opinion this is the cleaner way and requires you to be less of an escape artist.
if [ $(grep -c '\$' hello.txt) -gt 0 ] && [ $(grep -c ":97A:" hello.txt) -gt 1 ]
then
echo "condition satisfied"
else
echo "condition not satisfied"
fi
For your hello.txt the output will be:
>> bash test.bash
condition not satisfied
Since there's no dollar sign in your file
[ $(grep -c '\$' hello.txt) -gt 0 ]
will test
[ 0 -gt 0 ]
and yield false, while
[ $(grep -c ':97A' hello.txt) -gt 1 ]
will test
[ 2 -gt 1 ]
and yield true. Finally, false && true will yield false and the second echo statement will be executed.
"i'm checking for the existence of $ symbol"
First condition won't match because there is no "$" sign anywhere in your input, therefore output of first grep is 0. As 0 isn't greater than 0, the result is "false". Consequently, the second clause won't be executed at all. "Condition is not satisfied" because your requirement for "satisfied" is: input contains both "$" AND ":97A:".
For a result whether grep matched any line, you don't need to count number of matches.
if grep -q '\$' file; then ...
is a way to use result of grep in a conditional statement without rube-goldbergismns
Using awk and reading the file only once:
if awk '/[$]/{d++} /:97A:/{o++} END{exit !(d && o>1)}' hello.txt; then
echo "condition satisfied"
else
echo "condition not satisfied"
fi

Resources