bash while loop with mixed condition - bash

I'm not meaning multiple conditions, well it's also but mixed from that:
while [[ read line ] && [ "$GoodURL" == "false" ]]
What is the correct form for that? It's a while loop that runs on a text file line by line,
and I want to stop it with that $GoodURL type of boolean, please help.
Thanks.

while read line && [[ "$GoodURL" == "false" ]]
do
echo $line;
done
In case you want to read from a file/pipe, be sure to use indirection or you will get funny results (due the while loop executing in a subshell and not actually using the same environmen as the surrounding shell)
while read line && [[ "$GoodURL" == "false" ]]
do
echo $line;
done < input.txt

Related

Why is [[ "$input" == name1* || name2* ]] never false?

I'm a little befuddled with a script I've been writing - and would appreciate some help!
This is one of those cases where each command seems to work fine on their own, but not so when put together into a script.
Here's a gist of what I'm trying to do:
input=$1
single_func () {
command "$input"
}
multi_func () {
xargs < $input -n 1 single_func
}
if [[ "$input" == name1* || name2* ]];
then
single_func
elif [[ -f "$input" ]];
then
multi_func
else
echo "exiting"
exit
fi
The idea here is - if the script is invoked with ./script.sh input, if will run if the input starts with name1 or name2, using single_func. If the input provided doesn't start with name1 or name2, and is a file containing a list of items, elif will run (reason for -f) using multi_fuc, which is just single_func running with xarg on the provided file.
The 'single_func' component runs on the command line fine on its own (command "input"), and the 'multi_func' component runs fine with a test file (xargs < testfile.txt -n 1 ./single_func.sh). But when I put them together as above and try to run them together, only the first 'if' part works correctly. When provided with a file or some nonsense line not containing name1 or name2, the script simply exits without returning anything.
For the curious, I'm running entrez direct commands within the single_func block.
What am I doing wrong?
You need to write:
if [[ "$input" = name1* || "$input" = name2* ]]; then
Otherwise, the right-hand side of your || tests whether name2* is a non-empty string, which it always unconditionally is, making the statement always true.
If you don't want to repeat yourself (and your real use case is complex enough you can't just change it to if [[ "$input" = name[12]* ]]), use a case statement instead:
case $input in
name1*|name2*) echo "Either name1 or name2 prefix found";;
*) echo "Neither prefix found";;
esac

Why my if/else statement doesn't work? (shell)

I have problem with this shell script:
current_time=$(date +"%T");
current_day_of_week="$(date +'%u')";
if [[ current_day_of_week == 1 ]];
then echo "Setting max time to 03:00:00" && max_time="03:00:00";
else echo "Setting max time to 01:30:00" && max_time="01:30:00"; fi;
I want to set variable max_time to 03:00:00 when it's monday, but the code doesn't work :(
The string "current_day_of_week" is not the same as the string "1". If you want to compare the value of the variable, you need to dereference it. Replace
if [[ current_day_of_week == 1 ]];
with
if [ "$current_day_of_week" = 1 ];
Since you're not getting a syntax error, we can assume you are using a shell that supports the [[ syntax (you should probably change the tag !) so you can use:
if [[ $current_day_of_week == 1 ]];
Note that although quoting variables is not strictly necessary inside the [[ construct, it is a good idea to use quotes anyway:
if [[ "$current_day_of_week" == 1 ]];

"sh: : unknown operand" in Yocto

The following works in Ubuntu but not Yocto (Poky).
root#system:~/# x='abc'
root#system:~/# y=''
root#system:~/# [[ $(echo $x) != '' ]] && echo true
true
root#system:~/# [[ $(echo $y) != '' ]] && echo true
sh: : unknown operand
In Ubuntu the last line returns nothing (as expected). Any ideas why it's throwing an error in Yocto?
The problem seems to be that $(echo $y) is expanding to an empty string, and then [[ isn't handling it correctly. The solution to that would be to quote the command substitution like
[[ "$(echo "$y")" != '' ]] && echo true
though it's probably better still to use printf than echo so you might do it as
[[ "$(printf '%s' "$y")" != '' ]] && echo true
just in case $y might end up with special characters that can trip up echo or similar
Apparently, busybox ash has a rather simplistic implementation of [[. It is the same as [ except that it expects a ]] instead of ] final argument. This misses the point of why [[ can be useful at all: [[ is supposed to be a keyword with special parsing and using it looks more beautiful and avoids various pitfalls (while adding some of its own). I guess they added it so a few more bash scripts run unmodified on busybox ash.
To avoid confusion, I recommend not using [[ in busybox at all. Use [ and quote all command substitutions and parameter expansions.

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

How to get first character of variable

I'm trying to get the first character of a variable, but I'm getting a Bad substitution error. Can anyone help me fix it?
code is:
while IFS=$'\n' read line
do
if [ ! ${line:0:1} == "#"] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
Am I doing something wrong or is there a better way of doing this?
-- EDIT --
As requested - here's some sample input which is stored in /some/file.txt
$MOZ_HOME/mobile/android/chrome/content/browser.js
$MOZ_HOME/mobile/android/locales/en-US/chrome/browser.properties
$MOZ_HOME/mobile/android/components/ContentPermissionPrompt.js
To get the first character of a variable you need to say:
v="hello"
$ echo "${v:0:1}"
h
However, your code has a syntax error:
[ ! ${line:0:1} == "#"]
# ^-- missing space
So this can do the trick:
$ a="123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
doesnt start with #
$ a="#123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
$
Also it can be done like this:
$ a="#123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
$
$ a="123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
does not start with #
Update
Based on your update, this works to me:
while IFS=$'\n' read line
do
echo $line
if [ ! "${line:0:1}" == "#" ] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < file
Adding the missing space (as suggested in fedorqui's answer ;) ) works for me.
An alternative method/syntax
Here's what I would do in Bash if I want to check the first character of a string
if [[ $line != "#"* ]]
On the right hand side of ==, the quoted part is treated literally whereas * is a wildcard for any sequence of character.
For more information, see the last part of Conditional Constructs of Bash reference manual:
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching
Checking that you're using the right shell
If you are getting errors such as "Bad substitution error" and "[[: not found" (see comment) even though your syntax is fine (and works fine for others), it might indicate that you are using the wrong shell (i.e. not Bash).
So to make sure you are using Bash to run the script, either
make the script executable and use an appropriate shebang e.g. #!/bin/bash
or execute it via bash my_script
Also note that sh is not necessarily bash, sometimes it can be dash (e.g. in Ubuntu) or just plain ol' Bourne shell.
Try this:
while IFS=$'\n' read line
do
if ! [ "${line:0:1}" = "#" ]; then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
or you can use the following for your if syntax:
if [[ ! ${line:0:1} == "#" ]]; then
TIMTOWTDI ^^
while IFS='' read -r line
do
case "${line}" in
"#"*) echo "${line}"
;;
*) createSymlink ${line}
;;
esac
done < /some/file.txt
Note: I dropped the eval, which could be needed in some (rare!) cases (and are dangerous usually).
Note2: I added a "safer" IFS & read (-r, raw) but you can revert to your own if it is better suited. Note that it still reads line by line.
Note3: I took the habit of using always ${var} instead of $var ... works for me (easy to find out vars in complex text, and easy to see where they begin and end at all times) but not necessary here.
Note4: you can also change the test to : *"#"*) if some of the (comments?) lines can have spaces or tabs before the '#' (and none of the symlink lines does contain a '#')

Resources