Syntax error: invalid arithmetic operator (error token is ".txt") - bash

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"

Related

Error : [Too many arguments | SHELL SCRIPT

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
...

how can i mix or and and in an if statement in bash?

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}"; }

Losing newlines when reading from stdin

I have been beating my head up about this.
I wanted to loop over a multiline string character by character in bash but was loosing all newlines. First thing I did when i didn't find any obvious error was to run shellcheck on it, it seemed fine with the program.
script.sh:
#!/usr/bin/env bash
transform_single() {
if [[ $# -ne 1 ]]; then
echo 'Error: illegal number of args' 1>&2
fi
equation=''
delim0=0
delim="$1"
while IFS= read -rn1 c; do
if [[ $delim0 -eq 0 ]] && [[ "$c" == "$delim" ]]; then
delim0=1
equation=''
elif [[ $delim0 -ne 0 ]] && [[ "$c" == "$delim" ]]; then
delim0=0
echo -n "$equation" | texmath
elif [[ $delim0 -ne 0 ]]; then
equation="$equation$c"
else
echo -n "$c"
fi
done
}
transform_single '$'
input.txt:
<newlines>
<newlines>
# Hello world!
<newlines>
This is a test string.
<newlines>
invocation:
bash script.sh < input.txt
output:
# Hello world!This is a test string.
excepted output:
The same as in the input file.
Working script
#!/bin/bash
transform_single() {
if (($# != 1)); then echo 'Error: illegal number of args' 1>&2; fi
equation=''
delim0=0
delim="$1"
while IFS= read -r -d $'\0' -n 1 c; do
if ((delim0 == 0)) && [[ "$c" == "$delim" ]]; then
delim0=1
equation=''
elif ((delim0 != 0)) && [[ "$c" == "$delim" ]]; then
delim0=0
echo -n "$equation" | texmath
elif ((delim0 != 0)); then
equation="$equation$c"
else
echo -n "$c"
fi
done
}
transform_single '$'
The issue is that you must set a delimiter to read, a null character, to preserve line feed.

Bash test value evaluation

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).

Bash, if's, reusing variables

if [[ $line == *"option 1"* ]]
then
CURRENT_OPTION=1
fi
if [[ $line == *"option 2"* ]]
then
CURRENT_OPTION=2
fi
if [[ $line =~ "What i want" ]]
then
if [[ $CURRENT_OPTION -eq 1 ]]
then
MEM1=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
elif [[ $CURRENT_OPTION -eq 2 ]]
then
MEM2=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
fi
fi
Because CURRENT_OPTION is defined within an if, its value is not correct when checked in the third if. How do I pass it out so that it is?
Just declare CURRENT_OPTION at the top, something like:
declare -i CURRENT_OPTION=0
i to declare it as an int.
In all of your if statements you should enclose the variables in double quotes. If the variable is an empty string (or if the variable doesn't exist) then the if statement will not contain enough arguments and will throw an error.
Here is an example:
if [[ $var -eq 1 ]]
then
echo yes
else
echo no
fi
If var is uninitialised, bash will expand the statement to look like this:
if [[ -eq 1 ]]
then
echo yes
else
echo no
fi
There are not enough arguments to make the if statement valid here, and bash will throw an error:
bash: conditional binary operator expected
bash: syntax error near `1'
By wrapping the variable in quotes, this situation is avoided. This statement:
if [[ "$var" -eq 1 ]]
...
is expanded to:
if [[ "" -eq 1 ]]
...
and now the if statement has enough arguments (the first one being an empty string) to parse.

Resources