bash test for number in range (with float numbers) - linux

the problem is in this line
elif [ "$(echo $a '>' $v06 | bc -l)" -eq 1 && "$(echo $a '<' $v08 | bc -l)" -eq 1 ];then
How can I test if the number is in range ?
I know that there might be other solution for this but I want to solve in this way with bc...
a=0.1
v06=0.6
v08=0.8
if [ "$(echo $a '<' $v06 | bc -l)" -eq 1 ];then
echo " <0.6"
elif [ "$(echo $a '>' $v06 | bc -l)" -eq 1 && "$(echo $a '<' $v08 | bc -l)" -eq 1 ];then
echo " <0.6 >0.8"
else
echo ">1.5"
fi

Actually the only thing wrong is you're using && inside [ ] which does not work.
Only [[ ]] can contain && for multiple test conditions.
You can put the && outside instead, like this
elif [ "$(echo $a '>' $v06 | bc -l)" -eq 1 ] && [ "$(echo $a '<' $v08 | bc -l)" -eq 1 ]; then
or even better,
elif [ $(echo "($a > $v06) && ($a < $v08)" | bc -l) -eq 1 ]; then
since bc actually supports the (num1 > num2) && (num1 < num3) notation
p/s: You don't need the quotes around the $( ) since you're only expecting 0 or 1 as return value
p/s2: You can have quotes within $( ) even if it's enclosed in quotes e.g.
"$(echo "1 + 2" | bc -l )"

you can strip the "0." and use the -gt and -lt operators:
v06="$(echo $v06 | sed 's/0.//')"
v08="$(echo $v08 | sed 's/0.//')"
elif [ $a -gt $v06 -a $a -lt $v08 ];then

If you really want to use bc:
test1=$(bc -l <<< "${a} < ${v06}")
test2=$(bc -l <<< "${a} > ${v06}")
test3=$(bc -l <<< "${a} < ${v08}")
if [[ ${test1} -eq 1 ]]; then
blabla
elif [[ ${test1} -eq 1 && ${test2} -eq 1 ]]; then
blabla
fi
I recommend to use braces around a variable (so you are sure 06 and 08 won't be append to $v), use double brackets in your tests if you are using operators like &&.

Related

Problem with bash script that show volume level

I trying to make script that shows volume level of active volume sink. Here is code
#!/bin/bash
active_sink=$(pacmd list-sinks |awk '/* index:/{print $3}')
muted=$(pactl list sinks | perl -000ne 'if(/#${active_sink}/){/(Mute:.*)/; print "$1\n"}' | cut -f2 -d':' | cut -f2 -d' ')
vol=$(pactl list sinks | perl -000ne 'if(/#${active_sink}/){/(Volume:.*)/; print "$1\n"}' | cut -f1 -d'%' | tail -c 3
)
if [[ $muted = "no" ]]; then
if [[ $vol -ge 65 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-high.svg
echo "$vol%"
elif [[ $vol -ge 40 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-medium.svg
echo "$vol%"
elif
[[ $vol -ge 0 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-low.svg
echo "$vol%"
fi
else
echo ~/.config/tint2/executors/icons/audio-volume-muted.svg
echo "muted"
fi
I can't get this script to work, always get "muted".
I finally makes this thing to work, thanks #markp-fuso.
The problem was with perl and ${active_sink}.
Final code that work with corrected muted and vol
#!/bin/bash
active_sink=$(pacmd list-sinks | awk '/* index:/{print $3}')
muted=$(pactl list sinks | grep -A14 -P "(\#|№)$active_sink" | awk '/Mute:/{print $2}')
vol=$(pactl list sinks | grep -A14 -P "(\#|№)$active_sink" | awk '/Volume: front-left:/{print $5}' | cut -f1 -d'%')
if [[ $muted = "no" ]]; then
if [[ $vol -ge 65 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-high.svg
echo "$vol%"
elif [[ $vol -ge 40 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-medium.svg
echo "$vol%"
elif
[[ $vol -ge 0 ]]; then
echo ~/.config/tint2/executors/icons/audio-volume-low.svg
echo "$vol%"
fi
else
echo ~/.config/tint2/executors/icons/audio-volume-muted.svg
echo "muted"
fi

Why "[ 1 > 2 ]" evaluates to True?

I have two files:
f1.txt:
1
dest/f1.txt:
1
2
When I run wc -l on both of those files in linux terminal - I get my expected results:
$ wc -l < f1.txt
$ 1
$ wc -l < dest/f1.txt
$ 2
But when I run the following .sh file:
#!/bin/bash
if [ $(wc -l < f1.txt) > $(wc -l < dest/f1.txt) ]; then
echo -e "f1 has more lines"
else
echo -e "f1 doesn't have more lines"
fi
The output is:
f1 has more lines
Can you explian how could this be possible?
You should use the -gt for integer comparison in a if clause.
If you use > or < you will end up doing ASCII alphabetic order comparison.
integer comparison
-eq
is equal to
if [ "$a" -eq "$b" ]
-ne
is not equal to
if [ "$a" -ne "$b" ]
-gt
is greater than
if [ "$a" -gt "$b" ]
-ge
is greater than or equal to
if [ "$a" -ge "$b" ]
-lt
is less than
if [ "$a" -lt "$b" ]
-le
is less than or equal to
if [ "$a" -le "$b" ]
<
is less than (within double parentheses)
(("$a" < "$b"))
<=
is less than or equal to (within double parentheses)
(("$a" <= "$b"))
>
is greater than (within double parentheses)
(("$a" > "$b"))
>=
is greater than or equal to (within double parentheses)
(("$a" >= "$b"))
string comparison
=
is equal to
if [ "$a" = "$b" ]
Caution
Note the whitespace framing the =.
if [ "$a"="$b" ] is not equivalent to the above.
==
is equal to
if [ "$a" == "$b" ]
This is a synonym for =.
Note
The == comparison operator behaves differently within a double-brackets test than within single brackets.
[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
[ $a == z* ] # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
!=
is not equal to
if [ "$a" != "$b" ]
This operator uses pattern matching within a [[ ... ]] construct.
<
is less than, in ASCII alphabetical order
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
Note that the "<" needs to be escaped within a [ ] construct.
>
is greater than, in ASCII alphabetical order
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
Note that the ">" needs to be escaped within a [ ] construct.
-z
string is null, that is, has zero length
String='' # Zero-length ("null") string variable.
if [ -z "$String" ]
then
echo "\$String is null."
else
echo "\$String is NOT null."
fi # $String is null.
-n
string is not null.
Source: http://tldp.org/LDP/abs/html/comparison-ops.html
[ is also a command in Bash so [ 1 > 2 ] is the same as [ 1 ] > 2 which would succeed and create a file named 2.
As others pointed out you need to use the following syntax to compare integers:
[ 1 -gt 2 ]
[[ 1 -gt 2 ]]
(( 1 > 2 ))
Try with this code:
#!/bin/bash
if [ $(wc -l < f1.txt) -gt $(wc -l < dest/f1.txt) ]; then
echo -e "f1 has more lines"
else
echo -e "f1 doesn't have more lines"
fi
The -gt will proceed with a numeric compare and not ASCII.

Possible to turn 10 similar functions into a for loop?

I am trying to pipe information from my workspaces into another command, and because each workspace contains the same information, with only a different number to identify it, I have written 10 functions where the only difference is one character inside some variable names. I feel like this can be greatly simplified, but I cannot figure out how to get any sort of loop working in my situation.
All here is my script containing the 10 functions:
#!/bin/bash
# Include config file.
. $(dirname $0)/config
getWorkspaceInfo(){
filledWorkspaces=$(i3-msg -t get_workspaces | grep -Po '"'"name"'"\s*:\s*"\K([^"]*)')
currentWorkspace=$(i3-msg -t get_outputs | sed 's/.*"current_workspace":"\([^"]*\)".*/\1/')
}
# Determine the status of each workspace. Current is green, unfocused is white, empty is grey.
workspace1(){
if [[ ${currentWorkspace} -eq 1 ]]; then
workspace1Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "1") == "" ]]; then
workspace1Color=${grey}
else
workspace1Color=${foreground}
fi
echo "%{F${workspace1Color}}${workspace1Name}"
}
workspace2(){
if [[ ${currentWorkspace} -eq 2 ]]; then
workspace2Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "2") == "" ]]; then
workspace2Color=${grey}
else
workspace2Color=${foreground}
fi
echo "%{F${workspace2Color}}${workspace2Name}"
}
workspace3(){
if [[ ${currentWorkspace} -eq 3 ]]; then
workspace3Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "3") == "" ]]; then
workspace3Color=${grey}
else
workspace3Color=${foreground}
fi
echo "%{F${workspace3Color}}${workspace3Name}"
}
workspace4(){
if [[ ${currentWorkspace} -eq 4 ]]; then
workspace4Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "4") == "" ]]; then
workspace4Color=${grey}
else
workspace4Color=${foreground}
fi
echo "%{F${workspace4Color}}${workspace4Name}"
}
workspace5(){
if [[ ${currentWorkspace} -eq 5 ]]; then
workspace5Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "5") == "" ]]; then
workspace5Color=${grey}
else
workspace5Color=${foreground}
fi
echo "%{F${workspace5Color}}${workspace5Name}"
}
workspace6(){
if [[ ${currentWorkspace} -eq 6 ]]; then
workspace6Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "6") == "" ]]; then
workspace6Color=${grey}
else
workspace6Color=${foreground}
fi
echo "%{F${workspace6Color}}${workspace6Name}"
}
workspace7(){
if [[ ${currentWorkspace} -eq 7 ]]; then
workspace7Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "7") == "" ]]; then
workspace7Color=${grey}
else
workspace7Color=${foreground}
fi
echo "%{F${workspace7Color}}${workspace7Name}"
}
workspace8(){
if [[ ${currentWorkspace} -eq 8 ]]; then
workspace8Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "8") == "" ]]; then
workspace8Color=${grey}
else
workspace8Color=${foreground}
fi
echo "%{F${workspace8Color}}${workspace8Name}"
}
workspace9(){
if [[ ${currentWorkspace} -eq 9 ]]; then
workspace9Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "9") == "" ]]; then
workspace9Color=${grey}
else
workspace9Color=${foreground}
fi
echo "%{F${workspace9Color}}${workspace9Name}"
}
workspace10(){
if [[ ${currentWorkspace} -eq 10 ]]; then
workspace10Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "10") == "" ]]; then
workspace10Color=${grey}
else
workspace10Color=${foreground}
fi
echo "%{F${workspace10Color}}${workspace10Name}"
}
# Pipe functions to the bar infinitely.
while true; do
getWorkspaceInfo
echo "%{c}$(workspace1)${separator}$(workspace2)${separator}$(workspace3)${separator}$(workspace4)${separator}$(workspace5)${separator}$(workspace6)${separator}$(workspace7)${separator}$(workspace8)${separator}$(workspace9)${separator}$(workspace10)"
done | lemonbar -g ${panelWidth}x${panelHeight}+${panelX}+${bottomPanelY} -f "${font}" -f "${iconFont}" -B "${background}" -F "${foreground}" -p -d | \
while true; do read line; eval $line; done &
Here is the config file that I am importing:
#!/bin/bash
# Outside sources
xres="$HOME/.Xresources"
i3config="$HOME/.config/i3/config"
# Fetch information from Xresources
background=$(cat ${xres} | grep -i background | tail -c 8)
foreground=$(cat ${xres} | grep -i foreground | tail -c 8)
black=$(cat ${xres} | grep -i color0 | tail -c 8)
grey=$(cat ${xres} | grep -i color8 | tail -c 8)
red=$(cat ${xres} | grep -i color9 | tail -c 8)
green=$(cat ${xres} | grep -i color10 | tail -c 8)
yellow=$(cat ${xres} | grep -i color11 | tail -c 8)
blue=$(cat ${xres} | grep -i color12 | tail -c 8)
magenta=$(cat ${xres} | grep -i color13 | tail -c 8)
cyan=$(cat ${xres} | grep -i color14 | tail -c 8)
white=$(cat ${xres} | grep -i color15 | tail -c 8)
# Fetch information from i3 config
gapSize=$(cat ${i3config} | grep -i "gaps inner" | awk '{print $3}')
# Workspace names -- independant from i3 config -- workspaces in i3 config should be named numbers 1-10.
workspace1Name="Web Browser"
workspace2Name="Terminal"
workspace3Name="Text Editor"
workspace4Name="Unspecified"
workspace5Name="Unspecified"
workspace6Name="Unspecified"
workspace7Name="Unspecified"
workspace8Name="Unspecified"
workspace9Name="Messenger"
workspace10Name="Music Player"
# Fonts
font="InputSans-10"
iconFont="FontAwesome"
separator="%{F$foreground} |│| "
# Panel size
screenWidth=$(xrandr | grep 'Screen 0'| awk '{print $8}')
screenHeight=$(xrandr | grep 'Screen 0' | awk '{print $10}' | tr -d ",")
panelHeight=$((${gapSize} * 2))
panelWidth=$((${screenWidth} - ${panelHeight}))
panelX=${gapSize}
topPanelY=${gapSize}
bottomPanelY=$((${screenHeight} - ${panelHeight} - ${gapSize}))
Well, the simplest fix is to write something like:
function all_10_workspaces () {
local i
for i in {1..10} ; do
local workspaceNameVar="workspace${i}Name"
local workspaceName="${!workspaceNameVar}"
local color
if (( currentWorkspace == 1 )) ; then
color=$green
elif grep -w -q "$i" <<< "$filledWorkspaces" ; then
color=$foreground
else
color=$grey
fi
echo "%{F$color}$workspaceName"
done
}
. . . however, you should really consider using arrays instead. For example:
workspaceNames=(
'' # 0 (no such workspace)
'Web Browser' # 1
Terminal # 2
'Text Editor' # 3
Unspecified # 4
Unspecified # 5
Unspecified # 6
Unspecified # 7
Unspecified # 8
Messenger # 9
'Music Player' # 10
)
Then, for example, workspace #7 is named "${workspaceNames[7]}", and given a variable i, workspace #i is named "${workspaceNames[i]}".
Something like this, perhaps?
workspaceCount=10
while true; do
# Output will look like "%{c}$(workspace1Color)${separator}$(workspace2Color)${separator}...."
# This is what is sent before the first item in each line
itemSep="%{c}"
for i in {1..$workspaceCount}; do
if [ ${currentWorkspace} -eq $i ]; then
color="${green}"
elif [[ $(echo ${filledWorkspaces} | grep -w "1") == "" ]]; then
color="${grey}"
else
color="${foreground}"
fi
echo -n "${itemSep}${color}"
itemSep="${separator}"
done
echo # Send LF after all items
done
I figured out a way to get what I wanted using ideas from both ruakh and Phil Freed as well as something I came up with on my own. This may not be the shortest or most efficient way to solve the problem, but it is much shorter than having 10 separate functions.
#!/bin/bash
# Include config file.
. $(dirname $0)/config
getWorkspaceInfo(){
filledWorkspaces=$(i3-msg -t get_workspaces | grep -Po '"'"name"'"\s*:\s*"\K([^"]*)')
currentWorkspace=$(i3-msg -t get_outputs | sed 's/.*"current_workspace":"\([^"]*\)".*/\1/')
}
# Determine the status of each workspace. Current is green, unfocused is white, empty is grey.
workspaces(){
workspaces=""
currentSeparator="${separator}"
for i in {1..10} ; do
if [[ ${currentWorkspace} -eq ${i} ]]; then
color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "${i}") == "" ]]; then
color=${grey}
else
color=${foreground}
fi
if [[ ${i} -eq 10 ]]; then
currentSeparator=""
fi
workspaces+="%{F$color}${workspaceNames[i]}${currentSeparator}"
done
echo "${workspaces}"
}
# Pipe functions to the bar infinitely.
while true; do
getWorkspaceInfo
echo "%{c}$(workspaces)"
done | lemonbar -g ${panelWidth}x${panelHeight}+${panelX}+${bottomPanelY} -f "${font}" -f "${iconFont}" -B "${background}" -F "${foreground}" -p -d | \
while true; do read line; eval $line; done &
To explain what it does as simply as possible:
Loop through all 10 workspaces, adding what would have been the output of a single function to the end of a new variable. Since I cannot add a separator between each function call anymore, I simply added the separator to the end of the echo, making sure that no separator is added to the last workspace by using a for loop, which sets the separator variable to null.

When/how to use "==" or "-eq" operator in test?

In the following code I want to compare the command line arguments with the parameters but I am not sure what is the current syntax to compare the arguments with parameters..i.e "==" or "-eq".
#!/bin/bash
argLength=$#
#echo "arg = $1"
if [ argLength==0 ]; then
#Running for the very first
#Get the connected device ids and save it in an array
N=0
CONNECTED_DEVICES=$(adb devices | grep -o '\b[A-Za-z0-9]\{8,\}\b'|sed -n '2,$p')
NO_OF_DEVICES=$(echo "$CONNECTED_DEVICES" | wc -l)
for CONNECTED_DEVICE in $CONNECTED_DEVICES ; do
DEVICE_IDS[$N]="$CONNECTED_DEVICE"
echo "DEVICE_IDS[$N]= $CONNECTED_DEVICE"
let "N= $N + 1"
done
for SEND_DEVICE_ID in ${DEVICE_IDS[#]} ; do
callCloneBuildInstall $SEND_DEVICE_ID
done
elif [ "$1" -eq -b ]; then
if [ $5 -eq pass ]; then
DEVICE_ID=$3
./MonkeyTests.sh -d $DEVICE_ID
else
sleep 1h
callCloneBuildInstall $SEND_DEVICE_ID
fi
elif [ "$1" -eq -m ]; then
echo "Check for CloneBuildInstall"
if [ "$5" -eq pass ]; then
DEVICE_ID=$3
callCloneBuildInstall $SEND_DEVICE_ID
else
echo "call CloneBuildInstall"
# Zip log file and save it with deviceId
callCloneBuildInstall $SEND_DEVICE_ID
fi
fi
function callCloneBuildInstall {
./CloneBuildInstall.sh -d $SEND_DEVICE_ID
}
From help test:
[...]
STRING1 = STRING2
True if the strings are equal.
[...]
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
But in any case, each part of the condition is a separate argument to [.
if [ "$arg" -eq 0 ]; then
if [ "$arg" = 0 ]; then
Why not use something like
if [ "$#" -ne 0 ]; then # number of args should not be zero
echo "USAGE: "
fi
When/how to use “==” or “-eq” operator in test?
To put it simply use == when doing lexical comparisons a.k.a string comparisons but use -eq when having numerical comparisons.
Other forms of -eq (equal) are -ne (not equal), -gt (greater than), -ge (greater than or equal), -lt (lesser than), and -le (lesser than or equal).
Some may also suggest preferring (( )).
Examples:
[[ $string == "something else" ]]
[[ $string != "something else" ]] # (negated)
[[ $num -eq 1 ]]
[[ $num -ge 2 ]]
(( $num == 1 ))
(( $num >= 1 ))
And always use [[ ]] over [ ] when you're in Bash since the former skips unnecessary expansions not related to conditional expressions like word splitting and pathname expansion.

Linux bash script -

I am trying to use whether or not a line contains a date as a condition for an if statement:
if [grep -n -v '[0-9][0-9][0-9][0-9]' $line |wc -l==0]
then
...
The above returns an error. I don't necessarily need to use grep. The line processed by grep would look like:
1984 Dan Marino QB Miami Dolphins
Any help is appreciated.
if [[ $(echo $line | grep -q '[0-9][0-9][0-9][0-9]') ]]; then
# do something
fi
You can check this using bash built-ins:
re='\b[[:digit:]]{4}\b'
if [[ $line =~ $re ]] ; then
echo ok;
fi
[grep -n -v '[0-9][0-9][0-9][0-9]' $line |wc -l==0]
problem 1: [(space).....(space)] you need those spaces
problem 2: there is no [ foo==bar ] you can do something like [ $(echo "0") = "0" ] or [[ $(echo "0") == 0 ]] here the $(echo "0") is an example, you should fill with your commands.
You can just call grep with -q option and check the return value:
if [ $(grep -qv '[0-9][0-9][0-9][0-9]' $line) -eq 0 ]; then
# ...
fi
Use command substitution and proper bash syntax.
[[ "`grep -n -v '[0-9][0-9][0-9][0-9]' $line | wc -l`" -eq 0 ]]

Resources