Reputation: 7342
I want to pre- and postfix an array in bash similar to brace expansion.
Say I have a bash array
ARRAY=( one two three )
I want to be able to pre- and postfix it like the following brace expansion
echo prefix_{one,two,three}_suffix
The best I've been able to find uses bash regex to either add a prefix or a suffix
echo ${ARRAY[@]/#/prefix_}
echo ${ARRAY[@]/%/_suffix}
but I can't find anything on how to do both at once. Potentially I could use regex captures and do something like
echo ${ARRAY[@]/.*/prefix_$1_suffix}
but it doesn't seem like captures are supported in bash variable regex substitution. I could also store a temporary array variable like
PRE=(${ARRAY[@]/#/prefix_})
echo ${PRE[@]/%/_suffix}
This is probably the best I can think of, but it still seems sub par. A final alternative is to use a for loop akin to
EXPANDED=""
for E in ${ARRAY[@]}; do
EXPANDED="prefix_${E}_suffix $EXPANDED"
done
echo $EXPANDED
but that is super ugly. I also don't know how I would get it to work if I wanted spaces anywhere the prefix suffix or array elements.
Upvotes: 46
Views: 23026
Reputation: 520
Bash 5.2 uses substring matching by default (see patsub_replacement):
$ a=(1 2 3)
$ echo "${a[@]/*/'prefix'&'suffix'}"
prefix1suffix prefix2suffix prefix3suffix
Upvotes: 4
Reputation: 11
Perhaps this would be the most elegant solution:
$ declare -a ARRAY=( one two three )
$ declare -p ARRAY
declare -a ARRAY=([0]="one" [1]="two" [2]="three")
$
$ IFS=$'\n' ARRAY=( $(printf 'prefix %s_suffix\n' "${ARRAY[@]}") )
$
$ declare -p ARRAY
declare -a ARRAY=([0]="prefix one_suffix" [1]="prefix two_suffix" [2]="prefix three_suffix")
$
$ printf '%s\n' "${ARRAY[@]}"
prefix one_suffix
prefix two_suffix
prefix three_suffix
$
By using IFS=$'\n' in front of the array reassignment (being valid only for this assignment line), it is possible to preserve spaces in both prefix & suffix as well as array element strings.
Using "printf" is rather handy, because it allows to apply the format string (1st argument) to each additional string argument supplied to the call of "printf".
Upvotes: 1
Reputation: 1028
Prettier but essentially the same as the loop solution:
$ ARRAY=(A B C)
$ mapfile -t -d $'\0' EXPANDED < <(printf "prefix_%s_postfix\0" "${ARRAY[@]}")
$ echo "${EXPANDED[@]}"
prefix_A_postfix prefix_B_postfix prefix_C_postfix
mapfile
reads rows into elements of an array. With -d $'\0'
it instead reads null-delimited strings and -t
omits the delimiter from the result. See help mapfile
.
Upvotes: 3
Reputation: 1374
Bash brace expansion don't use regexes. The pattern used is just some shell glob, which you can find in bash manual 3.5.8.1 Pattern Matching.
Your two-step solution is cool, but it needs some quotes for whitespace safety:
ARR_PRE=("${ARRAY[@]/#/prefix_}")
echo "${ARR_PRE[@]/%/_suffix}"
You can also do it in some evil way:
eval "something $(printf 'pre_%q_suf ' "${ARRAY[@]}")"
Upvotes: 29
Reputation: 1231
I have exactly the same question, and I come up with the following solution using sed's word boundary match mechanism:
myarray=( one two three )
newarray=( $(echo ${myarray[*]}|sed "s/\(\b[^ ]\+\)/pre-\1-post/g") )
echo ${newarray[@]}
> pre-one-post pre-two-post pre-three-post
echo ${#newarray[@]}
> 3
Waiting for more elegant solutions...
Upvotes: -1
Reputation: 701
For arrays:
ARRAY=( one two three )
(IFS=,; eval echo prefix_\{"${ARRAY[*]}"\}_suffix)
For strings:
STRING="one two three"
eval echo prefix_\{${STRING// /,}\}_suffix
eval
causes its arguments to be evaluated twice, in both cases first evaluation results in
echo prefix_{one,two,three}_suffix
and second executes it. For array case subshell is used to avoid overwiting IFS
You can also do this in zsh:
echo ${${ARRAY[@]/#/prefix_}/%/_suffix}
Upvotes: 1
Reputation: 361605
Your last loop could be done in a whitespace-friendly way with:
EXPANDED=()
for E in "${ARRAY[@]}"; do
EXPANDED+=("prefix_${E}_suffix")
done
echo "${EXPANDED[@]}"
Upvotes: 18