How to use wc -l integer output in a if statement - bash

I am trying to execute the command git diff | grep pkg/client/clientset | wc -l and check if the output is more than 0 or not. I have the following script
if [ "$(git diff | grep pkg/client/clientset | wc -l "$i")" -gt "0" ]; then
echo "Hello"
fi
I am getting the following error while executing the script. The error I am getting is
line 29: [: : integer expression expected
Any idea of what can be going wrong?

Comparing the number of output lines to zero is almost always an antipattern. diff and grep both already tell you whether there was a difference (exit code 1) or a match (exit code 0) precisely so you can say
if diff old new; then
echo "There were differences"
fi
if git diff --exit-code; then
echo "There were differences"
fi
if git diff --exit-code pkg/client/clientset; then
echo "There were differences in this specific file"
fi
if git diff | grep -q pkg/client/clientset; then
echo "Hello"
fi
Notice that git diff requires an explicit option to enable this behavior.

-- EDIT --
There were some incorrect statements in the answer, pointed-out by commentators Gordon Davisson and iBug. They have been corrected in this version of the answer. The final conclusion (remove the "$i") remains the same though.
wc -l "$i" will count the lines in the file $i. If you never used i as a variable, then i will be empty and the command will be wc -l "". The output of that will be empty on STDOUT en contain wc: invalid zero-length file name on STDERR. If the variable i is used, wc will most likely complain about a non-existing file. The point is, that wc will not read STDIN.
I also made some incorrect statements about the quoting. As pointed out, between the ( and ), it is a different quoting context. This can be shown as follows:
$ a="$(/usr/bin/echo "hop")"
$ echo $a
hop
$ b=hop
$ a="$(/usr/bin/echo "$b")"
$ echo $a
hop
Just removing "$i" from the wc-l will solve your issue.
if [ "$(git diff | grep pkg/client/clientset | wc -l)" -gt "0" ]; then
echo "Hello"
fi

Only a note, that is to long for a comment:
if [ "$(git diff | grep pkg/client/clientset | wc -l "$i")" -gt "0" ]; then
I think you will test the existence of string pkg/client/clientset to enter the then part. In this case you can use:
if git diff | grep -q pkg/client/clientset; then
grep will only returns a status because option -q. The status is true after the first occurrence of the string. At this point grep stops. And this status is used by if.

Related

using ls | grep in an if statement

I'm trying to use an if [grep] using the exit value to trigger it
to test, from the command line
$ ls ~/dir | grep txt
$ echo $?
0
However when i use it in an if statement in a script, i.e.
if [ ls /some/dir | grep -q pattern ]; then
echo y
fi
It says
line 1: [ missing `]' (the line the if statement was written on)
Why is this happening and is there a way to fix it?
That is because [ is and conditional expression, check the details.
You should use () instead:
if (ls /some/dir | grep -q pattern); then
echo y
fi

How to match a folder name and use it in an if condition using grep in bash?

for d in */ ; do
cd $d
NUM = $(echo ${PWD##*/} | grep -q "*abc*");
if [[ "$NUM" -ne "0" ]]; then
pwd
fi
cd ..
done
Here I'm trying to match a folder name to some substring 'abc' in the name of the folder and check if the output of the grep is not 0. But it gives me an error which reads that NUM: command not found
An error was addressed in comments.
NUM = $(echo ${PWD##*/} | grep -q "*abc*"); should be NUM=$(echo ${PWD##*/} | grep -q "*abc*");.
To clarify, the core problem would be to be able to match current directory name to a pattern.
You can probably simply the code to just
if grep -q "*abc*" <<< "${PWD##*/}" 2>/dev/null; then
echo "$PWD"
# Your rest of the code goes here
fi
You can use the exit code of the grep directly in a if-conditional without using a temporary variable here ($NUM here). The condition will pass if grep was able to find a match. The here-string <<<, will pass the input to grep similar to echo with a pipeline. The part 2>/dev/null is to just suppress any errors (stderr - file descriptor 2) if grep throws!
As an additional requirement asked by OP, to negate the conditional check just do
if ! grep -q "*abc*" <<< "${PWD##*/}" 2>/dev/null; then

shell if statement always returning true

I want to check if my VPN is connected to a specific country. The VPN client has a status option but sometimes it doesn't return the correct country, so I wrote a script to check if I'm for instance connected to Sweden. My script looks like this:
#!/bin/bash
country=Sweden
service=expressvpn
while true; do
if ((curl -s https://www.iplocation.net/find-ip-address | grep $country | grep -v "grep" | wc -l) > 0 )
then
echo "$service connected!!!"
else
echo "$service not connected!"
$service connect $country
fi;
sleep 5;
done
The problem is, it always says "service connected", even when it isn't. When I enter the curl command manually, wc -l returns 0 if it didn't find Sweden and 1 when it does. What's wrong with the if statement?
Thank you
Peter
(( )) enters a math context -- anything inside it is interpreted as a mathematical expression. (You want your code to be interpreted as a math expression -- otherwise, > 0 would be creating a file named 0 and storing wc -l's output in that file, not comparing the output of wc -l to 0).
Since you aren't using )) on the closing side, this is presumably exactly what's happening: You're storing the output of wc -l in a file named 0, and then using its exit status (successful, since it didn't fail) to decide to follow the truthy branch of the if statement. [Just adding more parens on the closing side won't fix this, either, since curl -s ... isn't valid math syntax].
Now, if you want to go the math approach, what you can do is run a command substitution, which replaces the command with its output; that is a math expression:
# smallest possible change that works -- but don't do this; see other sections
if (( $(curl -s https://www.iplocation.net/find-ip-address | grep $country | grep -v "grep" | wc -l) > 0 )); then
...if your curl | grep | grep | wc becomes 5, then after the command substitution this looks like:
if (( 5 > 0 )); then
...and that does what you'd expect.
That said, this is silly. You want to know if your target country is in curl's output? Just check for that directly with shell builtins alone:
if [[ $(curl -s https://www.iplocation.net/find-ip-address) = *"$country"* ]]; then
echo "Found $country in output of curl" >&2
fi
...or, if you really want to use grep, use grep -q (which suppresses output), and check its exit status (which is zero, and thus truthy, if and only if it successfully found a match):
if curl -s https://www.iplocation.net/find-ip-address | grep -q -e "$country"; then
echo "Found $country in output of curl with grep" >&2
fi
This is more efficient in part because grep -q can stop as soon as it finds a match -- it doesn't need to keep reading more content -- so if your file is 16KB long and the country name is in the first 1KB of output, then grep can stop reading from curl (and curl can stop downloading) as soon as that first match 1KB in is seen.
The result of the curl -s https://www.iplocation.net/find-ip-address | grep $country | grep -v "grep" | wc -l statement is text. You compare text and number, that is why your if statement does not work.
This might solve your problem;
if [ $(curl -s https://www.iplocation.net/find-ip-address | grep $country | grep -v "grep" | wc -l) == "0" ] then ...
That worked, thank you for your help, this is what my script looks now:
#!/bin/bash
country=Switzerland
service=expressvpn
while true; do
if curl -s https://www.iplocation.net/find-ip-address | grep -q -e "$country"; then
echo "Found $country in output of curl with grep" >&2
echo "$service not connected!!!"
$service connect Russia
else
echo "$service connected!"
fi;
sleep 5;
done

get rid of integer expression expected in script

I know this has been asked many times here, but I have looked through all of the previous ones and still can't resolve it. This is just a simple script that checks for a running service and takes the action I define.
#!/bin/bash
SERVICE="$1"
RESULT=`ps -a | sed -n /${SERVICE}/p`
MEM=$(ps aux | sort -rk +4 | grep $1 | grep -v grep | awk '{print $4}' | awk 'NR == 1')
if [ "${RESULT:-null}" = null ]; then
echo "$1 is NOT running"
else
echo "$MEM"
fi
if [ "$MEM" -ge 1 ]; then
mailx -s "Alert: server needs to be checked" me#admins.com
fi
This is the error I get:
./check_service: line 15: [: 5.4: integer expression expected
If I take out the command for the MEM variable and run it outside the script it returns 5.4, which is what I would expect. In my script I have tried changing the "1" to a "1.0" since the output would always be in decimal format, but that did not help. I feel like I am missing something simple here.
Error is due to the fact that bash only supports integer mathematics and your script is comparing 5.4 with 1.
You can fix your script by using:
if [[ ${MEM%.*} -ge 1 ]]l then
mailx -s "Alert: server needs to be checked" me#admins.com
fi
${MEM%.*} will strip part after decimal point and since you're just comparing it with 1 there is no need to have decimal point in variable MEM.

preventing wildcard expansion in bash script

I've searched here, but still can't find the answer to my globbing problems.
We have files "file.1" through "file.5", and each one should contain the string "completed" if our overnight processing went ok.
I figure it's a good thing to first check that there are some files, then I want to grep them to see if I find 5 "completed" strings. The following innocent approach doesn't work:
FILES="/mydir/file.*"
if [ -f "$FILES" ]; then
COUNT=`grep completed $FILES`
if [ $COUNT -eq 5 ]; then
echo "found 5"
else
echo "no files?"
fi
Thanks for any advice....Lyle
Per http://mywiki.wooledge.org/BashFAQ/004, the best approach to counting files is to use an array (with the nullglob option set):
shopt -s nullglob
files=( /mydir/files.* )
count=${#files[#]}
If you want to collect the names of those files, you can do it like so (assuming GNU grep):
completed_files=()
while IFS='' read -r -d '' filename; do
completed_files+=( "$filename" )
done < <(grep -l -Z completed /dev/null files.*)
(( ${#completed_files[#]} == 5 )) && echo "Exactly 5 files completed"
This approach is somewhat verbose, but guaranteed to work even with highly unusual filenames.
try this:
[[ $(grep -l 'completed' /mydir/file.* | grep -c .) == 5 ]] || echo "Something is wrong"
will print "Something is wrong" if doesn't find 5 completed lines.
Corrected the missing "-l" - the explanation
$ grep -c completed file.*
file.1:1
file.2:1
file.3:0
$ grep -l completed file.*
file.1
file.2
$ grep -l completed file.* | grep -c .
2
$ grep -l completed file.* | wc -l
2
You can do this to prevent globbing:
echo \'$FILES\'
but it seems you have a different problem

Resources