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.
Screenshot of the my code
I am trying to make a shell program that tells me when a file has been created, when it has been modified, and when it has been deleted. I think I can solve this but my only issue is that I cant compare the stat values. It tells me that I have "too many arguments". Any help would be much appreciated :)
#!/bin/bash
run=yes
if [ -f $1 ]
then
while [ run=yes ]
do
time1=$(stat -c %y $1)
time2=$(stat -c %y $1)
if [ ! $time2 ]
then
echo "The file "$1" has been deleted."
run=no
elif [ $time2 -gt $time1 ]
then
echo "The file "$1" has been modified."
run=no
fi
done
else
while [ run=yes ]
do
sleep 2
if [ -f $1 ]
then
echo "The file "$1" has been created."
run=no
fi
done
fi
The output of static -c %y ... includes spaces, which is what the shell uses to separate arguments. When you then run:
if [ ! $time2 ]; then
This translates into something like:
if [ ! 2017-09-02 08:57:19.449051182 -0400 ]; then
Which is an error. The ! operator only expects a single argument. You could solve it with quotes:
if [ ! "$time2" ]; then
Or by using the bash-specific [[...]]] conditional:
if [[ ! $time2 ]]; then
(See the bash(1) man page for details on that second solution).
Separately, you're not going to be able to compare the times with -gt as in:
elif [ $time2 -gt $time1 ]
This (a) has the same problem as the earlier if statement, and (b) -gt can only be used to compare integers, not time strings.
If you were to use %Y instead of %y, you would get the time as an integer number of seconds since the epoch, which would solve all of the above problems.
The code is now working and I thought I would share the final result if anyone wanted to know.
#!/bin/bash
run=true
if [ -f $1 ]
then
while [ "$run" = true ]
do
time1=$(stat -c %Y $1 2>/dev/null)
sleep $2
time2=$(stat -c %Y $1 2>/dev/null)
if [ ! "$time2" ]
then
echo "The file "$1" has been deleted."
run=false
elif [ $time2 -gt $time1 ]
then
echo "The file "$1" has been modified."
run=false
fi
else
while [ "$run" = true ]
do
sleep 2
if [ -f $1 ]
then
echo "The file "$1" has been created."
run=false
fi
done
fi
I'm try to run this bash script.
#!/bin/sh
MAX=5
j=1
while [ $((1+$j)) -le $MAX ] do
input=$j
if [ $input -le $j ] then
echo "input=$j,$j,$((j+1)),$((j+2)),$((j+3)),$((j+4))"
else
echo "$input"
fi j=$((j+1))
done
I am writing a bash script and trying to check the order list provided in the argument of the shell values. The ouput has the content as:
input=1,1,2,3,4,5
input=2,2,3,4,5,6
input=3,3,4,5,6,7
input=4,4,5,6,7,8
As what I expect it should give the list in increase order at the each line but the result that i'm looking for is:
input=1,2,3,4,5
input=2,1,3,4,5
input=3,1,2,4,5
input=4,1,2,3,5
input=5,1,2,3,4
Please help me, thanks.
In your script, when you are iterating on variable j, only the while loop is keeping track of the range {1 .. MAX}. Hence, if you are at j=5 in your loop, then running echo on $j,$((j+1)),$((j+2)),$((j+3)),$((j+4)) results 5,6,7,8,9 respectively, which is not what you are looking for.
One approach at a solution is, given a number i, creating a range {1 .. MAX} with i removed. For example, given i=2, creating the list 1,3,4,...,MAX. This can then be concatenated to the final output format as echo "input=$i,$list".
The following range routine creates such a list:
# range() outputs a range of numbers 1 to MAX, but with
# the number 'num' removed from the range.
# Usage: range num MAX
# Example: [ input: range 2 5 ] [ output: 1,3,4,5 ]
range() {
num="$1"
MAX="$2"
for i in $(eval echo {1..$MAX}); do
if [ "$num" -eq "$MAX" ]; then
if [ "$i" -eq $((MAX-1)) ]; then
printf "$i"
break
else
printf "$i,"
fi
elif [ "$i" -eq "$MAX" ]; then
printf "$i"
elif [ "$i" -eq "$num" ]; then
continue
else
printf "$i,"
fi
done
printf "\n"
}
Then your while loop becomes,
j=1
MAX=5
while [ "$j" -le "$MAX" ]; do
list=$(range "$j" "$MAX")
echo "input=$j,$list"
j=$((j+1))
done
whereby list variable is assigned the values of the range created with range "$j" "$MAX", then list is concatenated to final output.
Tests: Assuming the above script is named permute,
# when j=1 and MAX=5
$ ./permute
input=1,2,3,4,5
input=2,1,3,4,5
input=3,1,2,4,5
input=4,1,2,3,5
input=5,1,2,3,4
# when j=1 and MAX=10
$ ./permute
input=1,2,3,4,5,6,7,8,9,10
input=2,1,3,4,5,6,7,8,9,10
input=3,1,2,4,5,6,7,8,9,10
input=4,1,2,3,5,6,7,8,9,10
input=5,1,2,3,4,6,7,8,9,10
input=6,1,2,3,4,5,7,8,9,10
input=7,1,2,3,4,5,6,8,9,10
input=8,1,2,3,4,5,6,7,9,10
input=9,1,2,3,4,5,6,7,8,10
input=10,1,2,3,4,5,6,7,8,9
My sample code is here
#!/bin/bash
file="output2.txt"
numbers="$(cut -d',' -f2 output2.txt)"
lines="$(cut -f2 output2.txt)"
hours="$(cut -d',' -f1 output2.txt)"
array_numbers=( $numbers )
lines_array=( $lines )
hours_array=( $hours )
difference=$1
let range=$1-1000
for (( i = 0 ; i < ${#array_numbers[#]} ; i++ ))
do
let num=$(( 10#${array_numbers[$i+1]} - 10#${array_numbers[$i]} ))
if [ $num -gt $1 ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -ge 0 ] && [ $num -lt $range ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -le $1 ]
then
if [${hours_array[$i+1]} != ${hours_array[$i]}]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than one second"
fi
fi
done
I'm working with the same output2.txt again:
12:43:40,317
12:43:40,318
12:43:40,332
12:43:40,333
12:43:40,334
12:43:40,335
12:43:40,336
12:43:40,337
12:43:40,338
12:43:40,339
12:43:40,353
12:43:40,354
12:43:40,356
12:43:40,358
12:43:40,360
12:43:40,361
12:43:40,362
12:43:40,363
12:43:40,364
12:43:40,365
12:43:40,382
12:43:40,384
12:43:40,385
12:43:40,387
12:43:40,388
12:43:40,389
12:43:40,390
12:43:40,391
12:43:40,404
12:43:40,405
12:43:40,406
12:43:40,407
12:43:40,408
12:43:40,409
12:43:40,410
12:43:40,412
12:43:40,413
12:43:40,414
12:43:40,415
12:43:40,428
12:43:40,429
12:43:40,431
12:43:40,432
12:43:40,433
12:43:40,434
12:43:40,435
12:43:40,436
12:43:40,437
12:43:40,438
12:43:40,440
12:43:40,443
12:43:40,458
12:43:40,459
12:43:40,460
12:43:40,461
12:43:40,462
12:43:40,463
12:43:40,464
12:43:40,465
12:43:40,466
12:43:40,479
12:43:40,480
12:43:40,481
12:43:40,482
12:43:40,483
12:43:40,484
12:43:40,485
12:43:40,486
12:43:40,487
12:43:40,501
12:43:40,503
12:43:40,504
12:43:40,505
12:43:40,506
12:43:40,509
12:43:40,510
12:43:40,511
12:43:40,512
12:43:40,513
12:43:40,514
12:43:40,515
12:43:40,517
12:44:40,518
What I want to do is take the difference as parameter and if there is a value difference more than 100 miliseconds than I'm wanna print output. The parts
for (( i = 0 ; i < ${#array_numbers[#]} ; i++ ))
do
let num=$(( 10#${array_numbers[$i+1]} - 10#${array_numbers[$i]} ))
if [ $num -gt $1 ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
elif [ $num -ge 0 ] && [ $num -lt $range ]
then
echo ${lines_array[$i+1]} "and" ${lines_array[$i]} "has a difference more than $1"
are actually working well , but i realized that if input has such a columns in order like the last part
12:43:40,517
12:44:40,518
it won't print anything so i put the last elif statement to my code but even it prints hours_array good, it doesn't work with while i'm comparing them. The output is always :
script.sh: line 22: [12:43:00: command not found
Why doesn't it accept this compare or is the problem is about my bash version ?
Thank you in advance for your help.
Add space before and after [. It is an 'alias' to the test buitin command.
You should also add double quote " around your variable. Because if they are empty, bash won't recognize them as a empty word.
And I generally use double brackets [[ for test condition which is more safer and has more features.
Example:
if [[ "${hours_array[$i+1]}" != "${hours_array[$i]}" ]]
You need a space here (the [ is a command)
if [ ${hours_array[$i+1]} != ${hours_array[$i]} ]
Missing space after [. [ is a command, so it needs to be separated from its arguments.
if [ ${hours_array[$i+1]} != ${hours_array[$i]} ]
I find few things that can be changed in this code.
Add space after [ and before ].
Add double quotes so that in case if variable is empty, script does not throw error.
if [ "${hours_array[$i+1]}" != "${hours_array[$i]}" ]
Also when you reach the last line, $i + 1 will fail. Hence, following would be better.
for (( i = 0 ; i < ${#array_numbers[#]} - 1 ; i++ ))
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.