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

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 [[ ... ]].

Related

How to use contents of text file as input to shell script?

I'm tasked with writing a shell script that takes in a string of 6 letters and numbers and checks if they meet a certain criteria.
This is that script
FILE=$1
var=${#FILE}
if [ $var -gt 6 ] || [ $var -lt 6 ]
then
#echo $FILE "is not a valid NSID"
exit 1
else if ( echo $1 | egrep -q '^[A-Za-z]{3}\d{3}$' )
then
#echo $1 "is a valid NSID"
exit 0
else
#echo $1 "is not a valid NSID"
exit 1
fi
fi
It works. so that isn't where the problem is.
What I am trying to do is use a "wrapper" script to gather potential valid NSID's from a text file and call that script on the list of inputs. so that if I call that script within my wrapper script it will step through the text file I have given my wrapper and check if each line is valid.
FILE=$1
YES= more $FILE
if ( exec /path/to/file/is_nsid.sh $YES -eq 0 )
then
echo $FILE "is a valid NSID"
else
echo $FILE "is not a valid NSID"
fi
so if I called it with a text file called file1.txt which contained
yes123
yess12
ye1243
it would output whether each was valid or not.
The line
YES= more $FILE
Sets YES in the environment passed to the command more $FILE. That's probably not what you intended.
The line
if ( exec /path/to/file/is_nsid.sh $YES -eq 0 )
starts a subshell to execute exec /path/to/file/is_nsid.sh $YES -eq 0. (That's what the parentheses do.) exec then replaces the subshell with a process which executes
/path/to/file/is_nsid.sh $YES -eq 0
which in turn runs the script at is_nsid.sh, passing it two or three command line arguments:
the value of $YES. This could be several arguments if the value of the shell variable includes whitespace or a glob symbol, but in this case it is more likely to be nothing since $YES has not been defined.
-eq
0
Since your script only examines its first argument, that's probably equivalent to
/path/to/file/is_nsid.sh -eq
That will, presumably, terminate with a failure status code, and since the subshell has been replaced with the script execution, that will also be the return status of the subshell. (Without exec, there would be essentially no difference; the subshell's return status is that of the last command executed in the subshell. Without either the parentheses or the exec, the result would also be the same. So you could have just written if /path/to/file/is_nsid.sh $YES -eq 0 and it would produce the same incorrect result.)
What you presumably wanted to do was to read each line in the file whose name is passed as the first command-line argument to the script. You could do that as follows:
while read -r line; do
if /path/to/file/is_nsid.sh "$line"; then
echo "$line is a valid NSID"
else
echo "$line is not a valid NSID"
fi
done < "$1"
You could simplify your is_nsid script considerably. The following is equivalent:
[ $#1 -eq 6 ] && echo "$1" | egrep -q '^[A-Za-z]{3}\d{3}$'
Note that \d is a Gnu extension to egrep and should not be relied on in portable code (which I assume this is trying to be). You should use [0-9] or [[:digit:]] instead.
The length check is actually unnecessary since the regex can only match six-character lines. Personally, I'd leave it out and just use
echo "$1" | egrep -q '^[[:alpha:]]{3}[[:digit:]]{3}$'
I removed all the unnecessary if statements. If I had left them in, I would have changed else if ... then ... fi to simply elif ... then ... to avoid unnecessary if nesting.

How can I get the return value and matched line by grep in bash at once?

I am learning bash. I would like to get the return value and matched line by grep at once.
if cat 'file' | grep 'match_word'; then
match_by_grep="$(cat 'file' | grep 'match_word')"
read a b <<< "${match_by_grep}"
fi
In the code above, I used grep twice. I cannot think of how to do it by grep once. I am not sure match_by_grep is always empty even when there is no matched words because cat may output error message.
match_by_grep="$(cat 'file' | grep 'match_word')"
if [[ -n ${match_by_grep} ]]; then
# match_by_grep may be an error message by cat.
# So following a and b may have wrong value.
read a b <<< "${match_by_grep}"
fi
Please tell me how to do it. Thank you very much.
You can avoid the double use of grep by storing the search output in a variable and seeing if it is not empty.
Your version of the script without double grep.
#!/bin/bash
grepOutput="$(grep 'match_word' file)"
if [ ! -z "$grepOutput" ]; then
read a b <<< "${grepOutput}"
fi
An optimization over the above script ( you can remove the temporary variable too)
#!/bin/bash
grepOutput="$(grep 'match_word' file)"
[[ ! -z "$grepOutput" ]] && (read a b <<< "${grepOutput}")
Using double-grep once for checking if-condition and once to parse the search result would be something like:-
#!/bin/bash
if grep -q 'match_word' file; then
grepOutput="$(grep 'match_word' file)"
read a b <<< "${grepOutput}"
fi
When assigning a variable with a string containing a command expansion, the return code is that of the (rightmost) command being expanded.
In other words, you can just use the assignment as the condition:
if grepOutput="$(cat 'file' | grep 'match_word')"
then
echo "There was a match"
read -r a b <<< "${grepOutput}"
(etc)
else
echo "No match"
fi
Is this what you want to achieve?
grep 'match_word' file ; echo $?
$? has a return value of the command run immediately before.
If you would like to keep track of the return value, it will be also useful to have PS1 set up with $?.
Ref: Bash Prompt with Last Exit Code

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 ...).

Grep a specific string from bash script and compare

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.

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