4

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
    CommentedJul 4, 2020 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
    CommentedJul 4, 2020 at 16:58

2 Answers 2

9

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).

    3

    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

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.