Error in script - bash

I am new to bash and scripting and I am trying to create a simple script but for some reason it won't let me run this:
fileCount= ls -1 | wc -l
#echo $fileCount
for (( i=0; i<$fileCount; ++i )) ; do
echo item: $i
done
Whenever I try to run this it just gives me an error message saying it expected an operand.
I am really confused on the error here and any help would be greatly appreciated!

To get your code running with minimal change, replace:
fileCount= ls -1 | wc -l
With:
fileCount=$(ls -1 | wc -l)
$(...) is called command substitution. It is what you use when you want capture the output of a command in a variable.
It is very important that there be no spaces on either side of the equal sign.
Improvements
To speed up the result, use the -U option to turn off sorting.
To prevent any attempt to display special characters, use -q.
Thus:
fileCount=$(ls -1Uq | wc -l)
Lastly, when ls is writing to something other than a terminal, such as, in this command, a pipeline, it prints one file name per line. This makes -1 optional.

you missed to assign the output of wc -l to your variable. Try this:
fileCount=$(ls | wc -l)
(option "-1" is not needed, because ls writes one file per line if its stdout is not a terminal)

Related

Pass the value of a variable to a command as if it were stored in a file

I am interested in counting the number of lines in the output of ps command which I can do with
echo "$(ps | wc -l)"
However, I now have the output of ps command stored in a variable by doing X="$(ps)". How do I pass X to wc -l command without using a pipe? I tried $(wc -l < "$X") but it didn't work. I read the man page for wc and it takes a file as an argument. So I guess another way to frame the question would be - How do I treat value of a variable as a file to pass as an argument to a command in bash script?
I am fairly new to bash scripting and keywords I tried to search with didn't give clear answer to my questions.
I suggest:
echo "$X" | wc -l
or
wc -l <<< "$X"

BASH tail logfile and send mail on matched condition

Completely clueless with Bash, but trying to learn to accomplish this single task.
I need a script that can tail the last 50 lines of a logfile, looking for the string "ERROR", and send an email if that condition is true.
I can do bits and pieces manually, but am not able to build something that works completely to stick in a cron. Sendmail works by itself.
tail -n 50 /var/log/unifi-video/recording.log | grep ERROR
works to at least output that. Ideally I'd like an email only if ERROR is found in the last 50 lines, else no action needs to be taken. If anyone can assist with the if/else statement to make this happen, it would be greatly appreciated.
If you are looking to do this in a one liner you could use something like:
[ $(tail -n 50 /var/log/unifi-video/recording.log | grep ERROR | wc -l) -gt 0 ] && yourmailcommand
This just pipes the output of your grep to wc -l which returns how many lines are returned from grep. If that count is greater than 0 then it will execute that bit after the double ampersands. If you want to include an else in the event that no ERROR lines are found you could:
[ $(tail -n 50 /var/log/unifi-video/recording.log | grep ERROR | wc -l) -gt 0 ] && yourmailcommand || dosomethingelseinstead
grep exits with status 0 if a match is found, 1 if not, so something like
tail -n 50 /var/log/unifi-video/recording.log | grep -q ERROR && <sendmail command here>
would do what you want. (The -q option suppresses any output, because you don't care what line actually matched, just that a match was found.)

Understanding the use of command substitution in bash

I would like to know what the difference between the two commands below is ?
ubuntu:~/bin$ (ls -A1 /home/ | wc -l)
1
ubuntu:~/bin$ $(ls -A1 /home/ | wc -l)
1: command not found
If I put dir_count=(ls -A1 /home/ | wc -l) in a script i get the following error.
./two_args: line 24: syntax error near unexpected token `|'
./two_args: line 24: `dir_1_count=(ls -A1 "$dir_1" | wc -l)'
where as the following works:
dir_count=$(ls -A1 /home/ | wc -l)
$(command), is command substitution. It simply executes the command and substitutes the standard output of the command.
So if you want to set the variable, simply: dir_count=$(ls -A1 /home/ | wc -l)
About the rest of your code:
(ls -A1 /home/ | wc -l)
this one executes the command in a subshell. You probably don't want those parentheses.
$(ls -A1 /home/ | wc -l)
this one just doesn't make any sense, you substitute the result, so you get 1, and the shell will try to execute the command called 1.
To simplify the variable management in the script, I recommend you try the following:
dir_1_count=`ls -A1 ${dir1} | wc -l`
Use the character "`" to delimitate variables and store the result of a usual shell command in your script.
Remember to use the inverted apostrophe, not the single quote, copy the variable definition I just gave you.
(...) is just a special grouping operator. It runs ... in a subshell, meaning that it cannot modify the parent's execution environment (so, for example, (foo=bar) is useless, because the assignment will not survive past the end of the command), but is otherwise treated pretty normally (its standard output goes to standard output, etc.).
$(...) is a substitution; just like how $foo gets replaced with the value of the variable foo, $(...) gets replaced with the output of the command .... More precisely . . . like (...), it also runs ... in a subshell, but in addition, it captures the standard output of ..., and then ends up getting replaced with that output. So, for example, this:
"$(echo cd)" "foo$(echo bar)"
runs echo cd and captures the cd, and runs echo bar and captures the bar, and then runs the combined command cd foobar.

Output number of lines in a text file to screen in Unix [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
bash echo number of lines of file given in a bash variable
Was wondering how you output the number of lines in a text file to screen and then store it in a variable.
I have a file called stats.txt and when I run wc -l stats.txt it outputs 8 stats.txt
I tried doing x = wc -l stats.txt thinking it would store the number only and the rest is just for visual but it does not work :(
Thanks for the help
There are two POSIX standard syntax for doing this:
x=`cat stats.txt | wc -l`
or
x=$(cat stats.txt | wc -l)
They both run the program and replace the invocation in the script with the standard output of the command, in this case assigning it to the $x variable. However, be aware that both trim ending newlines (this is actually what you want here, but can be dangerous sometimes, when you expect a newline).
Also, the second case can be easily nested (example: $(cat $(ls | head -n 1) | wc -l)). You can also do it with the first case, but it is more complex:
`cat \`ls | head -n 1\` | wc -l`
There are also quotation issues. You can include these expressions inside double-quotes, but with the back-ticks, you must continue quoting inside the command, while using the parenthesis allows you to "start a new quoting" group:
"`echo \"My string\"`"
"$(echo "My string")"
Hope this helps =)
you may try:
x=`cat stats.txt | wc -l`
or (from the another.anon.coward's comment):
x=`wc -l < stats.txt`

bash echo number of lines of file given in a bash variable without the file name

I have the following three constructs in a bash script:
NUMOFLINES=$(wc -l $JAVA_TAGS_FILE)
echo $NUMOFLINES" lines"
echo $(wc -l $JAVA_TAGS_FILE)" lines"
echo "$(wc -l $JAVA_TAGS_FILE) lines"
And they both produce identical output when the script is run:
121711 /home/slash/.java_base.tag lines
121711 /home/slash/.java_base.tag lines
121711 /home/slash/.java_base.tag lines
I.e. the name of the file is also echoed (which I don't want to). Why do these scriplets fail and how should I output a clean:
121711 lines
?
An Example Using Your Own Data
You can avoid having your filename embedded in the NUMOFLINES variable by using redirection from JAVA_TAGS_FILE, rather than passing the filename as an argument to wc. For example:
NUMOFLINES=$(wc -l < "$JAVA_TAGS_FILE")
Explanation: Use Pipes or Redirection to Avoid Filenames in Output
The wc utility will not print the name of the file in its output if input is taken from a pipe or redirection operator. Consider these various examples:
# wc shows filename when the file is an argument
$ wc -l /etc/passwd
41 /etc/passwd
# filename is ignored when piped in on standard input
$ cat /etc/passwd | wc -l
41
# unusual redirection, but wc still ignores the filename
$ < /etc/passwd wc -l
41
# typical redirection, taking standard input from a file
$ wc -l < /etc/passwd
41
As you can see, the only time wc will print the filename is when its passed as an argument, rather than as data on standard input. In some cases, you may want the filename to be printed, so it's useful to understand when it will be displayed.
wc can't get the filename if you don't give it one.
wc -l < "$JAVA_TAGS_FILE"
You can also use awk:
awk 'END {print NR,"lines"}' filename
Or
awk 'END {print NR}' filename
(apply on Mac, and probably other Unixes)
Actually there is a problem with the wc approach: it does not count the last line if it does not terminate with the end of line symbol.
Use this instead
nbLines=$(cat -n file.txt | tail -n 1 | cut -f1 | xargs)
or even better (thanks gniourf_gniourf):
nblines=$(grep -c '' file.txt)
Note: The awk approach by chilicuil also works.
It's a very simple:
NUMOFLINES=$(cat $JAVA_TAGS_FILE | wc -l )
or
NUMOFLINES=$(wc -l $JAVA_TAGS_FILE | awk '{print $1}')
I normally use the 'back tick' feature of bash
export NUM_LINES=`wc -l filename`
Note the 'tick' is the 'back tick' e.g. ` not the normal single quote

Resources