I am learning about shell scripting, particularly the shell commands test, [ ]. [[ ]] and (( )), I wrote a real simple script to test my understanding, it takes in three arguments, if all three are integers (and the second argument is lower than the third) it tests if the first argument lays in the range between the second and third. Here it is:
#!/bin/bash
#test if arg1 is contained in the range [arg2, arg3]
function isint()
{
if [[ "$1" =~ ^-?[0-9]+$ ]]; then
echo $1 is integer
return 0
else
echo $1 is not integer
return 1
fi
}
if [[ $(isint $1) && $(isint $2) && $(isint $3) && $2 -lt $3 ]]; then
if [[ $2 -le $1 && $1 -le $3 ]]; then
echo "$1 is in the range [$2,$3]"
else
echo "$1 is not in the range [$2,$3]"
fi
else
echo usage: $0 INT1 INT2 INT3
fi
Here is my problem: the preceding script doesn't work, the
if [[ $(isint $1) && $(isint $2) && $(isint $3) && $2 -lt $3 ]]
never evaluates to true, if I want to obtain the wanted result I need to change the test to:
if [[ !$(isint $1) && !$(isint $2) && !$(isint $3) && $2 -lt $3 ]]
I don't get why this is working, the way I understood the testand [[ ]] commands is that they both evaluate the expressions passed to them, 0 evaluates to true and 1 evaluates to false. In my case if the correct parameters are passed to the script, the isint calls evaluate to 0, hence true, why the need for inverting them for the script to work as I want ?
Thank you in advance.
Your issue is the $() enclosing your calls to isint. These replace the command with the standard output of the call to isint, so in this case what you're really running is:
if [[ "$1 is integer" && "$2 is integer" && "$3 is integer" && $2 -lt $3 ]]
You want to use the return values instead, so just drop the $() and move the isint's out of the brackets:
if isint "$1" && isint "$2" && isint "$3" && [[ "$2" -lt "$3" ]]
I added some quotes as well: you really should have your arguments quoted throughout the whole thing.
You're mixing up literal values with return codes. It should be this:
isint "$1" && isint "$2" && isint "$3" && [[ "$2" -lt "$3" ]]
That says, run these four commands and see if they're all true (return code == 0).
Related
Please see below example test code that I am getting false positive for:
#!/bin/bash
sub1(){
if [[ (($1 -lt 0) || "$1" == "-0") && ($3 =~ re) ]]
then
echo "Sub1: Condition is true $1 $2 $3 $4"
else
echo "Sub1: Condition is false $1 $2 $3 $4"
fi
}
sub2(){
if [[ (($1 -lt 0) || ("$1" == "-0")) && ($3 -ge 0) ]]
then
echo "Sub2: Condition is true $1 $2 $3 $4"
else
echo "Sub2: Condition is false $1 $2 $3 $4"
fi
}
nz='^-0$'
re='^[1-9]+$'
ne='^-[1-9]+$'
A=-0
B=50
C=-0
D=55
sub1 "$A" "$B" "$C" "$D"
sub2 "$A" "$B" "$C" "$D"
I am trying to pass multiple values of C (ex. 0, -0, 1, -1 etc)
Function Sub1 I am trying to check conditions using regex.
Function Sub2 I am trying to check conditions using arithmetic and string conditions.
None of them gives error but both provides false positive when you test with all possible values of C.
Any idea what's wrong in If statement?
I'd like to write a script that takes 2 parameters,
The first is parameter that could be "alpha/beta/tcp_friendliness/fast_convergence".
The Second should be a number for the alpha/beta cases and a 0/1 for the other 2.
for example: ./manage_cc alpha 512
Now i've wrote the following script that supposedly covers my cases, but it seems to go into all the conditionals. surely my syntax is broken so any help would be appreciated.
#!/bin/bash
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
if [["$1" == ""] || ["$2" == ""]]
then
echo "You need to pass a property to modify as a first parameter and a value as the second"
fi
if [["$1" == "alpha"] || ["$1" == "beta"]]
then
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
else
if [["$1" == "tcp_friendliness"] || ["$1" == "fast_convergence"]]
then
if [["$2" != "0"] && ["$2" != "1"]]
then
echo "This parameter only accepts a boolean value (0/1)"
exit 1
else
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
fi
else
echo "The only accepted values for first parameter are alpha/beta/tcp_friendliness/fast_convergence"
exit 1
fi
fi
A rewrite of your code:
#!/usr/bin/env bash
write() {
printf "%s" "$2" > "/sys/module/tcp_tuner/parameters/$1"
}
die() {
echo "$*" >&2
exit 1
}
main() {
[[ -z $2 ]] && die "You need to pass a property to modify as a first parameter and a value as the second"
case $1 in
alpha|beta)
write "$1" "$2"
;;
tcp_friendliness|fast_convergence)
if [[ "$2" == "0" || "$2" == "1" ]]; then
write "$1" "$2"
else
die "This parameter only accepts a boolean value (0/1)"
fi
;;
*) die "The only accepted values for first parameter are alpha/beta/tcp_friendliness/fast_convergence"
;;
esac
}
main "$#"
Your condition syntax is wrong. When you start a condition with [[ you have to end it with ]], not just ]. They don't nest like parentheses.
if [[ "$1" == "alpha" || "$1" == "beta" ]]
Well i need to compare all the strings as arguments of this shell script and say if all of them are equals or not so i try this
#!/bin/bash
#Ejercicio_4
if [ $# -ne 6 ]
then
echo Número de argumentos incorrecto
else
if [ $1 == $2 == $3 == $4 == $5 == $6 ]
then
echo Son iguales
else
echo No todas las palabras son iguales
fi
fi
also i try thinks like $# == $1 but this didnt work :(
As long as you're using bash, and comparing text strings, the [[..]] test is safer and more flexible. You can use && and || inside them for and/or operators. So this would work :
#!/bin/bash
#Ejercicio_4
if [ $# -ne 6 ]
then
echo Número de argumentos incorrecto
else
if [[ "$1" == "$2" && "$1" == "$3" && "$1" == "$4" && "$1" == "$5" && "$1" == "$6" ]]
then
echo Son iguales
else
echo No todas las palabras son iguales
fi
fi
Note also that "==" is not actually valid syntax in the old "[" test, although bash accepts it. You should properly only use the single-character "=" in single-bracket tests.
If you're comparing integers, however, you should use ((..)) instead.
But I strongly suggest you not follow this method, since when the number of arguments increase you will have to include a lot more conditions in the if statement which could get cumbersome. So prefer a loop and check the first arg with all others and see if they're equal, like given below :
#!/usr/bin/env bash
if [ $# -ne 6 ]
then
echo "Number of arguments are lesser than required number 6"
else
number_of_elements=$#
arguments=("$#")
first=("$1")
for (( i=1; i<number_of_elements; i++ )); do
if [[ "$first" != ${arguments[i]} ]];then
break
fi
done
if [[ $i -eq $number_of_elements ]];then
echo "All the argument strings are equal"
else
echo "Not equal"
fi
fi
The operator for string equality usually is =. And such chains aren't possible either. Check your man test.
Normally you'd use -a for "and" and check for each argument individually.
...
if [ $1 = $2 -a $1 = $3 -a $1 = $4 -a $1 = $5 -a $1 = $6 ]; then
...
i have this function that accepts 3 parameters , ech one contain of 4 numbers and a capital letter for example : "1234A"
and i want to print 1 if the second parameter is bigger than the third one and smaller than the first one ,
i wrote this function that i cutted the 4 numbers in a parameter for each parameter and the letter in diffrent paramater for each one and i began to compare
but the problem it print nothing !!
anyone know how to do things in one if statement rather than two if statements ??
what i did :
function check {
curr_letter=`echo "$1" | cut -c5`
min_letter=`echo "$3" | cut -c5`
sm_letter=`echo "$2" | cut -c5`
curr_nums=`echo "$1" | cut -c1-4`
min_nums=`echo "$3" | cut -c1-4`
sm_nums=`echo "$2" | cut -c1-4`
if [[ sm_nums -eq curr_nums && sm_letter < curr_letter ]] ; then
if [[ sm_nums -eq min_nums && sm_letter > min_letter ]] ; then
echo 1
fi
if [[ sm_nums > min_nums ]] ; then
echo 1
fi
fi
if [[ sm_nums < curr_nums ]] ; then
if [[ sm_nums -eq min_nums && sm_letter > min_letter ]] ; then
echo 1
fi
if [[ sm_nums > min_nums ]] ; then
echo 1
fi
fi
}
i get nothing when i test this in bash , i get an empty line..
this is how i tested it :
p=`check "1617B" "1617A" "0000A"` echo $p
You can omit the $ in variable names within arithmetic context ((...)).
Within [[ ... ]] you cannot omit it.
Instead of calling echo ... | cut -c..., you can easily extract substrings using Bash's very own syntax {var:start:length}.
Within a [[ ... ]] or ((...)),
use == instead of -eq.
Note however that < and > operators sort lexicographically within a [[ ... ]], but numerically in arithmetic context ((...)).
Therefore the string-valued variables (named *_letter in your example)
should be compared within [[ ... ]], the numeric variables (named *_nums in your example) should be compared within ((...)).
Like this:
function check() {
curr_letter=${1:4:1}
min_letter=${3:4:1}
sm_letter=${2:4:1}
curr_nums=${1:0:4}
min_nums=${3:0:4}
sm_nums=${2:0:4}
if (( sm_nums == curr_nums )) && [[ $sm_letter < $curr_letter ]]; then
if (( sm_nums == min_nums )) && [[ $sm_letter > $min_letter ]] ; then
echo 1
fi
if (( sm_nums > min_nums )) ; then
echo 1
fi
fi
if (( sm_nums < curr_nums )) ; then
if (( sm_nums == min_nums )) && [[ $sm_letter > $min_letter ]] ; then
echo 1
fi
if (( sm_nums > min_nums )) ; then
echo 1
fi
fi
}
Lastly, instead of p=`check "1617B" "1617A" "0000A"`; echo $p,
better write like this:
echo $(check "1617B" "1617A" "0000A")
why not just
awk '$3 <= $2 && $2 <= $1 {print 1}'
or if you need a function
check() { awk '$3 <= $2 && $2 <= $1 {print 1}' <<< "$#"; }
or
check() { awk "BEGIN{if($3 <= $2 && $2 <= $1) print 1}"; }
I'm getting an error
Syntax error: invalid arithmetic operator (error token is ".txt")
at the if statement line. I check words_in_line by doing echo $words_in_line and it outputs numbers, so I don't understand why I'm getting this error. How do I fix this?
#!/usr/bin/env bash
#Outputs the lines that match wordcount range specified by min, $1, and max, $2
function get_correct_lines_in_file() {
while read line ; do
words_in_line=$( echo "$line" | wc -w );
if [[ words_in_line -ge $1 ]] && [[ words_in_line -le $2 ]]; then #ERROR HERE
echo "$line" >> MARBLES.txt
fi
done < $1
}
#Check if $1 and $2 arguments exists- are NOT NULL
if [[ "$1" != "" ]] && [[ "$2" != "" ]]; then
for i in ${*:3}
do
#If is a file you can read
if [[ -r $i && -f $i ]]; then
echo "$i exists and is readable"
get_correct_lines_in_file "$i"
#If file doesn't exist
elif [[ ! -f $i ]]; then
echo $i >> FAILED.log
fi
done
fi
If you want your minimum and maximum values to be accessible in your function, you need to pass them through. Consider accepting three arguments in your function, and explicitly passing the function's arguments through:
get_correct_lines_in_file() {
local -a words
while read -r -a words ; do
words_in_line=${#words[#]};
if (( words_in_line >= $2 )) && (( words_in_line <= $3 )); then
printf '%s\n' "${words[*]}"
fi
done <"$1" >>MARBLES.txt
}
...and later, passing the filename to be the function's $1, the script's $1 to be the function's $2, and the script's $2 to be the function's $3:
get_correct_lines_in_file "$i" "$1" "$2"