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.)
Related
I have a script that simply needs to check a timestamp with another timestamp in an if-statement.
However, it constantly returns an Illegal number
currentTime=$(($(date +%s) -d ))
cutoffTime=$((currentTime - 604800))
cutoffTime is the first variable to compare
time="$(echo $notes | grep -oP '^[0-9]{0,10}')"
time is the second variable to compare and is parsed from a string.
The if-statement looks like this
if [ $(($time)) -lt "$cuttofTime" ];
but even if I try
if [ "$time" -lt "$cuttofTime" ];
or
if [ "$time" < "$cuttofTime" ];
It will still say 'Illegal number'. I have no clue why it says illegal number when converting $time to a number in the first if-statement.
What is the proper way to do this in shell scripts and why does it state it is an illegal number?
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.)
I'm trying to execute this simple script in solaris.
I want to sort(numeric) the filenames of the files in source directory and copy the file one by one to another directory. And, I want to print a message after copying every 100 files.
#!/bin/bash
count=0
for i in `ls | sort -n`
do
cp $i ../target
count = $((count+1))
if[ $count%100 -eq 0 ]
then
echo $count files copied
sleep 1
fi
done
this is not working. I tried different things after searching in net.
I get errors like these -
syntax error at line 8: '(' unexpected.
syntax error at line 10: 'then' unexpected.
syntax error at line 13: 'fi' unexpected etc.
What is the problem with this script?
bash version - GNU bash, version 3.00.16(1)-release (sparc-sun-solaris2.10)
The basic problem with the script is spacing. You have spaces where you shouldn't have them:
(wrong) count = $((count+1))
(right) count=$((count+1))
(better) ((count++))
and you're missing spaces where you need them:
(wrong) if[ $count%100 -eq 0 ]
(right) if [ $((count % 100)) -eq 0 ]
(better) if ((count % 100 == 0))
count = $((count+1)) tries to run the command count passing it two arguments, = and the value of count+1. if[ ... tries to run the command if[ because [ is a valid word character; it doesn't automatically start a new token.
Having said all that, I'm puzzled by the unexpected ( error message. Could your bash be too old to recognize $(( syntax? Possibly. It's a very old bash.
count=$((count+1))
if [ `echo $count % 100 | bc` -eq 0 ]
Make these corrections.
Edit: Please try
count=`expr $count + 1`
I see a few errors here. First, you need double quotes around $i in case they have special characters.
Second, you shouldn't ever use
for i in `ls | sort -n`
Instead, try the following
ls -1 | sort -n | while read i
Third, change your if statement to
if ((count%5 == 0))
The (( syntax is bash is made just for integer math.
Fourth, change your counter increment to
((count++))
This is more concise. Also, the space in your version may break things. Remember, spaces matter.
I am trying to run the below logic
if [-f $file] && [$variable -lt 1] ; then
some logic
else
print "This is wrong"
fi
It fails with the following error
MyScipt.ksh[10]: [-f: not found
Where 10th line is the if condition , I have put in .
I have also tried
if [-f $file && $variable -lt 1] ; then
which gives the same error.
I know this is a syntax mistake somehwere , but I am not sure , what is the correct syntax when I am using multiple conditions with && in a if block
[ is not an operator, it's the name of a program (or a builtin, sometimes). Use type [ to check. Regardless, you need to put a space after it so that the command line parser knows what to do:
if [ -f $file ]
The && operator might not do what you want in this case, either. You should probably read the bash(1) documentation. In this specific case, it seems like what you want is:
if [ -f $file -a $variable -lt 1 ]
Or in more modern bash syntax:
if [[ -f $file && $variable -lt 1 ]]
The [ syntax is secretly a program!
$ type [
[ is a shell builtin
$ ls -l $(which [)
-rwxr-xr-x 1 root root 35264 Nov 19 16:25 /usr/bin/[
Because of the way the shell parses (technically "lexes") your command line, it sees this:
if - keyword
[-f - the program [-f
$file] - A string argument to the [-f program, made by the value of $file and ]. If $file was "asdf", then this would be asdf]
And so forth, down your command. What you need to do is include spaces, which the shell uses to separate the different parts (tokens) of your command:
if [ -f "$file" ]; then
Now [ stands on its own, and can be recognized as a command/program. Also, ] stands on its own as an argument to [, otherwise [ will complain. A couple more notes about this:
You don't need to put a space before or after ;, because that is a special separator that the shell recognizes.
You should always "double quote" $variables because they get expanded before the shell does the lexing. This means that if an unquoted variable contains a space, the shell will see the value as separate tokens, instead of one string.
Using && in an if-test like that isn't the usual way to do it. [ (also known as test) understands -a to mean "and," so this does what you intended:
if [ -f "$file" -a "$variable" -lt 1 ]; then
Use -a in an if block to represent AND.
Note the space preceding the -f option.
if [ -f $file -a $variable -lt 1] ; then
some logic
else
print "This is wrong"
fi
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.