Home > database >  Issue understanding a parameter expansion in a bash script
Issue understanding a parameter expansion in a bash script

Time:12-24

I am trying to understand what a parameter expansion does inside a bash script.

third_party_bash_script

#!/bin/sh
files="${*:--}"
# For my understanding I tried to print the contents of files
echo $files 

pkill bb_stream
if [ "x$VERBOSE" != "" ]; then
        ARGS=-v1
fi
while [ 1 ]; do cat $files; done | bb_stream $ARGS

When I run ./third_party_bash_script, all it prints is a hyphen - and nothing else. Since it did not make sense to me, I also tried to experiment with it in the terminal

$ set one="1" two="2" three="3"
$ files="${*:--}"
$ echo $files
one="1" two="2" three="3"
$ set four="4"
$ files="${*:--}"
four="4"

I can't seem to understand what it's doing. Could someone kindly help me with the interpretation of ${*:--} by the sh?

CodePudding user response:

"$@" is an array of the arguments passed to your script, "$*" is a string of all of those arguments concatenated with blanks in between.

"${*:--}" is the string of arguments if any were provided (:-), or - otherwise which means "take input from stdin" otherwise.

"${@:--}" is the array of arguments if any were provided (:-), or - otherwise which means "take input from stdin" otherwise.

$ cat file
foo
bar

$ cat tst.sh
#!/usr/bin/env bash

awk '{ print FILENAME, $0 }' "${@:--}"

When an arg is provided to the script, "$@" contains "file" so that is the arg that awk is called with:

$ ./tst.sh file
file foo
file bar

When no arg is provided to the script, "$@" is empty so awk is called with - (meaning read from stdin) as it's arg:

$ cat file | ./tst.sh
- foo
- bar

You almost always want to use "${@:--}" rather than "${*:--}" in this context, see https://unix.stackexchange.com/questions/41571/what-is-the-difference-between-and for more info on "$@" vs "$*".

CodePudding user response:

${param:-default} expands to the value of $param if $param is set and not empty, otherwise it expands to default.

$* is all the command-line arguments to the script.

In ${*:--}, param is * and default is -. If $* is not an empty string, it expands to the script arguments. If it's empty, it expands to the default value -.

This can be used in a script to implement the common behavior that a program reads from the files listed in its arguments, and reads from standard input if no filename arguments are given. Many commands treat an input filename argument - as standing for the standard input.

CodePudding user response:

NOTE: addressing OP's original, pre-edited post ...

See shell parameter expansion for a brief review of different options.

While the other answers reference the use of ${*:--} (and ${@:--}) as a alternate means of reading from stdin, OP's sample script is a bit simpler ... if the variable $* (ie, script's command line args) is empty then replace with the literal string -.

We can see this with a few examples:

$ third_party_bash_script
-

$ third_party_bash_script a b c
a b c

$ echo 'a b c' | third_party_bash_script
-

If we replace ${*:--} with ${*:-REPLACEMENT}:

$ third_party_bash_script
REPLACEMENT

$ third_party_bash_script a b c
a b c

$ echo 'a b c' | third_party_bash_script
REPLACEMENT

I'm guessing in OP's actual script there's more going on with the $files variable so in order to know for sure how the ${*:--} is being processed we'd need to see the actual script and how it's referencing the $files variable.


As for OP's set|files=|echo code snippets:

$ set one="1" two="2" three="3"
$ files="${*:--}"
$ echo $files
one=1 two=2 three=3

We can see the same behavior from the script with:

$ third_party_bash_script one="1" two="2" three="3"
one=1 two=2 three=3
  • Related