1

Given

hasArgs=a:b:c:
x=b
if [[ ${hasArgs} = *$x:* ]] ; then …

I wish I could replace the above double brackets test with a less Bash specific alternative (meaning no subshell/command etc.)

Parameter expansion does not help because it does not expend variables.

if [ "${hasArgs}" != "${hasArgs#$x:}" ] ; then …

Is there any simple (one line) solution that would fit this use case?

3
  • This is not bash specific but ksh specific. Double brackets have been introduced by ksh88.
    – schily
    Aug 3, 2018 at 22:38
  • I wanted to say POSIX compliant, but thank's I will remember where double brackets are coming from.
    – Stphane
    Aug 4, 2018 at 8:57
  • Double brackets have intentionally not put in to POSIX even though ksh88 was the master for the POSIX shell.
    – schily
    Aug 4, 2018 at 9:01

3 Answers 3

5

It's not a one-liner, but this is quite readable and can be expanded to check for any number of arguments:

hasArgs="a:b:c"
x=b
case "$hasArgs" in
    *$x*)
        echo "$x is present"
        ;;
esac
1
  • I should have told about this but I already use a case structure somewhere else in my script (meaning I am aware of the glob capability), this is the reason why I asked for a one-liner. But not everyone may be aware of this so your answer is surely welcome.
    – Stphane
    Aug 4, 2018 at 8:07
3

The following will work in sh:

if echo "$hasArgs" | grep -q "$x"; then
2
  • 2
    Congrats on 10k!
    – slm
    Aug 3, 2018 at 22:43
  • Looks like a good alternative to [ since both are external executables. (+ one-liner)
    – Stphane
    Aug 4, 2018 at 8:45
2

Actually, you can use the standard parameter expansions here (you were just missing the wildcard):

$ args=a:b:c:
$ x=b
$ echo "${args%$x:*}"
a:
$ if [ "$args" != "${args#*$x:}" ]; then echo 'b: is present'; fi
b: is present

(or equally "${args%$x:*}")

The downside is that you need to have the colon after b, so a:b or a plain b wouldn't match, and it would also match a:bb:c. ([[ $var = *$x:* ]] as well as the obvious pattern match with case have the same disadvantage.)

Matching all of b, b:c, a:b, a:b:c, while not matching a:bb:c would be a bit more tricky. All I can think of is something like this:

args=a:b:c
x=b
IFS=:                   # affects globally
set -f                  # affects globally...
for x in $args; do
    if [ "$x" = "b" ]; then
        echo "b is present"
    fi
done

Unless you want to write all the cases out:

args=a:b:c
x=b
found=0
case "$args" in
    $x)     found=1;;
    $x:*)   found=1;;
    *:$x)   found=1;;
    *:$x:*) found=1;;
esac
if [ "$found" = 1 ]; then ...; fi

Much easier with regexes (in Bash):

args=b:c
x=b
if [[ $args =~ (^|:)$x(:|$) ]]; then
    echo "b found"
fi
2
  • So (some?) parameter expansion(s) actually evaluate variables in their pattern prior to expansion, I missed the wildcard. Since there will always have a colon after the x variable, I can safely use this expression.
    – Stphane
    Aug 4, 2018 at 9:03
  • 2
    @Stphane, they all do, on the right hand side that takes a string, not on the left part that takes a parameter name. So, ${x#$y} works, but ${${x#foo}%bar} doesn't. Except in zsh.
    – ilkkachu
    Aug 4, 2018 at 9:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .