Strings not matching in Bash - bash

I am running bash in Ipython notebook. I have a variable called run_state that presently is RUNNING. However when I try to compare its value to this string, it doesn't match. What am I doing wrong? The image shows the output of the first echo and that indeed run_state equals RUNNING
run_state="RUNNING"
gcloud ai-platform jobs describe $1 >describe
grep -m 1 -o 'state: [a-zA-Z]*' describe | sed s/'state: '// >state
state_var=$(<state)
echo $state_var
if [ ["$state_var" == "$run_state"] ];
then
echo $state_var
fi

You can't have spaces between the brackets in [[, and you must have spaces around the brackets. So it should be
if [[ "$state_var" == "$run_state" ]];

Related

How to compare a string variable against a literal in zsh

I'm trying to do an OS check in my .zshrc. I can't get the string comparison against Ubuntu to match correctly when running on Ubuntu.
Snippet:
function get_linux_distro()
{
echo `awk -F= '/^NAME/{print $2}' /etc/os-release`
}
function is_os_ubuntu()
{
set -x
local dist=`get_linux_distro`
if [[ ${dist} = "Ubuntu"* ]]; then # <<< string comp
echo "UBUNTU"
return 0
else
echo "BLAHHHH"
return 1
fi
}
Output:
❯ is_os_ubuntu
+is_os_ubuntu:4> get_linux_distro
+get_linux_distro:3> awk '-F=' '/^NAME/{print $2}' /etc/os-release
+get_linux_distro:3> echo '"Ubuntu"'
+is_os_ubuntu:4> local dist='"Ubuntu"'
+is_os_ubuntu:5> [[ '"Ubuntu"' = Ubuntu* ]] # <<< don't match due to quotes??
+is_os_ubuntu:10> echo BLAHHHH
BLAHHHH
+is_os_ubuntu:11> return 1
Note: I've added the bash tag since I'm lead to believe this is the same in both and bash has more visibility.
How to correctly compare a string variable against a litteral in zsh
Remove the * or quote it, otherwise, it's parsed as a glob expression. You are correctly comparing it.
[[ ${dist} = "Ubuntu" ]]
Because your string is not Ubuntu, but "Ubuntu", it's not equal and works correctly.
Please do not ask XY questions.
From man os-release:
The basic file format of os-release is a newline-separated list of environment-like shell-compatible variable assignments. [...]
Source the file in shell and output the variable, preferably in a subshell.
get_linux_distro() {
sh -c 'source /etc/os-release; echo "$NAME"'
}
Do not use backticks `. Prefer $(...).
Do not use: echo $(something) - it's a useless use of echo, like echo $(echo $(echo $(something))). Just do the thing you want to do, without echo.
Check your scripts with HTTP://shellcheck.net.
While the comments you got, and the answer given by KamilCuk. are of course correct, one alternative worth considering would be to use [[ $dist:l == *ubuntu* ]] for the test. This would test whether dist contains ubuntu as a substring, case insensitively.

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

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

Finding a part of a string in another string variable in bash

I have an issue in finding a part of string variable in another string variable, I tried many methods but none worked out..
for example:
echo -e " > Required_keyword: $required_keyword"
send_func GUI WhereAmI
echo -e " > FUNCVALUE: $FUNCVALUE"
flag=`echo $FUNCVALUE|awk '{print match($0,"$required_keyword")}'`;
if [ $flag -gt 0 ];then
echo "Success";
else
echo "fail";
fi
But it always gives fail though there are certain words in variable which matches like
0_Menu/BAA_Record ($required_keyword output string)
Trying to connect to 169.254.98.226 ... OK! Executing sendFunc GUI
WhereAmI Sent Function WhereAmI [OK PageName:
"_0_Menu__47__BAA_Record" ($FUNCVALUE output string)
As we can see here the BAA_Record is common in both of the output still, it always give FAIL
The output echo is
> Required_keyword: 0_Menu/BAA_Record
> FUNCVALUE:
Trying to connect to 169.254.98.226 ... OK!
Executing sendFunc GUI WhereAmI
Sent Function WhereAmI [OK]
PageName: "_0_Menu__47__BAA_Record"
Bash can do wildcard and regex matches inside double square brackets.
if [[ foobar == *oba* ]] # wildcard
if [[ foobar =~ fo*b.r ]] # regex
In your example:
if [[ $FUNCVALUE = *$required_keyword* ]]
if [[ $FUNCVALUE =~ .*$required_keyword.* ]]
Not sure if I understand what you want, but if you need to find out if there's part of string "a" present in variable "b" you can use simply just grep.
grep -q "a" <<< "$b"
[[ "$?" -eq 0 ]] && echo "Found" || echo "Not found"
EDIT: To clarify, grep searches for string a in variable b and returns exit status (see man grep, hence the -q switch). After that you can check for exit status and do whatever you want (either with my example or with regular if statement).

Bash script trouble interpretting input

I wrote a bash script that uploads a file on my home server. It gets activated from a folder action script using applescript. The setup is the folder on my desktop is called place_on_server. Its supposed to have an internal file structure exactly like the folder I want to write to: /var/www/media/
usage goes something like this:
if directory etc added to place_on_server: ./upload DIR etc
if directory of directory: etc/movies ./upload DIR etc movies //and so on
if file to place_on_server: ./upload F file.txt
if file in file in place_on_server ./upload F etc file.txt //and so on
for creating a directory its supposed to execute a command like:
ssh root#192.168.1.1<<EOF
cd /var/www/media/wherever
mkdir newdirectory
EOF
and for file placement:
rsync -rsh='ssh -p22' file root#192.168.1.1:/var/www/media/wherever
script:
#!/bin/bash
addr=$(ifconfig -a | ./test)
if ($# -le "1")
then
exit
elif ($1 -eq "DIR")
then
f1="ssh -b root#$addr<<EOF"
list = "cd /var/www/media\n"
if($# -eq "2")
then
list=list+"mkdir $2\nEOF\n"
else
num=2
i=$(($num))
while($num < $#)
do
i=$(($num))
list=list+"mkdir $i\n"
list=list+"cd $i\n"
$num=$num+1
done
fi
echo $list
elif ($1 -eq "F")
then
#list = "cd /var/www/media\n"
f2="rsync -rsh=\'ssh -p22\' "
f3 = "root#$addr:/var/www/media"
if($# -eq "2")
then
f2=f2+$2+" "+f3
else
num=3
i=$(($num))
while($num < $#)
do
i=$(($num))
f2=f2+"/"+$i
$num=$num+1
done
i=$(($num))
f2=f2+$i+" "+$f3
fi
echo $f2
fi
exit
output:
(prompt)$ ./upload2 F SO test.txt
./upload2: line 3: 3: command not found
./upload2: line 6: F: command not found
./upload2: line 25: F: command not found
So as you can see I'm having issues handling input. Its been awhile since I've done bash. And it was never extensive to begin with. Looking for a solution to my problem but also suggestions. Thanks in advance.
For comparisons, use [[ .. ]]. ( .. ) is for running commands in subshells
Don't use -eq for string comparisons, use =.
Don't use < for numerical comparisons, use -lt
To append values, f2="$f2$i $f3"
To add line feeds, use $'\n' outside of double quotes, or a literal linefeed inside of them.
You always need "$" on variables in strings to reference them, otherwise you get the literal string.
You can't use spaces around the = in assignments
You can't use $ before the variable name in assignments
To do arithmetics, use $((..)): result=$((var1+var2))
For indirect reference, such as getting $4 for n=4, use ${!n}
To prevent word splitting removing your line feeds, double quote variables such as in echo "$line"
Consider writing smaller programs and checking that they work before building out.
Here is how I would have written your script (slightly lacking in parameter checking):
#!/bin/bash
addr=$(ifconfig -a | ./test)
if [[ $1 = "DIR" ]]
then
shift
( IFS=/; echo ssh "root#$addr" mkdir -p "/var/www/media/$*"; )
elif [[ $1 = "F" ]]
then
shift
last=$#
file=${!last}
( IFS=/; echo rsync "$file" "root#$addr:/var/www/media/$*" )
else
echo "Unknown command '$1'"
fi
$* gives you all parameters separated by the first character in $IFS, and I used that to build the paths. Here's the output:
$ ./scriptname DIR a b c d
ssh root#somehost mkdir -p /var/www/media/a/b/c/d
$ ./scriptname F a b c d somefile.txt
rsync somefile.txt root#somehost:/var/www/media/a/b/c/d/somefile.txt
Remove the echos to actually execute.
The main problem with your script are the conditional statements, such as
if ($# -le "1")
Despite what this would do in other languages, in Bash this is essentially saying, execute the command line $# -le "1" in a subshell, and use its exit status as condition.
in your case, that expands to 3 -le "1", but the command 3 does not exist, which causes the error message
./upload2: line 3: 3: command not found
The closest valid syntax would be
if [ $# -le 1 ]
That is the main problem, there are other problems detailed and addressed in that other guy's post.
One last thing, when you're assigning value to a variable, e.g.
f3 = "root#$addr:/var/www/media"
don't leave space around the =. The statement above would be interpreted as "run command f3 with = and "root#$addr:/var/www/media" as arguments".

Resources