When expanding ${*:0:80} which should display the 1st chars of $1-$n, bash will prepend $0 and won’t honor the 80 size limit.
Even wired, if I do some shift, $0 is still displayed while, shifted element have disappeared.
le say we create the /tmp/test.sh (chmod +x /tmp/test.sh)
#!/bin/bash
A=$1
shift
B=$2
shift
C="$*"
echo "test1: ${*:0:80}"
echo "test2: ${C:0:80}"
When running it on CentOS-7.6.1810 bash (bash-4.2.46-31.el7.x86_64), here is the strange output: (test1 wrong ($0 printed and message not truncated to 80 chars), test2 ok)
/tmp/test.sh word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17 word18 word19 word20
test1: /tmp/test.sh word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17 word18 word19 word20
test2: word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 wor
When running it on MacOS Mojav 10.14.6 (version 3.2.57(1)-release (x86_64-apple-darwin18)),
here is another strange output: (test1 still wrong (not truncated), test2 ok)
/tmp/test.sh word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17 word18 word19 word20
test1: word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 word16 word17 word18 word19 word20
test2: word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 wor
IMHO, Expected result should be:
- $0 shouldn’t be outputted
- output ($2-$n) should be truncated to 80 chars
Do I miss something or do I hit a bash bug or is this inconsistency across Oses expected?
2
Answers
Substring expansion for
*
is not documented at all, though it is for@
:That is, substring expansion selects a range of positional parameters (including, for some reason, the special parameter $0, probably to make indexing intuitive); it does not apply substring expansion to each parameter individually. The documentation has no mention of what happens if the parameter is
*
(though it explicitly defines the behavior of an array indexed by either@
or*
).${*:x:y}
does appear to behave identically to${@:x:y}
, though it isn’t clear whether this is an artifact of the implementation that could change or if there is an oversight in the documentation.zsh
does define${*:0:80}
as the first 80 characters of the single string resulting from the concatenation of the positional parameters, for what it’s worth.In a
${var:off:len}
expansion form, if thevar
is*
or@
, theoff
andlen
will select a number oflen
positional parameters starting with indexoff
instead of a substring ofvar
:Notice the difference between
$*
when$@
when quoted: the first will join the parameters into a single word with the first character of theIFS
variable, the second will expand each parameter into a separate word.This is consistent with how arrays indexed by
[*]
or[@]
work, and is the same as inksh93
(where IIRC the${var:off:len}
construct first appeared).In the
bash
docs, this is not documented for$*
, but only for$@
and for the arrays indexed by[*]
or[@]
.But the source code follows the same paths and diverge at the same points for
*
or@
: so while it is perfectly possible that they may change it one day by adding a special case (maybe just out of spite ;-)), I find it highly unlikely.For completion, this is a snippet from
ksh93
‘s manpage, which describes this behavior:This is very different in
zsh
(but even there, it is not inconsistent between positional parameters and arrays): when either$*
, an array subscripted by*
or a simple array name is used, all the elements will be first joined into a string, then the${var:off:len}
will extract a substring from it:In
bash
, if you want to join the positional arguments into a string, and extract a substring of it, use an intermediate variable: