2

I have an array containing strings to exclude with grep from the output of another program. I need to add an -e before each element. For instance:

exclude=("$0" /usr/sbin/crond)
needs-restarting | grep -Fwiv "${exclude[@]}"

Now I know in this case I could prepend --regexp= (or just -e) to each element like so: exclude=( "${exclude[@]/#/--regexp=}" )

But in the general case, how would I go about it? I came up with this but maybe there's a simpler way.

i=0
for elem in "${exclude[@]}"; do
  exclude[i]='-e'
  exclude[i+1]="$elem"
  ((i+=2))
done
declare -p exclude
2
  • What can be more general than the "${exclude[@]/#/foo}" that you're using? That seems pretty general to me, what am I missing?
    – terdon
    Jul 4 '20 at 16:13
  • I mean to make the, in this example, -e its own element. When doing that substitution, it would be added to the existing element, which wouldn't work correctly when using it as an argument as they would need to be separate tokens.
    – Bangaio
    Jul 4 '20 at 16:58
6

In bash 4.4+, you could do:

readarray -td '' array < <(
  ((${#array[@]})) && printf -- '-e\0%s\0' "${array[@]}"
)

Here using \0 as the delimiter as bash variables can't contain NUL bytes anyway. If you know the array is not going to be empty, you can skip the ((${#array[@]})) &&.

Example:

  • before:
    bash-5.0$ array=($'a\nb' '' 'c d' e)
    bash-5.0$ typeset -p array
    declare -a array=([0]=$'a\nb' [1]="" [2]="c d" [3]="e")
    
  • after:
    bash-5.0$ typeset -p array
    declare -a array=([0]="-e" [1]=$'a\nb' [2]="-e" [3]="" [4]="-e" [5]="c d" [6]="-e" [7]="e")
    

In zsh, you could use its array zipping operator:

opt=-e
(($#array == 0)) || array=("${(@)opt:^^array}")

Or this convoluted one:

set -o extendedglob # for (#m)
array=("${(Q@)"${(@z)array//(#m)*/-e ${(qq)MATCH}}"}")

Where we replace each element with -e <the-element-quoted> (with the qq flag), and then use z to parse that quoting back into a list of elements (where -e and <the-element-quoted> are then separated out), and Q to remove the quotes (and @ within quotes used to preserve empty elements if any).

2

You can simplify your proposed loop a little:

exclude_args=()
for elem in "${exclude[@]}"; do
  exclude_args+=('-e' "$elem")
done
exclude=("${exclude_args[@]}")   # Optional, if you want to replace the original array's contents
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.