bash compare the numeric value of multiple columns - bash

i have a variable which it output yileds 2 columns:
echo "$SIZE_TO_SOCKET"
A 256
B 256
My gloal is basically to compare the 2 numbers and print a massage
somthing like :if they match : Success: A(256) is euqal to B(256)
or Error: A(256) is not euqal to B(256) if there is the numbers are not euqal
My code is:
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
if [[ $SOCKET_A -ne $SOCKET_B]];then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B]];then
cho "success: SOCKET_A is equal to SOCKET_B"
fi
done <<< "$SIZE_TO_SOCKET"
the code doesn't yields any output ,anyway there is properly shorter and more elegant way

input="A 256\nB 256"
arr=($(echo -e "$input"))
if [[ "${arr[1]}" -eq "${arr[3]}" ]]
then
echo "success: ${arr[0]}(${arr[1]}) is equal to ${arr[2]}(${arr[3]})"
else
echo "error: ${arr[0]}(${arr[1]}) is not equal to ${arr[2]}(${arr[3]})"
fi
Replace the input variable by your input and this script outputs either
success: A(256) is equal to B(256)
for two same numbers or
error: A(257) is not equal to B(256)
for two different numbers.

This was pretty straightforward solution I ended up with:
line1=$(echo "$SIZE_TO_SOCKET" | awk 'NR == 1')
line2=$(echo "$SIZE_TO_SOCKET" | awk 'NR == 2')
key1=$(echo "$line1" | cut -d ' ' -f 1)
value1=$(echo "$line1" | cut -d ' ' -f 2)
key2=$(echo "$line2" | cut -d ' ' -f 1)
value2=$(echo "$line2" | cut -d ' ' -f 2)
if [[ $value1 -eq $value2 ]]; then
echo "Success: $key1($value1) is equal to $key2($value2)"
else
echo "Error: $key1($value1) is not equal to $key2($value2)"
fi

What is wrong with your code?:
your code "corrected" would be:
SIZE_TO_SOCKET=$(printf '41203235360a42203235360a' | xxd -p -r)
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
if [[ $SOCKET_A -ne $SOCKET_B ]] ; then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B ]] ; then
echo "success: SOCKET_A is equal to SOCKET_B"
fi
done <<< "$SIZE_TO_SOCKET"
you needed spaces in different places like this one $SOCKET_B ]] ; then (your original code is $SOCKET_B]];then)
the output of your "corrected" code is:
error: SOCKET_A is not equal to SOCKET_B
success: SOCKET_A is equal to SOCKET_B
if you use bash -x YOUR_SCRIPT, you get:
++ printf 41203235360a42203235360a
++ xxd -p -r
+ SIZE_TO_SOCKET='A 256
B 256'
+ IFS=
+ read -r i
++ echo 'A 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'A 256'
++ awk '{print $1}'
+ SOCKET=A
+ [[ A = \A ]]
+ SOCKET_A=256
+ [[ 256 -ne '' ]]
+ echo 'error: SOCKET_A is not equal to SOCKET_B'
error: SOCKET_A is not equal to SOCKET_B
+ IFS=
+ read -r i
++ echo 'B 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'B 256'
++ awk '{print $1}'
+ SOCKET=B
+ [[ B = \A ]]
+ [[ B = \B ]]
+ SOCKET_B=256
+ [[ 256 -ne 256 ]]
+ [[ 256 -eq 256 ]]
+ echo 'success: SOCKET_A is equal to SOCKET_B'
success: SOCKET_A is equal to SOCKET_B
+ IFS=
+ read -r i
read that carefully; you have an error in your logic.
this could be the corrected version of your code:
SIZE_TO_SOCKET=$(printf '41203235360a42203235360a' | xxd -p -r)
while IFS= read -r i;do
SIZE=$(echo "$i"|awk '{print $2}')
SOCKET=$(echo "$i"|awk '{print $1}')
if [[ "$SOCKET" = "A" ]] ; then
SOCKET_A="$SIZE"
elif [[ "$SOCKET" = "B" ]] ; then
SOCKET_B="$SIZE"
fi
done <<< "$SIZE_TO_SOCKET"
if [[ $SOCKET_A -ne $SOCKET_B ]] ; then
echo "error: SOCKET_A is not equal to SOCKET_B"
elif [[ $SOCKET_A -eq $SOCKET_B ]] ; then
echo "success: SOCKET_A is equal to SOCKET_B"
fi
the output is:
success: SOCKET_A is equal to SOCKET_B
and if you use bash -x YOUR_CORRECTED_SCRIPT, you get:
++ printf 41203235360a42203235360a
++ xxd -p -r
+ SIZE_TO_SOCKET='A 256
B 256'
+ IFS=
+ read -r i
++ echo 'A 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'A 256'
++ awk '{print $1}'
+ SOCKET=A
+ [[ A = \A ]]
+ SOCKET_A=256
+ IFS=
+ read -r i
++ echo 'B 256'
++ awk '{print $2}'
+ SIZE=256
++ echo 'B 256'
++ awk '{print $1}'
+ SOCKET=B
+ [[ B = \A ]]
+ [[ B = \B ]]
+ SOCKET_B=256
+ IFS=
+ read -r i
+ [[ 256 -ne 256 ]]
+ [[ 256 -eq 256 ]]
+ echo 'success: SOCKET_A is equal to SOCKET_B'
success: SOCKET_A is equal to SOCKET_B

Related

check if a file contains two variables in a line

I am trying to check the values of my variables are exist in the file or not using if condition in Bash.
I tried is as follows, but for all the values I am getting value false.
a=-127.5256
b=24.5632
file=test.txt
-54.2565 58.9685
-127.2568 12.5890
-127.5256 24.5632
-78.9865 35.2366
I tried as follow but not working in my case:
if grep -Fxq "($a $b | bc)" $file; then
echo True
else
echo False
Is there any other way to perform the above job?
Thank you
I would prefer to use AWK.
awk -v a=-127.5256 -v b=24.5632 '{if ($1==a && $2=b) print "True"}' < input file
using bash
while IFS=" " read -r f1 f2;
do
if [ "$a" == "$f1" ] && [ "$b" == "$f2" ]
then
echo "True"
fi
done < input file
Demo:
$cat test.txt
-54.2565 58.9685
-127.2568 12.5890
-127.5256 24.5632
-78.9865 35.2366
$awk -v a=-127.5256 -v b=24.5632 '{if ($1==a && $2=b) print "True"}' test.txt
True
$echo $a $b
-127.5256 24.5632
$while IFS=" " read -r f1 f2;
> do
> if [ "$a" == "$f1" ] && [ "$b" == "$f2" ]
> then
> echo "True"
> fi
> done < test.txt
True
$

how will I fix the echo when counting chars in a string

I have an issue with the echo in the for loop, as I want to count string chars especailly for "*" it but it prints all the files in current directory.
clearvar() {
int=0
str=0
uniqchar=0
}
countstring(){
for c in $(echo "${1}" | fold -w1); do
echo "$c"
if [[ $c == [0-9] ]];then
int=$(( $int + 1 ))
elif [[ $c == [a-Z] ]];then
str=$(( $str + 1 ))
else
uniqchar=$(( $uniqchar + 1 ))
fi
done
}
while [ $# -gt 0 ];do
echo "Argument input: $1"
read -p "Input: " string
rmws=$(echo $string | tr -d " ")
mashed=$rmws$1
countstring $mashed
echo -e "int: $int\nstr: $str\nuniquechar: $uniqchar\nWhole string: $mashed"
clearvar
shift
done
Example output:
Argument input: io1
Input: fj^*23
f
j
^
file1
file2
file3
2
3
i
o
1
int: 3
str: 4
uniquechar: 4
Whole string: fj^*2wio1
it interprets as echo * instead of echo "*".
so I expect it to not print out the file names.
rmws=$(echo $string | tr -d " ")
If string=* this just executes echo * and expands the *.
The same happens in:
countstring $mashed
Both these expansions are unquoted. Quote them in double quotes. As a rule of a thumb - always use double quotes.
Also the same happens in the for loop:
for c in $(echo "${1}" | fold -w1)
the expansion, as elsewhere, is unquoted, so * expands. You have to quote. That's why the for i in $(..) is considered bad style - because such bugs happen. You can't do for i in "$(...)" because then you would iterate over one element. To iterate over lines or elements in a stream use a while IFS= read -r loop. You can print every character on each separate line with ex. sed 's/./&\n/g' and iterate over lines, or use bash extension read -n1 to read one character.
while IFS= read -r -n1 c; do
..
done <<<"$1"
The <<<"$1" is a bash's "here string".
You don't need $ in arithmetic expansion. Just:
int=$(( int + 1 ))
str=$(( str + 1 ))
uniqchar=$(( uniqchar + 1 ))
or in bash you can even do:
(( int++ ))
# and so on
Your script could become:
clearvar() {
int=0
str=0
uniqchar=0
}
countstring(){
while IFS= read -r -n1 c; do
echo "$c"
if [[ $c == [0-9] ]];then
(( int++ ))
elif [[ $c == [a-Z] ]];then
(( str++ ))
else
(( uniqchar++ ))
fi
done <<<"$1"
}
while [ $# -gt 0 ]; do
echo "Argument input: $1"
read -p "Input: " string
rmws="$(echo "$string" | tr -d " ")"
mashed="$rmws$1"
countstring "$mashed"
echo "int: $int"
echo "str: $str"
echo "uniquechar: $uniqchar"
echo "Whole string: $mashed"
clearvar
shift
done
Notes:
echo has portability issues. Prefer to use printf instead.
I prefer while (($#)); do in place of while [ $# -eq 0 ]; do.
PS. I would use tr:
countstring() {
printf "%s" "$1" | tr -cd '[0-9]' | wc -c
printf "%s" "$1" | tr -cd '[a-zA-Z]' | wc -c
printf "%s" "$1" | tr -d '[0-9a-zA-Z]' | wc -c
}

Grep command in array

For a homework assignment I have to Take the results from the grep command, and write out up to the first 5 of them, numbering them from 1 to 5. (Print the number, then a space, then the line from grep.) If there are no lines, print a message saying so. So far I managed to store the grep command in an array but this is where I've gotten stuck: Can anyone provide guidance as to how to proceed in printing this as stated above
pattern="*.c"
fileList=$(grep -l "main" $pattern)
IFS=$"\n"
declare -a array
array=$fileList
for x in "${array[#]}"; do
echo "$x"
done
you can grep options -c and -l
pattern="*.c"
searchPattern="main"
counter=1
while read -r line ; do
IFS=':' read -r -a lineInfo <<< "$line"
if [[ $counter > 5 ]]; then
exit 1
fi
if [[ ${lineInfo[1]} > 0 ]]; then
numsOfLine=""
while read -r fileline ; do
IFS=':' read -r -a fileLineInfo <<< "$fileline"
numsOfLine="$numsOfLine ${fileLineInfo[0]} "
done < <(grep -n $searchPattern ${lineInfo[0]})
echo "$counter ${lineInfo[0]} match on lines: $numsOfLine"
let "counter += 1"
else
echo "${lineInfo[0]} no match lines"
fi
done < <(grep -c $searchPattern $pattern)
If you're only allowed to use grep and bash(?):
pattern="*.c"
fileList=($(grep -l "main" $pattern))
if test ${#fileList[#]} = 0 ; then
echo "No results"
else
n=0
while test $n -lt ${#fileList[#]} -a $n -lt 5 ; do
i=$n
n=$(( n + 1 ))
echo "$n ${fileList[$i]}"
done
fi
If you are allowed to use commands in addition to grep, you can pipe the results through nl to add line numbers, then head to limit the results to the first 5 lines, then a second grep to test if there were any lines. For example:
if ! grep -l "main" $pattern | \
nl -s ' ' | sed -e 's/^ *//' | \
head -n 5 | grep '' ; then
echo "No results"
fi

Finding patterns within array

I have an array of elements and I would like to find all elements that have the following form:
$i or ${i}
Where i can be any natural number?
Can this be achieved without using AWK?
You can do this using grep if you prefer. For instance:
a=('$1' '$3' '$(4)' '5' 'a' '$a' '$1' '${52}')
for i in ${a[*]}; do
if [ $(echo "$i" | grep -E "^[$][0-9]+$") ]; then # First possible pattern
echo "$i"
elif [ $(echo "$i" | grep -E "^[$]{[0-9]+}$") ]; then # Second possible pattern
echo "$i"
fi
done
Output:
$1
$3
$1
${52}
#!/bin/bash
ARRAY=('a' '1' '$1' '${1}')
FOUND=()
for __ in "${ARRAY[#]}"; do
[[ $__ =~ ^[$]([0-9]+|[{][0-9]+[}])$ ]] && FOUND+=("$__")
done
echo "Found: ${FOUND[*]}"
Output:
Found: $1 ${1}

I'm trying to create a script that'll delete all files which have a bitrate less than 130 kbps

to do that
#!/bin/bash
find ./ -name '*.mp3' | while read -r i; do
echo "----------------------------------------"
if [ $(mp3info -x "$i" | grep Audio | awk '{print $2}') < 130 ]
then
read -p "Delete? " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
rm -f "$i" && echo "$i succesfully deleted!"
fi
fi
echo "----------------------------------------"
done
it stops with this output:
Error opening MP3: /Like A Prayer/Madonna - Act Of Contrition.mp3: No such file or directory
It looks like there is an error with the filepath, cause the leading dot is missing.
I think IFS is set to a value with ".".
Also, to compare integers, use [[ ]]:
#!/bin/bash
find ./ -name '*.mp3' | while IFS='' read -r i; do
echo "----------------------------------------"
if [[ $(mp3info -x "$i" | grep Audio | awk '{print $2}') -lt 130 ]]; then
read -p "Delete? " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -f "$i" && echo "$i succesfully deleted!"
fi
fi
echo "----------------------------------------"
done
Btw I think you have to add your file to the question:
read -p "Delete $i? " -n 1 -r

Resources