Bash - catch the output of a command - bash

I am trying to check the output of a command and run different commands depending on the output.
count="1"
for f in "$#"; do
BASE=${f%.*}
# if [ -e "${BASE}_${suffix}_${suffix2}.mp4" ]; then
echo -e "Reading GPS metadata using MediaInfo file ${count}/${##} "$(basename "${BASE}_${suffix}_${suffix2}.mp4")"
mediainfo "${BASE}_${suffix}_${suffix2}.mp4" | grep "©xyz" | head -n 1
if [[ $? != *xyz* ]]; then
echo -e "WARNING!!! No GPS information found! File ${count}/${##} "$(basename "${BASE}_${suffix}_${suffix2}.mp4")" || exit 1
fi
((count++))
done
MediaInfo is the command I am checking the output of.
If a video file has "©xyz" atom written into it the output looks like this:
$ mediainfo FILE | grep "©xyz" | head -n 1
$ ©xyz : +60.9613-125.9309/
$
otherwise it is null
$ mediainfo FILE | grep "©xyz" | head -n 1
$
The above code does not work and echos the warning even when ©xyz presented.
Any ideas of what I am doing wrong?

The syntax you are using the capture the output of the mediainfo command is plain wrong. When using grep you can use its return code (the output of $?) directly in the if-conditional
if mediainfo "${BASE}_${suffix}_${suffix2}.mp4" | grep -q "©xyz" 2> /dev/null;
then
..
The -q flag in grep instructs it to run the command silently without throwing any results to stdout, and the part 2>/dev/null suppresses any errors thrown via stderr, so you will get the if-conditional pass when the string is present and fail if not present

$? is the exit code of the command: a number between 0 and 255. It's not related to stdout, where your value "xyz" is written.
To match in stdout, you can just use grep:
if mediainfo "${BASE}_${suffix}_${suffix2}.mp4" | grep -q "©xyz"
then
echo "It contained that thing"
else
echo "It did not"
fi

Related

How to 'grep' a continuous log file stream with 'tailf' and when the needed string is obtained, close/break the 'tailf' automatically?

into a bash script, I need to grep a contiuous log streaming and when the proper string is filtered, I need to stop the 'tailf' command to move ond with other implementations.
The common command that works is:
tailf /dir/dir/dir/server.log | grep --line-buffered "Started in"
after the "Started in" line is gathered, I need to break down the "tailf" command.
All this stuff into a bash script.
use grep -m1, it means return the first match then stop:
-m num, --max-count=num
Stop reading the file after num matches.
tailf /dir/dir/dir/server.log | grep -m1 "Started in"
Figured out...
tailf /dir/dir/dir/server.log | while read line
do
echo $line | grep "thing_to_grep"
if [ "$?" -eq "0" ]; then
echo "";echo "[ message ]";echo "";
kill -2 -$$
fi
done
$$ is the PID of the current shell, in this case associated to the "tailf" command.

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

Redirect grep output to file

I am not sure as to why that redirection provided in the code does not work. Every time I run the script, the output file is always empty. Does anyone have an idea on that?
Thanks.
#!/bin/sh
LOOK_FOR="DefaultProblem"
FILES=`ls plugins/*source*.jar`
for i in $FILES
do
# echo "Looking in $i ..."
unzip -p $i | grep -i $LOOK_FOR > output #> /dev/null
if [ $? == 0 ]
then
echo ">>>> Found $LOOK_FOR in $i <<<<"
fi
done
You may want to use >> (append) instead of > (overwrite) for redirection as:
unzip -p $i | grep -iF "$LOOK_FOR" >> output
Since you're executing this command in a loop and overwriting file output every time, it might be blank in the end if very last command with grep doesn't find any matching line in unzip output.
You have three problems
Don't try to parse the output of ls. Instead just use for i in plugins/*source*.jar The major reason is that your script will completely and utterly break on any files that have spaces in their names. See this link for a litany of reasons why not to parse ls
You need to use >> instead of > as the latter will overwrite the output file on each iteration of the loop. The former will append to it
Use more quotes! You'll want to quote your variables to make sure they aren't subjected to word splitting
Also, you can inline the if test. So putting it all together we have:
#!/bin/sh
LOOK_FOR="DefaultProblem"
for i in plugins/*source*.jar
do
# echo "Looking in $i ..."
if unzip -p "$i" | grep -i "$LOOK_FOR" >> output #> /dev/null
then
echo ">>>> Found $LOOK_FOR in $i <<<<"
fi
done
You can redirect the output of the entire loop:
#!/bin/sh
LOOK_FOR="DefaultProblem"
FILES=`ls plugins/*source*.jar`
for i in $FILES ; do
# echo "Looking in $i ..." 1>&2
unzip -p $i | grep -i $LOOK_FOR
if [ $? == 0 ] ; then
echo ">>>> Found $LOOK_FOR in $i <<<<" 1>&2
fi
done > output
Note that I've redirected the diagnostic messages to stderr.
Instead of a for loop and an if conditional you can do everything in one find command
find /path/to/plugins -name "*source*.jar" -exec sh -c 'unzip -l "{}" | grep -q DefaultProblem' \; -print

In a unix box, I am taking a list of files as input. If it is found, return the path otherwise return a message "filename file not found"

I have used the find command for this, but it doesnt return any message when a file is not found.
And I want the search to be recursive and return a message "not found" when a file is not found.
Here's the code I have done so far. Here "input.txt" contains the list of files to be searched.
set `cat input.txt`
echo $#
for i in $#
do
find $HOME -name $i
done
Try this:
listfile=input.txt
exec 3>&1
find | \
grep -f <( sed 's|.*|/&$|' "$listfile" ) | \
tee /dev/fd/3 | \
sed 's|.*/\([^/]*\)$|\1|' | \
grep -v -f - "$listfile" | \
sed 's/$/ Not found/'
exec 3>&-
open file descriptor 3
find the files
see if they're on the list (use sed to
send a copy of the found ones to file descriptor 3
strip off the directory name
get a list of the ones that don't appear
add the "Not found" message
close file descriptor 3
Output looks like:
/path/to/file1
/path/somewhere/file2
foo Not found
bar Not found
No loops necessary.
Whats wrong with using a script. I hope this will do.
#!/bin/bash -f
for i in $#
do
var=`find $HOME -name $i`
if [ -z "$var"]
then
var="File not found"
fi
echo $var
done
You can use the shell builtin 'test' to test the existence of a file. There is also an alternative syntax using square brackets:
if [ -f $a ]; then # Don't forget the semicolon.
echo $a
else
echo 'Not Found'
fi
Here is one way - create a list of all the files to grep against. If your implementation supports
grep -q otherwise use grep [pattern] 2&>1 >/dev/null....
find $HOME -type f |
while read fname
do
echo "$(basename $fname) $fname"
done > /tmp/chk.lis
while read fname
do
grep -q "^$fname" /tmp/chk.lis
[ $? -eq 0 ] && echo "$fname found" || echo "$fname not found"
done < /tmp/chk.lis
All of this is needed because POSIX find does not return an error when a file is not found
perl -nlE'say-f$_?$_:"not found: $_"' file

Resources