Bash arrays: adding and adding to each element of an array

I am trying to build a long command with find . I have an array of directories that I want to ignore, and I want to format this directory in the command.

Basically, I want to convert this array:

 declare -a ignore=(archive crl cfg) 

in it:

 -o -path "$dir/archive" -prune -o -path "$dir/crl" -prune -o -path "$dir/cfg" -prune 

That way, I can just add directories to the array, and the find will change accordingly.

So far I have been figuring out how to add or add using

 ${ignore[@]/#/-o -path \"\$dir/} ${ignore[@]/%/\" -prune} 

But I do not know how to combine them and at the same time add and add to each element of the array.

+6
source share
4 answers

You cannot do this at the same time. Fortunately, you do not need to:

 ignore=( archive crl cfg ) ignore=( "${ignore[@]/%/\" -prune}" ) ignore=( "${ignore[@]/#/-o -path \"\$dir/}" ) echo ${ignore[@]} 

Pay attention to parentheses and double quotes - they guarantee that the array contains three elements after each substitution, even if there are spaces.

+12
source

If I understand correctly,

 declare -a ignore=(archive crl cfg) a=$(echo ${ignore[@]} | xargs -n1 -I% echo -o -path '"$dir/%"' -prune) echo $a 

prints

 -o -path "$dir/archive" -prune -o -path "$dir/crl" -prune -o -path "$dir/cfg" -prune 

Only works with xargs , which has the following switches:

  -I replstr Execute utility for each input line, replacing one or more occurrences of replstr in up to replacements (or 5 if no -R flag is specified) arguments to utility with the entire line of input. The resulting arguments, after replacement is done, will not be allowed to grow beyond 255 bytes; this is implemented by concatenating as much of the argument containing replstr as possible, to the constructed arguments to utility, up to 255 bytes. The 255 byte limit does not apply to arguments to utility which do not contain replstr, and furthermore, no replacement will be done on utility itself. Implies -x. -J replstr If this option is specified, xargs will use the data read from standard input to replace the first occur- rence of replstr instead of appending that data after all other arguments. This option will not affect how many arguments will be read from input (-n), or the size of the command(s) xargs will generate (-s). The option just moves where those arguments will be placed in the command(s) that are executed. The replstr must show up as a distinct argument to xargs. It will not be recognized if, for instance, it is in the middle of a quoted string. Furthermore, only the first occurrence of the replstr will be replaced. For example, the following command will copy the list of files and directories which start with an uppercase letter in the current directory to destdir: /bin/ls -1d [AZ]* | xargs -J % cp -rp % destdir 
+2
source

In general, you should strive to always process each variable in a quoted form (for example, "${ignore[@]}" ) instead of trying to insert quotes yourself (just like you should use parameterized instructions instead of escaping the input in SQL) because it is difficult to be perfect by manually escaping; for example, suppose a variable contains a quotation mark.

In this regard, I would like to create an array where each argument word for find becomes an element: ("-o" "-path" "$dir/archive" "-prune" "-o" "-path" "$dir/crl" "-prune" "-o" "-path" "$dir/cfg" "-prune") (12-element array).

Unfortunately, Bash does not seem to support a parameter expansion form, where each element expands to a few words. ( p{1,2,3}q expands to p1q p2q p3q , but with a=(1 2 3) , p"${a[@]}"q expands to p1 2 3q .) So you need to refer to the loop :

 declare -a args=() for i in "${ignore[@]}" do args+=(-o -path "$dir/$i" -prune) # I'm not sure if you want to have # $dir expanded at this point; # otherwise, just use "\$dir/$i". done find ... "${args[@]}" ... 
+2
source

Check out printf , which also does the job:

printf -- '-o -path "$dir/%s" -prune ' ${ignore[@]}

+1
source

Source: https://habr.com/ru/post/949271/


All Articles