开发者

Bash parameter expansion

I have a script which uses the following logic:

if [ ! -z "$1" ]; then         # if any parameter is supplied
    ACTION=                    # clear $ACTION
else
    ACTION=echo                # otherwise, set i开发者_Python百科t to 'echo'
fi

This works fine, as-is. However, in reading the Shell Parameter Expansion section of the bash manual, it seems this should be able to be done in a single step. However, I can't quite wrap my head around how to do it.

I've tried:

ACTION=${1:-echo}              # ends up with $1 in $ACTION

ACTION=${1:+}
ACTION=${ACTION:-echo}         # ends up always 'echo'

and a few ways of nesting them, but nesting seems to be disallowed as far as I can tell.

I realize I've already got a working solution, but now I'm genuinely curious if this is possible. It's something that would be straightforward with a ternary operator, but I don't think bash has one.

If this is possible, I'd like to see the logic to do this seeming two-step process, with no if/else constructs, but using only any combination of the Shell Parameter Expansion features.

Thank you.


EDIT for elderarthis:

The remainder of the script is just:

find . -name "*\?[NMSD]=[AD]" -exec ${ACTION} rm -f "{}" +

I just want ACTION=echo as a sanity check against myself, hence, passing any argument will actually do the deletion (by nullifying ${ACTION}, whereas passing no args leaves echo in there.

And I know TIMTOWTDI; I'm looking to see if it can be done with just the stuff in the Shell Parameter Expansion section :-)


EDIT for Mikel:

$ cat honk.sh
#!/bin/bash
ACTION=${1-echo}
echo $ACTION
$ ./honk.sh
echo
$ ./honk.sh foo
foo

The last needs to have ACTION='', and thus return a blank line/null value.


If I insisted on doing it in fewer than 4 lines and no sub-shell, then I think I'd use:

ACTION=${1:+' '}
: ${ACTION:=echo}

This cheats slightly - it creates a blank action rather than an empty action if there is an argument to the script. If there is no argument, then ACTION is empty before the second line. On the second line, if action is empty, set it to 'echo'. In the expansion, since you (correctly) do not quote $ACTION, no argument will be passed for the blank.

Tester (xx.sh):

ACTION=${1:+' '}
: ${ACTION:=echo}

echo $ACTION rm -f a b c

Tests:

$ sh xx.sh 1
rm -f a b c
$ sh xx.sh
echo rm -f a b c
$ sh xx.sh ''
echo rm -f a b c
$ 

If the last line is incorrect, then remove the colon from before the plus.


If a sub-shell is acceptable, then one of these two single lines works:

ACTION=$([ -z "$1"     ] && echo echo)
ACTION=$([ -z "${1+X}" ] && echo echo)

The first corresponds to the first version shown above (empty first arguments are treated as absent); the second deals with empty arguments as present. You could write:

ACTION=$([ -z "${1:+X}" ] && echo echo)

to make the relation with the second clearer - except you're only going to use one or the other, not both.


Since the markdown notation in my comment confused the system (or I got it wrong but didn't get to fix it quickly enough), my last comment (slightly amended) should read:

The notation ${var:+' '} means 'if $var is set and is not empty, then use what follows the +' (which, in this case, is a single blank). The notation ${var+' '} means 'if $var is set - regardless of whether it is empty or not - then use what follows the +'. These other expansions are similar:

  • ${var:=X} - set $var to X unless it already has a non-empty value.
  • ${var:-X} - expands to $var if it has a non-empty value and expands to X if $var is unset or is empty

Dropping the colon removes the 'empty' part of the test.


ACTION=${1:-echo}

is correct.

Make sure it's near the top of your script before anything modifies $1 (e.g. before any set command). Also, it wouldn't work inside a function, because $1 would be the first parameter to the function.

Also check if $1 is set but null, in which case fix how you're calling it, or use ACTION=${1-echo} (note there is no :).


Update

Ah, I assumed you must have meant the opposite, because it didn't really make sense otherwise.

It still seems odd, but I guess as a mental exercise, maybe you want something like this:

#!/bin/bash
shopt -s extglob
ACTION=$1
ACTION=${ACTION:-echo}
ACTION=${ACTION/!(echo)/}    # or maybe ACTION=${ACTION#!(echo)}
echo ACTION=$ACTION

It's not quite right: it gives ACTION=o, but I think something along those lines should work.

Further, if you pass echo as $1, it will stay as echo, but I don't think that's a bad thing.

It's also terribly ugly, but you knew that when asking the question. :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜