I'd like to use the result of a comparison in a subsequent comparison. I'm trying to do something like:
# $1 - expected result
# $2 - actual result
function print_result() {
if [[ [[ $1 -eq 0 ]] -eq [[ $2 -eq 0 ]] ]]; then # invalid
echo "Pass"
else
echo "Fail"
fi
}
I can get the desired behaviour with the more verbose form:
function print_result() {
if [[ (($1 -eq 0) && ($2 -eq 0)) || (($1 -ne 0) && ($2 -ne 0)) ]]; then
echo "Pass"
else
echo "Fail"
fi
}
but it seems like there should be a simpler way?
You need something which produces the result of the comparison operator as text rather than as a status return. That would be arithmetic expansion: $((expression)).
Note also that bash includes a numeric conditional compound statement -- (( expr )) -- which is often easier to use for numerical comparisons than the non-numerical [[ ... ]] conditional compound statement.
Putting that together, what you are looking for is:
if (( $(($1==0)) == $(($2==0)) )); then
Or just:
if (( ($1==0) == ($2==0) )); then
If you only want to know if $1 and $2 are both zero or both non-zero, then you can use the fact that boolean not (!) always evaluates to either 0 or 1. Consequently, the following even simpler expression is equivalent (but see warning below):
if ((!$1 == !$2)); then
Important: If you use that in a script, it will work fine, but at the command prompt you need to put a space after the ! to avoid it being interpreted as a history expansion character (unless you've turned history expansion off: if you don't actually use history expansion, turning it off is not a bad idea.)
A possibly slightly more readable (but not particularly simpler) way would be to store the result of each comparison in a variable and then compare the variables:
function print_result() {
[[ $1 -eq 0 ]]; arg_one_is_zero=$?
[[ $2 -eq 0 ]]; arg_two_is_zero=$?
if [[ $arg_one_is_zero -eq $arg_two_is_zero ]]; then
echo "Pass"
else
echo "Fail"
fi
}
There might be a better way to store the logical result (exit code) of a comparison in a variable but I couldn't find one after a quick look.
Related
x=23;
y=223;
if [[ $x < $y ]]
then
echo "TRUE"
else
echo "FALSE"
fi
So this always print FALSE even thought 23 is clearly less than 223. I am new to bash so I might be missing something obvious. I tried substituting $x and $y with their actually value and that means to work fine. Please send help
< performs lexicographic ordering of strings. To get numeric ordering, use -lt.
x=23;
y=223;
if [[ $x -lt $y ]]
then
echo "TRUE"
else
echo "FALSE"
fi
Or you can use an arithmetic expression instead of a conditional expression:
if (( $x < $y ))
I'm unfamiliar with bash scripting. I wrote a script check arguments. the code is:
for (( i=1; i<=4; i++ ))
do
if ! [[ "$"$i =~ .*[^0-9].* ]]; then
echo "bad input was $i"
fi
done
Actually i want to split non numerical arguments, But it seems that "$"$i is wrong because the answer is always true or false independent of arguments.
can anybody tell me what is the mistake?
You seem to be trying to use indirect parameter expansion.
for (( i=1; i<=4; i++ ))
do
if ! [[ ${!i} =~ .*[^0-9].* ]]; then
echo "bad input was $i"
fi
done
However, it's cleaner to just iterate over the parameters directly, rather than over their position:
for arg in "${#:1:4}"; do
if ! [[ $arg =~ .*[^0-9].* ]]; then
echo "bad input was $arg"
fi
done
If condition should be like this:
if [[ ! "$i" =~ [^0-9] ]]; then
OR remove 2 negatives:
if [[ "$i" =~ [0-9] ]]; then
OR use glob:
if [[ "$i" == *[0-9]* ]]; then
Which means $i contains a digit 0-9
Update: Based on your comments it looks like you are looking for BASH variable indirection like this script check-num.sh:
#!/bin/bash
for (( i=1; i<=$#; i++ )); do
[[ "${!i}" != *[0-9]* ]] && echo "bad input was ${!i}"
done
You can run this script as: ./check-num.sh 1 2 x 4 a
Note how ${!i} syntax is being used here to access the variable's $1, $2, $3 etc that is called BASH variable indirection. You shouldn't use $$i for this purpose.
As per BASH manual:
If the first character of parameter is an exclamation point, a level of variable indirection is introduced. Bash uses the value of the variable formed from
the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the
value of parameter itself.
Use something like this :
for i in "$#"; do
[[ $i =~ .*[^0-9].* ]] || echo "bad input was $i"
done
N.B : It's not necessary to use doubles quotes arround the variable with the [[ internal instruction.
I tried to play around with bash, and even tried documentation and few threads though I cannot seem to get it right.
S=(true false)
if (( ${S[0]} || ${S[1]} ))
then
echo "true"
else
echo "false"
fi
how can the boolean expression be evaluated under bash? (any chance to fix the snippet above?)
is there any possibility to evaluate it without if, i.e. assign it to a variable directly (no if manipulation)?
Instead of S=(true false), you need to create your array like this:
S=(1 0)
Then this if block:
if (( ${S[0]} || ${S[1]} ))
then
echo "true"
else
echo "false"
fi
will output:
true
Please note that true/false are treated as literal strings "true" and "false" in BASH.
There isn't really such a thing as boolean in bash, only integral arithmetic expressions e.g. (( n )), which would return an exit code of 0 (no error or no failure code) if its value is greater than 1, or a nonzero code (has error code) if it evaluates to 0. if statements execute a then block if the command it calls returns 0 exit code, or on the else block otherwise. You can imitate boolean systems in bash like this though:
#!/bin/bash
true=1
false=0
A=true
B=false
# Compare by arithmetic:
if (( A || B )); then
echo "Arithmetic condition was true."
else
echo "Arithmetic condition was false."
fi
if (( (I > 1) == true )); then ## Same as (( I > 1 )) actually.
echo "Arithmetic condition was true."
else
echo "Arithmetic condition was false."
fi
# Compare by string
if [[ $A == true || $B == true ]]; then
echo "Conditional expression was true."
else
echo "Conditional expression was false."
fi
# Compare by builtin command
if "$A" || "$B"; then
echo "True concept builtin command was called."
else
echo "False concept builtin command was called."
fi
And I prefer the string comparison since it's a less hacky approach despite probably being a bit slower. But I could use the other methods too if I want too.
(( )), [[ ]], true, and false all are just builtin commands or expressions that return an exit code. It's better that you just think of the like that than thinking that they are really part of the shell's main syntax.
I am trying to write a script in bash that check the validity of a user input.
I want to match the input (say variable x) to a list of valid values.
what I have come up with at the moment is:
for item in $list
do
if [ "$x" == "$item" ]; then
echo "In the list"
exit
fi
done
My question is if there is a simpler way to do this,
something like a list.contains(x) for most programming languages.
Say list is:
list="11 22 33"
my code will echo the message only for those values since list is treated as an array and not a string,
all the string manipulations will validate 1 while I would want it to fail.
[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'
or create a function:
contains() {
[[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}
to use it:
contains aList anItem
echo $? # 0: match, 1: failed
how about
echo $list | grep -w -q $x
you could either check the output or $? of above line to make the decision.
grep -w checks on whole word patterns. Adding -q prevents echoing the list.
Matvey is right, but you should quote $x and consider any kind of "spaces" (e.g. new line) with
[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no'
so, i.e.
# list_include_item "10 11 12" "2"
function list_include_item {
local list="$1"
local item="$2"
if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then
# yes, list include item
result=0
else
result=1
fi
return $result
}
end then
`list_include_item "10 11 12" "12"` && echo "yes" || echo "no"
or
if `list_include_item "10 11 12" "1"` ; then
echo "yes"
else
echo "no"
fi
Note that you must use "" in case of variables:
`list_include_item "$my_list" "$my_item"` && echo "yes" || echo "no"
IMHO easiest solution is to prepend and append the original string with a space and check against a regex with [[ ]]
haystack='foo bar'
needle='bar'
if [[ " $haystack " =~ .*\ $needle\ .* ]]; then
...
fi
this will not be false positive on values with values containing the needle as a substring, e.g. with a haystack foo barbaz.
(The concept is shamelessly stolen form JQuery's hasClass()-Method)
You can use (* wildcards) outside a case statement, too, if you use double brackets:
string='My string';
if [[ $string == *My* ]]
then
echo "It's there!";
fi
If it isn't too long; you can just string them between equality along a logical OR comparison like so.
if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
echo In the list
fi
I had this exact problem and while the above is ugly it is more obvious what is going on than the other generalized solutions.
If your list of values is to be hard-coded in the script, it's fairly simple to test using case. Here's a short example, which you can adapt to your requirements:
for item in $list
do
case "$x" in
item1|item2)
echo "In the list"
;;
not_an_item)
echo "Error" >&2
exit 1
;;
esac
done
If the list is an array variable at runtime, one of the other answers is probably a better fit.
There's a cleaner way to check if string is in the list:
if [[ $my_str = #(str1|str2|str3) ]]; then
echo "string found"
fi
Consider exploiting the keys of associative arrays. I would presume this outperforms both regex/pattern matching and looping, although I haven't profiled it.
declare -A list=( [one]=1 [two]=two [three]='any non-empty value' )
for value in one two three four
do
echo -n "$value is "
# a missing key expands to the null string,
# and we've set each interesting key to a non-empty value
[[ -z "${list[$value]}" ]] && echo -n '*not* '
echo "a member of ( ${!list[*]} )"
done
Output:
one is a member of ( one two three )
two is a member of ( one two three )
three is a member of ( one two three )
four is *not* a member of ( one two three )
If the list is fixed in the script, I like the following the best:
validate() {
grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}
Then use validate "$x" to test if $x is allowed.
If you want a one-liner, and don't care about whitespace in item names, you can use this (notice -w instead of -x):
validate() { echo "11 22 33" | grep -F -q -w "$1"; }
Notes:
This is POSIX sh compliant.
validate does not accept substrings (remove the -x option to grep if you want that).
validate interprets its argument as a fixed string, not a regular
expression (remove the -F option to grep if you want that).
Sample code to exercise the function:
for x in "item 1" "item2" "item 3" "3" "*"; do
echo -n "'$x' is "
validate "$x" && echo "valid" || echo "invalid"
done
I find it's easier to use the form echo $LIST | xargs -n1 echo | grep $VALUE as illustrated below:
LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
...
fi
This works for a space-separated list, but you could adapt it to any other delimiter (like :) by doing the following:
LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
...
fi
Note that the " are required for the test to work.
Thought I'd add my solution to the list.
# Checks if element "$1" is in array "$2"
# #NOTE:
# Be sure that array is passed in the form:
# "${ARR[#]}"
elementIn () {
# shopt -s nocasematch # Can be useful to disable case-matching
local e
for e in "${#:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
# Usage:
list=(11 22 33)
item=22
if elementIn "$item" "${list[#]}"; then
echo TRUE;
else
echo FALSE
fi
# TRUE
item=44
elementIn $item "${list[#]}" && echo TRUE || echo FALSE
# FALSE
The shell built-in compgen can help here. It can take a list with the -W flag and return any of the potential matches it finds.
# My list can contain spaces so I want to set the internal
# file separator to newline to preserve the original strings.
IFS=$'\n'
# Create a list of acceptable strings.
accept=( 'foo' 'bar' 'foo bar' )
# The string we will check
word='foo'
# compgen will return a list of possible matches of the
# variable 'word' with the best match being first.
compgen -W "${accept[*]}" "$word"
# Returns:
# foo
# foo bar
We can write a function to test if a string equals the best match of acceptable strings. This allows you to return a 0 or 1 for a pass or fail respectively.
function validate {
local IFS=$'\n'
local accept=( 'foo' 'bar' 'foo bar' )
if [ "$1" == "$(compgen -W "${accept[*]}" "$1" | head -1)" ] ; then
return 0
else
return 1
fi
}
Now you can write very clean tests to validate if a string is acceptable.
validate "blah" || echo unacceptable
if validate "foo" ; then
echo acceptable
else
echo unacceptable
fi
Prior answers don't use tr which I found to be useful with grep. Assuming that the items in the list are space delimited, to check for an exact match:
echo $mylist | tr ' ' '\n' | grep -F -x -q "$myitem"
This will return exit code 0 if the item is in the list, or exit code 1 if it isn't.
It's best to use it as a function:
_contains () { # Check if space-separated list $1 contains line $2
echo "$1" | tr ' ' '\n' | grep -F -x -q "$2"
}
mylist="aa bb cc"
# Positive check
if _contains "${mylist}" "${myitem}"; then
echo "in list"
fi
# Negative check
if ! _contains "${mylist}" "${myitem}"; then
echo "not in list"
fi
Late to the show? Following very easy variant was not clearly mentioned yet. I use case for checking simple lists, which is a general Bourne Shell idiom not relying on anything external nor extended:
haystack='a b c'
needle='b'
case " $haystack " in (*" $needle "*) :;; (*) false;; esac
Please note the use of the separator (here: SPC) to correcyly delimit the pattern: At the beginning and end of " $haystack " and likewise in the test of " $needle ".
This statement returns true ($?=0) in case $needle is in $haystack, false otherwise.
Also you can test for more than one $needle very easily. When there are several similar cases like
if (haystack.contains(needle1)) { run1() } elif (haystack.contains(needle2)) { run2() } else { run3() }
you can wrap this into the case, too:
case " $haystack " in (*" $needle1 "*) run1;; (*" $needle2 "*) run2;; (*) run3;; esac
and so on
This also works for all lists with values which do not include the separator itself, like comma:
haystack=' a , b , c '
needle=' b '
case ",$haystack," in (*",$needle,"*) :;; (*) false;; esac
Note that if values can contain anything including the separator sequence (except NUL, as shells do not suport NUL in variables as you cannot pass arguments containing NUL to commands) then you need to use arrays. Arrays are ksh/bashisms and not supported by "ordinary" POSIX/Bourne shells. (You can work around this limitation using $# in POSIX-Shells, but this is something completely different than what was aked here.)
Can the (*) false part be left away?
No, as this is the critical return value. By default case returns true.
Yes if you do not need the return value and put your processing at the location of the :
Why the :;;
We could also write true;;, but I am used to use : instead of true because it is shorter and faster to type
Also I consider not writing anything bad practice, as it is not obvious to everybody that the default return value of case is true.
Also "leaving out" the command usually indicates "something was forgotten here". So putting a redundant ":" there clearly indicates "it is intended to do nothing else than return true here".
In bash you can also use ksh/bashisms like ;& (fallthroug) or ;;& (test other patterns) to express if (haystack.contains(needle1)) { run1(); }; if (haystack.contains(needle2)) { run2(); }
Hence usually case is much more maintainable than other regex constructs. Also it does not use regex, it only use shell patterns, which might even be faster.
Reusable function:
: Needle "list" Seperator_opt
NeedleListSep()
{
if [ 3 -gt $# ];
then NeedleListSep "$1" "$2" " ";
else case "$3$2$3" in (*"$3$1$3"*) return 0;; esac; return 1;
fi;
}
In bash you can simplify this to
: Needle "list" Seperator_opt
NeedleListSep()
{
local s="${3-" "}";
case "$s$2$s" in (*"$s$1$s"*) return 0;; esac; return 1;
}
Use like this
Test() {
NeedleListSep "$1" "a b c" && echo found $1 || echo no $1;
NeedleListSep "$1" "a,b,c" ',' && echo found $1 || echo no $1;
NeedleListSep "$1" "a # b # c" ' # ' && echo found $1 || echo no $1;
NeedleListSep "$1" "abc" '' && echo found $1 || echo no $1;
}
Test a
Test z
As shown above, this also works for degerated cases where the separator is the empty string (so each character of the list is a needle). Example:
Test
returns
no
no
no
found
As the empty string is cleary part of abc in case your separator is the empty string, right?
Note that this function is Public Domain as there is absolutely nothing to it which can be genuinely copyrighted.
An alternative solution inspired by the accepted response, but that uses an inverted logic:
MODE="${1}"
echo "<${MODE}>"
[[ "${MODE}" =~ ^(preview|live|both)$ ]] && echo "OK" || echo "Uh?"
Here, the input ($MODE) must be one of the options in the regular expression ('preview', 'live', or 'both'), contrary to matching the whole options list to the user input. Of course, you do not expect the regular expression to change.
Examples
$ in_list super test me out
NO
$ in_list "super dude" test me out
NO
$ in_list "super dude" test me "super dude"
YES
# How to use in another script
if [ $(in_list $1 OPTION1 OPTION2) == "NO" ]
then
echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2"
exit;
fi
in_list
function show_help()
{
IT=$(CAT <<EOF
usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ...
e.g.
a b c d -> NO
a b a d -> YES
"test me" how "test me" -> YES
)
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ "$#" -eq 0 ]; then
show_help
fi
SEARCH_FOR=$1
shift;
for ITEM in "$#"
do
if [ "$SEARCH_FOR" == "$ITEM" ]
then
echo "YES"
exit;
fi
done
echo "NO"
Assuming TARGET variable can be only 'binomial' or 'regression', then following would do:
# Check for modeling types known to this script
if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then
echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2
usage
fi
You could add more strings into the list by separating them with a | (pipe) character.
Advantage of using egrep, is that you could easily add case insensitivity (-i), or check more complex scenarios with a regular expression.
This is almost your original proposal but almost a 1-liner. Not that complicated as other valid answers, and not so depending on bash versions (can work with old bashes).
OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella"
for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done
[ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }
I guess my proposal can still be improved, both in length and style.
Simple oneliner...
if [[ " 11 22 33 " == *" ${x} "* ]]; then echo "${x} is in the list"; fi;
Add before fi: else echo "${x} is NOT in the list";
The script below implements contains function for a list.
function contains {
local target=$1
shift
printf '%s\n' "$#" | grep -x -q "$target"
out=$?
(( out = 1 - out ))
return $out
}
If you convert a string based on white space into a list and use it, it seems to be solved as follows.
list="11 22 33"
IFS=" " read -ra parsed_list <<< "$list"
# parsed_list would be ("11" "22" "33")
contains "11" "${parsed_list[#]}"
echo $? # 1
contains "22" "${parsed_list[#]}"
echo $? # 1
contains "1" "${parsed_list[#]}"
echo $? # 0
contains "11 22" "${parsed_list[#]}"
echo $? # 0
I would like to check if a string begins with "node" e.g. "node001". Something like
if [ $HOST == user* ]
then
echo yes
fi
How can I do it correctly?
I further need to combine expressions to check if HOST is either "user1" or begins with "node"
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
> > > -bash: [: too many arguments
How can I do it correctly?
This snippet on the Advanced Bash Scripting Guide says:
# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.
[[ $a == z* ]] # True if $a starts with a "z" (wildcard matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
So you had it nearly correct; you needed double brackets, not single brackets.
With regards to your second question, you can write it this way:
HOST=user1
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes1
fi
HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
echo yes2
fi
Which will echo
yes1
yes2
Bash's if syntax is hard to get used to (IMO).
If you're using a recent version of Bash (v3+), I suggest the Bash regex comparison operator =~, for example,
if [[ "$HOST" =~ ^user.* ]]; then
echo "yes"
fi
To match this or that in a regex, use |, for example,
if [[ "$HOST" =~ ^user.*|^host1 ]]; then
echo "yes"
fi
Note - this is 'proper' regular expression syntax.
user* means use and zero-or-more occurrences of r, so use and userrrr will match.
user.* means user and zero-or-more occurrences of any character, so user1, userX will match.
^user.* means match the pattern user.* at the begin of $HOST.
If you're not familiar with regular expression syntax, try referring to this resource.
Note that the Bash =~ operator only does regular expression matching when the right hand side is UNQUOTED. If you do quote the right hand side, "any part of the pattern may be quoted to force it to be matched as a string.". You should not quote the right hand side even when doing parameter expansion.
I always try to stick with POSIX sh instead of using Bash extensions, since one of the major points of scripting is portability (besides connecting programs, not replacing them).
In sh, there is an easy way to check for an "is-prefix" condition.
case $HOST in node*)
# Your code here
esac
Given how old, arcane and crufty sh is (and Bash is not the cure: It's more complicated, less consistent and less portable), I'd like to point out a very nice functional aspect: While some syntax elements like case are built-in, the resulting constructs are no different than any other job. They can be composed in the same way:
if case $HOST in node*) true;; *) false;; esac; then
# Your code here
fi
Or even shorter
if case $HOST in node*) ;; *) false;; esac; then
# Your code here
fi
Or even shorter (just to present ! as a language element -- but this is bad style now)
if ! case $HOST in node*) false;; esac; then
# Your code here
fi
If you like being explicit, build your own language element:
beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
Isn't this actually quite nice?
if beginswith node "$HOST"; then
# Your code here
fi
And since sh is basically only jobs and string-lists (and internally processes, out of which jobs are composed), we can now even do some light functional programming:
beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }
all() {
test=$1; shift
for i in "$#"; do
$test "$i" || return
done
}
all "beginswith x" x xy xyz ; checkresult # Prints TRUE
all "beginswith x" x xy abc ; checkresult # Prints FALSE
This is elegant. Not that I'd advocate using sh for anything serious -- it breaks all too quickly on real world requirements (no lambdas, so we must use strings. But nesting function calls with strings is not possible, pipes are not possible, etc.)
You can select just the part of the string you want to check:
if [ "${HOST:0:4}" = user ]
For your follow-up question, you could use an OR:
if [[ "$HOST" == user1 || "$HOST" == node* ]]
I prefer the other methods already posted, but some people like to use:
case "$HOST" in
user1|node*)
echo "yes";;
*)
echo "no";;
esac
Edit:
I've added your alternates to the case statement above
In your edited version you have too many brackets. It should look like this:
if [[ $HOST == user1 || $HOST == node* ]];
While I find most answers here quite correct, many of them contain unnecessary Bashisms. POSIX parameter expansion gives you all you need:
[ "${host#user}" != "${host}" ]
and
[ "${host#node}" != "${host}" ]
${var#expr} strips the smallest prefix matching expr from ${var} and returns that. Hence if ${host} does not start with user (node), ${host#user} (${host#node}) is the same as ${host}.
expr allows fnmatch() wildcards, thus ${host#node??} and friends also work.
Since # has a meaning in Bash, I got to the following solution.
In addition I like better to pack strings with "" to overcome spaces, etc.
A="#sdfs"
if [[ "$A" == "#"* ]];then
echo "Skip comment line"
fi
Adding a tiny bit more syntax detail to Mark Rushakoff's highest rank answer.
The expression
$HOST == node*
Can also be written as
$HOST == "node"*
The effect is the same. Just make sure the wildcard is outside the quoted text. If the wildcard is inside the quotes it will be interpreted literally (i.e. not as a wildcard).
#OP, for both your questions you can use case/esac:
string="node001"
case "$string" in
node*) echo "found";;
* ) echo "no node";;
esac
Second question
case "$HOST" in
node*) echo "ok";;
user) echo "ok";;
esac
case "$HOST" in
node*|user) echo "ok";;
esac
Or Bash 4.0
case "$HOST" in
user) ;&
node*) echo "ok";;
esac
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi
doesn't work, because all of [, [[, and test recognize the same nonrecursive grammar. See section CONDITIONAL EXPRESSIONS on your Bash man page.
As an aside, the SUSv3 says
The KornShell-derived conditional command (double bracket [[]]) was removed from the shell command language description in an early proposal. Objections were raised that the real problem is misuse of the test command ([), and putting it into the shell is the wrong way to fix the problem. Instead, proper documentation and a new shell reserved word (!) are sufficient.
Tests that require multiple test operations can be done at the shell level using individual invocations of the test command and shell logicals, rather than using the error-prone -o flag of test.
You'd need to write it this way, but test doesn't support it:
if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi
test uses = for string equality, and more importantly it doesn't support pattern matching.
case / esac has good support for pattern matching:
case $HOST in
user1|node*) echo yes ;;
esac
It has the added benefit that it doesn't depend on Bash, and the syntax is portable. From the Single Unix Specification, The Shell Command Language:
case word in
[(]pattern1) compound-list;;
[[(]pattern[ | pattern] ... ) compound-list;;] ...
[[(]pattern[ | pattern] ... ) compound-list]
esac
grep
Forgetting performance, this is POSIX and looks nicer than case solutions:
mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
echo matches
fi
Explanation:
printf '%s' to prevent printf from expanding backslash escapes: Bash printf literal verbatim string
grep -q prevents echo of matches to stdout: How to check if a file contains a specific string using Bash
grep -E enables extended regular expressions, which we need for the ^
Keep it simple
word="appel"
if [[ $word = a* ]]
then
echo "Starts with a"
else
echo "No match"
fi
I tweaked #markrushakoff's answer to make it a callable function:
function yesNo {
# Prompts user with $1, returns true if response starts with y or Y or is empty string
read -e -p "
$1 [Y/n] " YN
[[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}
Use it like this:
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] y
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] Y
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] yes
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n]
true
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] n
false
$ if yesNo "asfd"; then echo "true"; else echo "false"; fi
asfd [Y/n] ddddd
false
Here is a more complex version that provides for a specified default value:
function toLowerCase {
echo "$1" | tr '[:upper:]' '[:lower:]'
}
function yesNo {
# $1: user prompt
# $2: default value (assumed to be Y if not specified)
# Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string
local DEFAULT=yes
if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
if [[ "$DEFAULT" == y* ]]; then
local PROMPT="[Y/n]"
else
local PROMPT="[y/N]"
fi
read -e -p "
$1 $PROMPT " YN
YN="$( toLowerCase "$YN" )"
{ [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}
Use it like this:
$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi
asfd [y/N]
false
$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi
asfd [y/N] y
true
$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi
asfd [Y/n] n
false