Illegal number in shell script - bash

I am new to writing scripts and am trying to start out with a simple one. I am stumped as to why I am receiving the error: [: 13: Illegal number: count from the code below. Line 13 is the last fi.
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l
if [ count -ge 50 ]
then
if [ count -lt 100 ]
then
exit 1
fi
if [ count -ge 100 ]
then
exit 2
fi
exit 0
fi
Also is there anyway to do compound if statements like if(count >= 50 && count < 100)?

Two reasons. (1) in bash variables are untyped (could be int, could be char). In order to remove ambiguity, you can specify the type with:
declare -i count
To tell bash it should be an int. (2) you need to dereference your variables with $ to get the number back. I.E.
[ $count -lt 100 ]
(it is also good practice to quote your variables - not required, but good practice: [ "$count" -lt 100 ]. Drop a comment if you still have trouble.

The line:
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/zumigo/zlsapp.log | wc -l
probably does not do what you think it does (or you've not accurately copied and pasted your actual code into the question). If run in the middle of 2014-08-01, it runs the command "2014-08-01 12:00" with the log file as an argument and the environment variable count set to the value grep, and pipes the output from the probably non-existent command to wc -l.
When you subsequently go to test $count in the test statements, it is an empty string, which doesn't convert properly to a number.
You probably meant:
count=$(grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/zumigo/zlsapp.log | wc -l)
This captures the output of running grep on the log file and piping the output to wc -l.
If you run your script with bash -x or equivalent (the -x option usually shows the execution of the script), you should see this.

The problem is that count does not refer to the variable count; it's simply the string "count".
Change:
if [ count -ge 50 ]
to
if [ $count -ge 50 ]
and make the corresponding change elsewhere (but not in the initial assignment).
You should also use double quotes:
if [ "$count" -ge 50 ]
Also is there anyway to do compound if statements like if(count >= 50 && count < 100)?
Yes:
if [ "$count" -ge 50 -a "$count" -lt 100 ]
is likely to work, but the -a (logical and) operator is marked as obsolescent by POSIX. Instead write
if [ "$count" -ge 50 ] && [ "$count" -lt 100 ]
If you're using bash, info bash and search for the "test" command ([ is an alias for test).
And if you're using bash, you should consider using [[ ... ]] rather than [ ... ] -- or you can use (( ... )) for arithmetic expressions. See the bash documentation for more information (follow the iink or type info bash).
In addition to the missing $ signs, the first line of your script:
count=grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l
doesn't set the count variable, since you're not capturing the output of the grep ... | wc -l command. To do so:
count=$(grep "^$(date -d -30minute +'%Y-%m-%d %H:%M')" /var/log/****/zlsapp.log | wc -l)
(Yes, $(...) can be nested.)

Related

ash: -c: unknown operand

I'm trying to run a ash script that constantly checks how many characters are in a file and execute some code if it reaches at least 170 characters or if 5 seconds have passed. To do that I wanted to call wc -c, however it keeps telling me it has an unknown operand.
The code:
#!/bin/ash
while true; do
secs=5
endTime=$(( $(date +%s) + secs ))
while [ /usr/bin/wc -c < "/tmp/regfile_2" -gt 170 ] || [ $(date +%s) -lt $endTime ]; do
#more code
It's output is ash: -c: unknown operand
You want to check whether the output from wc meets a specific condition. To do that, you need to actually execute wc, just like you already do with date to examine its output.
while [ $(wc -c < "/tmp/regfile_2") -gt 170 ] ||
[ $(date +%s) -lt $endTime ]; do
# ...stuff
Notice the $(command substitution) around the wc command.
As you can see from the answer to the proposed duplicate Checking the success of a command in a bash `if [ .. ]` statement your current command basically checks whether the static string /usr/bin/wc is non-empty; the -c after this string is indeed unexpected, and invalid syntax.
(It is unclear why you hardcode the path to wc; probably just make sure your PATH is correct before running this script. There are situations where you do want to hardcode paths, but I'm guessing this isn't one of them; if it is, you should probably hardcode the path to date, too.)

Getting the line count of a file in a shell script with wc failing

my script check if the arguments are files or folders
if it is a file, he count the number of lines
after that, if the number of lines is great then 20 or less he do some instructions
the problem is in this instructionn= cat $a | wc -l
My script:
#!/usr/bin/env bash
echo 'Hello this is the test of' `date`
echo 'arguments number is ' $#
if [ $# -eq 4 ]
then
for a in $#
do
if [ -d $a ]
then
ls $a > /tmp/contenu
echo "contenu modified"
elif [ -f $a ]
then
# this instruction must set a numeric value into n
echo "my bad instruction"
n= cat $a | wc -l
echo "number of lines = " $n
# using the numeric value in a test (n must be numeric and takes the number of lines in the current file)
if [ $n -eq 0 ]
then
echo "empty file"
elif [ $n -gt 20 ]
then
echo ` head -n 10 $a `
else
cat $a
fi
else
echo "no file or directory found"
fi
done
else
echo "args number must be 4"
fi
This is the output of the execution of the incorrect instruction
my bad instruction
5
number of lines =
ExamenEx2.sh: line 19: [: -eq : opérateur unaire attendu
The line n= cat $a | wc -l is an offending instruction. Always remember that bash shell scripting is extremely case-sensitive. Your command is interpreted by the shell as having to run two separate commands
n= cat $a | wc -l
#^^ ^^^^^^^^^^^^^^
#1 2
The first part just stores an empty string to the variable n and the next prints the line count of the file stored in variable a. Notice that the shell does not throw errors for this. Because it is not violating the syntax (just the semantics are wrong). But the line count is never assigned to the variable n.
The error is seen when the conditional if [ $n -eq 0 ] is hit when you are doing a comparison with an empty variable on the LHS.
You wanted to run a command and store its output, you need command-substitution($(..)) for that. Assuming the $a contains a name of a file just do
n=$(wc -l < "$a")
Note, that I've removed the useless cat usage and piping it to wc. But wc can read from an input stream directly.
Also note that you have multiple bad practices in your script. Remember to do the following
Always double-quote the shell variables - "$#", "$#", [ -f "$a" ], [ -d "$a" ]
Don't use the `` for command-substitution, because it is not easily nestable and you might have issues related to quoting also.
You can use conditional expression [[ if you are sure if the script is running under bash in which a variable containing spaces can be used without quoting on the LHS

Unix - Count line inside two files and sum the value

I want to count how much line within 2 files. I create function to get the value for each file, and sum it.
Here is the code :
count_sus_pop()
{
wc -l < ${SCRIPTDIR}/output/${OUTPUTFILE1}_${DATE}.csv
}
count_waive_pop()
{
wc -l < ${SCRIPTDIR}/output/${OUTPUTFILE2}_${DATE}.csv
}
if [$(count_sus_pop)+$(count_waive_pop) -gt 2];
then
pop="[POPULATION]"
else
pop=""
fi
If the summation result greater than 2, assign "[POPULATION]" to variable pop, else pop is empty.
My code doesn't work. Please give me your suggestion
My suggestion is to just use:
total_lines=$(cat "${SCRIPTDIR}/output/${OUTPUTFILE1}_${DATE}.csv" "${SCRIPTDIR}/output/${OUTPUTFILE2}_${DATE}.csv" | wc -l)
if [[ $total_lines -gt 2 ]]; then
pop="[POPULATION]"
else
pop=""
fi
You have some basic syntax errors in your script - I would suggest using ShellCheck to fix those.
In terms of counting the combined number of lines, I would suggest this approach:
total_lines=$(cat "${SCRIPTDIR}/output/${OUTPUTFILE1}_${DATE}.csv" "${SCRIPTDIR}/output/${OUTPUTFILE2}_${DATE}.csv" | wc -l)
That is, use cat to combine the two files and use wc -l to obtain the total number of lines.
Your test would then be if [ "$total_lines" -gt 2 ] - note that the spaces are important.
you need spaces around [ and ]. And the test operator doesn't perform arithmetic, so you need to use the shell arithmetic operator $(( ))
if [ $(( $(count_sus_pop)+$(count_waive_pop) )) -gt 2 ]
or you can use bash's built-in `[[ ]]`` operator:
if [[ $(count_sus_pop)+$(count_waive_pop) -gt 2 ]];

Check number of lines returned by bash command

I have a command similar to this:
LIST=$(git log $LAST_REVISION..$HEAD --format="%s" | egrep -o "[A-Z]-[0-9]{1,4}" | sort -u)
Now, I need to do something if $LIST returned zero or more lines. Here's what I've tried:
if [ ! $($LIST | wc -l) -eq 0 ]; then
echo ">0 lines returned"
else
echo "0 lines returned"
fi
But it throws me an error. What's the correct syntax of doing this (with some details on the syntax used, if possible)?
To check whether a variable is empty, use test -z, which can be written several ways:
test -z "$LIST"
[ -z "$LIST" ]
with bash (or many other "modern" shells):
[[ -z $LIST ]]
I prefer the last one, as long as you're using bash.
Note that what you are doing: $($LIST | ...) is to execute $LIST as a command. That is almost certain to create an error, and guaranteed to do so if $LIST is empty.

Why is date so slow?

Anyone know a better way to do this were it is faster? This currently is slow when pushing high lines per second to this script:
#!/bin/bash
declare -A clientarray
file=$1
timer=$2
e=$(date --date "now +$timer second" +%s)
while read line
do
if [ -n "${clientarray[$line]}" ]; then
let "clientarray[$line]=clientarray[$line]+1"
echo "$line: ${clientarray[$line]}"
elif [ -z "${clientarray[$line]}" ]; then
clientarray[$line]=1
echo "$line: ${clientarray[$line]}"
fi
if [ $(date +%s) -gt $e ]; then
e=$(date --date "now +$timer second" +%s)
fi
done < <(tail -F $file | gawk -F"]" '/]/ {print $1}')
Here is an example of the lines:
someline]
someline2]
somethingidontwant
someline3]
somethingelseidontwant
someline4]
and to call the script:
bash script.sh somelogfile.log 1
If I comment out the if logic at the very end it goes really fast but with it the speed drops 2/3rds. Tested it with pv:
(this is with the if logic):
ubuntu#myhost:~/graphs$ tail -F somelogfile.log | pv -N RAW -lc >/dev/null |
> bash script.sh somelogfile.log 1 | pv -N SCP -lc >/dev/null
RAW: 2.18k 0:00:16 [ 493/s ] [ <=> ]
SCP: 593 0:00:16 [ 150/s ] [ <=> ]
(this is without)
ubuntu#myhost:~/graphs$ tail -F somelogfile.log | pv -N RAW -lc >/dev/null |
> bash script.sh somelogfile.log 1 | pv -N SCP -lc >/dev/null
RAW: 7.69k 0:00:15 [512/s] [ <=> ]
SCP: 7.6k 0:00:15 [503/s] [ <=> ]
Let me know if I am missing something on my script or testing side, especially any "DOH!"'s.
I think at this point I would love one =)
As a guess, I'd say it could be that that last if...fi block adds two non-builtin commands per iteration. Everything else in the loop is bash builtins, which execute much faster. With it, you have a call to date within the test, and another in the body of the if. In addition, date --date has to parse and evaluate that "now +$timer second" expression each time it's called, which probably isn't very speedy, given --date's generality. If I were you, I'd try reimplementing this in a scripting language with more native handling of dates/times: Perl, Ruby, Python, whatever you're comfortable with.
You also appear to have a bug:
if [ `date +%s` > $e ] ...
This says: execute the command date +%s and interpolate its output (say 12345) into another command [ 12345 > $e ] (so far so good). That command says: run the [ builtin with two arguments (12345 and ]), and redirect its standard output stream to a file named by the value of $e (uh-oh). You probably want to use -gt instead of > here.
I'm not sure what you are doing with $e, but you can print the current date using the shell builtin printf much faster than you can by calling out to date. Subprocess calls tend to be expensive. For example, if you are not on glibc2 you can do:
printf '%(%+)T\n' -1
to get exactly the output of the date command. %+ is not supported on glibc2 so you can construct something identical with other parameters, or something similar with:
printf '%(%c %Z)T\n' -1
If you need to capture and process the date somehow then you may still need a subshell call using $() but there's a decent chance it's still faster than date.

Resources