TransWikia.com

Prepend and append a string to each element of $* in shell

Unix & Linux Asked by erenon on November 26, 2021

I’m looking for the simplest solution that takes $* as input, and expands to each element prefixed and suffixed with a given string:

$*=foo bar baz
<solution(x,y)>=xfooy xbary xbazy

I can do either prepending or appending, but not both:

echo ${*/#/x}
# prints xfoo xbar xbaz
echo ${*/%/y}
# prints fooy bary bazy

I’m unable to combine the two solutions. The documentation claims the value returned by the expansion in the parameter=* case is a list, but I’m unable to use it as such. I want to pass the resulting array of values to a further command as separate arugments, therefore simply building a single string wouldn’t work.

3 Answers

Given a list in $@
... print it

set -- foo bar baz
printf '%sn' "$@"
foo
bar
baz

... perform a list op

set -- $(printf 'x%sy ' "$@")
printf '%sn' "$@"
xfooy
xbary
xbazy

... stringify list

printf '%sn' "$*"
xfooy xbary xbazy

No special bash features involved.

Answered by martin8guest on November 26, 2021

${var/pattern/replacement} is a ksh93 parameter expansion operator, also supported by zsh, mksh, and bash, though with variations (mksh's currently can't operate on arrays).

ksh93

In ksh93, you'd do ${var/*/xy} to prefix the expansion of $var with x and suffix with y, and ${array[@]/*/xy} to do that for each element of the array.

So, for the array of positional parameters:

print -r -- "${@/*/xy}"

(beware however that like for your ${*/#/x}, it's buggy when the list of positional parameters is empty).

zsh

zsh's equivalent of ksh93's to recall the matched string in the replacement is $MATCH, but only if you use (#m) in the pattern (for which you need the extendedglob option):

set -o extendedglob
print -r -- "${@/(#m)*/x${MATCH}y}"

But in zsh, you can nest parameter expansions, so you can also do:

print -r -- ${${@/#/x}/%/y}

Though you would probably rather use the $^array operator which turns on rcexpandparam for the expansion of that array, making it behave like brace expansion:

print -r -- x$^@y

Or you could use:

printf -v argv x%sy "$@"

To modify $@ (aka $argv in zsh) in-place (here assuming "$@" is not the empty list).

bash

In the bash shell, you'd probably need to do it in two steps with an intermediary array as shown by @L.ScottJohnson, or modifying $@ in place with:

set -- "${@/#/x}"
echo -E "${@/%/y}"

(here assuming the prefix (x in this case), doesn't start with -).

POSIXly

You could modify the positional parameters in-place with a loop:

for i do
  set -- "$@" "x${i}y"
  shift
done
echo "$@"

(though beware that echo can't be used portably to display arbitrary data that may contain backslash characters or start with -)

Note

Note that the $* form of parameter expansion (which is only useful quoted), is the one that is meant to concatenate the positional parameters (with the first character of $IFS, SPC by default). You need $@ (again, quoted) to expand to all positional parameters as separated arguments. Unquoted, $* and $@ make little sense (except in zsh where they expand to the non-empty positional parameters) as they would be subject to split+glob, and the behaviour varies between shells.

Answered by Stéphane Chazelas on November 26, 2021

#!/bin/bash
echo $*
FIELDS=("${@/#/x}")
FIELDS=("${FIELDS[@]/%/y}")
echo "${FIELDS[*]}"

When run:

$ t.sh foo bar baz
foo bar baz
xfooy xbary xbazy

Answered by L. Scott Johnson on November 26, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP