Grep a specific string from bash script and compare - bash

i have a large log file A.log, i want to grep ONE CERTAIN STRING from the last 10 lines and compare to a variable (FTP_SUCCESS_MSG), how can i do that?
something like:
logs='/tmp/A.log'
FTP_SUCCESS_MSG="226 Transfer complete"
if [tail -10 $logs == $FTP_SUCCESS_MSG] ;
then
echo "Success"
else
echo "Failed"
exit 1
fi

if tail -10 "$logs" | grep -Fq "$FTP_SUCCESS_MSG" ; then ...
Notice how [ is not present in the condition (and if it were, it would require non-optional spaces on both sides).
Notice also how variable interpolations are in double quotes unless you require the shell to tokenize the value and perform wildcard expansion on the tokens.

Related

script in bash two if condition and print in for loop

Hello im learning to script in bash, i trying to solve a little exercise.
The Exercise is this
If the variable named "basenew" contains the contents of the variable
named "valuebase". "basenew" must contain more than 113,469
characters. If both conditions are met, the script must then print
the last 20 characters of the variable "basenew".
My code is
#!/bin/bash
basenew="8dm7KsjU28B7v621Jls"
valuebase="ERmFRMVZ0U2paTlJYTkxDZz09Cg"
for i in {1..40}
do
basenew=$(echo $basenew | base64)
if [[ $basenew =~ $valuebase && ${#basenew} -ge 113469 ]] ; then
echo $i
echo $basenew | wc -c
StrLen=`echo ${basenew} | wc -c`
From=`expr $StrLen - 20`
echo $basenew | cut -c ${From}-${StrLen}
else
echo "error"
fi
done
But im stuck, because prints in the loop 28, and are the 20 last but isn't the correct answer.
Any advice to print the last 20 characters using tail -c 20?
Thanks
Why do you think the value of basenew will change during the execution of your loop? You never assign it. But you assign var that you never use, could it be the issue? Yes it is, you need to update basenew, not the unused var.
There is another problem with your script: cut applies your character selection on each line of the input. base64, by default, splits the output in lines of 76 characters. So, after a while, you have many short lines. And as they all are much shorter than your specification cut would only print a lot of newlines, not the last 20 characters of the string. You could change the behaviour of base64 with base64 -w0 but it would also change the results of each iteration and you would probably never match the reference string.
Instead of piping echo in cut follow joop's suggestion: "${basenew: -20: 20}" expands as the 20 last characters of the value of basenew. Or, a bit simpler but equivalent: "${basenew: -20}". Do not forget the space between : and -20, it is needed.
Finally, you could use more built-in bash constructs to avoid external commands (wc, echo, cut), use the modern $(...) instead of the old backticks, and use the arithmetic evaluation (((...))) instead of the obsolete expr. Try, maybe:
#!/usr/bin/env bash
# file: foo.sh
basenew="8dm7KsjU28B7v621Jls"
valuebase="ERmFRMVZ0U2paTlJYTkxDZz09Cg"
for i in {1..40}; do
basenew=$(echo "$basenew" | base64)
if [[ $basenew =~ $valuebase ]] && (( ${#basenew} >= 113469 )) ; then
printf '%d\n%d\n%s\n' "$i" "${#basenew}" "${basenew: -20}"
else
printf 'error\n'
fi
done
Demo:
$ ./foo.sh
error
...
error
28
113469
U2paTlJYTkxDZz09Cg==
error
...
error
Any advice to print the last 20 characters using tail
From the tail man-page:
-c, --bytes=[+]NUM
output the last NUM bytes; or use -c +NUM to output starting with
byte NUM of each file
Hence, if you have a variable x, you get the last 20 characters of it printed to stdout using
tail --bytes=20 <<<$x

What does [[ $(echo ${lines} | grep \'_SUCCESS\') ]] mean?

I came across this bash command and not able to interpret as it always print NO which is in else part.
if [[ $(echo ${lines} | grep \'_SUCCESS\') ]] ; then echo \'Y\'; else echo \'N\'; fi;
exit 0
I have _SUCCESS file ins
[[ ... ]] is a bash construct that will transform the truth value of the expression within into a exit status code 0/1. if will execute the then branch if the exit status code is 0, and the else branch otherwise.
Within [[ ... ]], you still get command substitution, so echo ${lines} | grep \'_SUCCESS\' will be executed, and its output substituted into the command. These commands will output the line inside ${lines} that contains '_SUCCESS' (with single quotes!) if such is present, or nothing.
[[ ... ]] that contains a single string evaluates as true if string is non-empty, and false if empty.
Thus, the then branch will execute if ${files} contains '_SUCCESS'. If you are always getting a 'N' as output, it follows ${files} does not contain '_SUCCESS' (even if it maybe does contain _SUCCESS). If you want to look for _SUCCESS (without quotes), then grep _SUCCESS or equivalently grep '_SUCCESS' suffices.
This is a long way around of writing what sergio says in comments: grep will not only output (or not output) the lines, it will also signal with its exit status code whether something is found or not, and can thus directly be used as the if condition, without using [[ ... ]].

Search for value and print something if found (BASH)

I have the following list:
COX1
COX1
COX1
COX1
COX1
Cu-oxidase
Cu-oxidase_3
Cu-oxidase_3
Fer4_NifH
and I want to search if COX1 and Cu-oxidase is in the list, I want to print xyz, if Cu-oxidase_3 and Fer4_NifHis in the list too (independent if the first two are in the list, then it should print abc.
This is what I could script so far:
if grep 'COX1' file.txt; then echo xyz; else exit 0; fi
but it is of course incomplete.
Any solution to that?
ideally my output would be:
xyz
abc
Awk lets you easily search for multiple regular expressions and print something else than the matched string itself. (grep can easily search for multiple patterns, too, but it will print the match or its line number or file name, not some arbitrary string.)
The following assumes that you have a single token per line. This assumption makes the script really simple, though it would also not be hard to support other scenarios.
awk '{ a[$1]++ }
END { if (("COX1" in a) && ("Cu-oxidase" in a)) print "xyz";
if (("Cu-oxidase_3" in a) && ("Fer4_NifH" in a)) print "abc" }' file.txt
This builds an associative array of each token (actually the first whitespace-separated token on each line) and then at the end, when it has read every line in the file, checks whether the sought tokens exist as keys in the array.
Performing a single pass over the input file is a big win especially if you have a large input file and many patterns. Just for completeness, the syntax for performing multiple passes with grep is very straightforward;
if grep -qx 'COX1' file.txt && grep -qx 'Cu-oxidase' file.txt
then
echo xyz
fi
which can be further abbreviated to
grep -qx 'COX1' file.txt && grep -qx 'Cu-oxidase' file.txt && echo xyz
Notice the -x switch to require the whole line to match (otherwise the regex 'Cu-oxidase' would also match on the Cu-oxidase_3 lines).
Above is a very verbose way to achieve this. There are ways to write the same with less ifs and less greps, but I really wanted to show you the logic:
you run a grep command, check for its return value with $?, and finally acts on the conditions.
# default values
HAS_COX1=0
HAS_CUOX=0
HAS_CUO3=0
HAS_FER4=0
# run silently grep
grep -q 'COX1' file.txt
# check for return value and set variable accordingly
if [ $? -eq 0 ]; then HAS_COX1=1; fi
# same as above
grep -q 'Cu-oxidase' file.txt
if [ $? -eq 0 ]; then HAS_CUOX=1; fi
grep -q 'Cu-oxidase_3' file.txt
if [ $? -eq 0 ]; then HAS_CUO3=1; fi
grep -q 'Fer4_NifH' file.txt
if [ $? -eq 0 ]; then HAS_FER4=1; fi
if [ $HAS_COX1 -eq 1 ]; then
if [ $HAS_CUOX -eq 1 ]; then
echo 'xyz'
exit 0
fi
fi
if [ $HAS_CUO3 -eq 1 ]; then
if [ $HAS_FER4 -eq 1 ]; then
echo 'abc'
exit 0
fi
fi
echo 'None of the checks where matched'
exit 1
Beware: this code is untested, so there might be bugs ☺
The code isn't perfect, as it cannot print both 'xyz' and 'abc' when both conditions are met (but that would be an easy fix with the syntax I provide). Also $HAS_CUOX will be set to 1 whenever $HAS_CUO3 is found (no boundary checking in the grep regex).
You could take that code further by using a single grep for each set of conditions to check, using something like 'COX1\|Cu_oxidase' as the regex for grep. And also fix the minor issues I mentioned above.
ideally my output would be:
xyz
abc
You added your expected output after I wrote the above script, but given the elements I gave you, you should be able to figure how to improve that (basically removing the exit 0 where I placed them, and doing exit 1 when no output has been given.
Or just remove all exits as a dirty solution.

tail | grep -q always returning true

When I execute this code, the loop always ends at first time (even when the last two lines of auth.log doen't contain "exit"), which means that $c always gets some string:
while true;
do
c=$(tail -2 /var/log/auth.log | grep -q "exit")
if $c ;
then
echo "true"
unset c
break
fi
done
Do you know why c=$(tail -2 /var/log/auth.log | grep -q "exit") is always getting some kind of string? I think it is becaues of tail.
I can use the -o option and then compare strings, but I prefer to use a boolean inside the if condition.
grep -q by design returns no output, it simply signals via its exit code whether a match was found.
Thus, you can simply use your pipeline directly as a condition:
while true;
do
if tail -2 /var/log/auth.log | grep -q "exit";
then
echo "true"
break
fi
done
As for what you tried:
As Benjamin W. implies in a comment on the question, executing a command expanding to the empty string is always considered a successful command.
Note: Whether the command is effectively empty because the variable in question is unset or, as in this case, was explicitly assigned a null (empty) string, doesn't matter.
Thus, given that $c is invariably empty - because grep -q by design never returns stdout output - the if condition always evaluates to true.
To be clear: $c, since it is not being used in a conditional (if $c; ... rather than if [ "$c" ]; ...), is interpreted as a command to execute rather than as a string to test for emptiness.
If the command whose output is captured in $c were to generate stdout output, you'd have to test for that with a conditional: if [ -n "$c" ]; then ... (or, more succinctly, if [ "$c" ]; then ...).

How do I use a file grep comparison inside a bash if/else statement?

When our server comes up we need to check a file to see how the server is configured.
We want to search for the following string inside our /etc/aws/hosts.conf file:
MYSQL_ROLE=master
Then, we want to test whether that string exists and use an if/else statement to run one of two options depending on whether the string exists or not.
What is the BASH syntax for the if statement?
if [ ????? ]; then
#do one thing
else
#do another thing
fi
From grep --help, but also see man grep:
Exit status is 0 if any line was selected, 1 otherwise;
if any error occurs and -q was not given, the exit status is 2.
if grep --quiet MYSQL_ROLE=master /etc/aws/hosts.conf; then
echo exists
else
echo not found
fi
You may want to use a more specific regex, such as ^MYSQL_ROLE=master$, to avoid that string in comments, names that merely start with "master", etc.
This works because the if takes a command and runs it, and uses the return value of that command to decide how to proceed, with zero meaning true and non-zero meaning false—the same as how other return codes are interpreted by the shell, and the opposite of a language like C.
if takes a command and checks its return value. [ is just a command.
if grep -q ...
then
....
else
....
fi
Note that, for PIPE being any command or sequence of commands, then:
if PIPE ; then
# do one thing if PIPE returned with zero status ($?=0)
else
# do another thing if PIPE returned with non-zero status ($?!=0), e.g. error
fi
For the record, [ expr ] is a shell builtin† shorthand for test expr.
Since grep returns with status 0 in case of a match, and non-zero status in case of no matches, you can use:
if grep -lq '^MYSQL_ROLE=master' ; then
# do one thing
else
# do another thing
fi
Note the use of -l which only cares about the file having at least one match (so that grep returns as soon as it finds one match, without needlessly continuing to parse the input file.)
†on some platforms [ expr ] is not a builtin, but an actual executable /bin/[ (whose last argument will be ]), which is why [ expr ] should contain blanks around the square brackets, and why it must be followed by one of the command list separators (;, &&, ||, |, &, newline)
just use bash
while read -r line
do
case "$line" in
*MYSQL_ROLE=master*)
echo "do your stuff";;
*) echo "doesn't exist";;
esac
done <"/etc/aws/hosts.conf"
Below code sample should work:
(echo "hello there" | grep -q "AAA") && [ $? -eq 0 ] && echo "hi" || echo "bye"

Resources