Массивы Bash: добавление и добавление к каждому элементу в массиве

Я пытаюсь построить длинную команду с участием find. У меня есть масса каталогов, которые я хочу игнорировать, и я хочу отформатировать этот каталог в команду.

в принципе, я хочу, чтобы преобразовать этот массив:

declare -a ignore=(archive crl cfg)

в:

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

таким образом, я могу просто добавить каталоги в массив и find команда отрегулирует соответственно.

до сих пор я понял, как добавить или добавить используя

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

но я не знаю, как объединить их и одновременно добавить и добавить к каждому элементу массива.

3 ответов


вы не можете делать это одновременно. К счастью, вам не нужно:

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

echo ${ignore[@]}

обратите внимание на круглые скобки и двойные кавычки - они гарантируют, что массив содержит три элемента после каждой подстановки, даже если есть пробелы.


в общем, вы должны стремиться всегда обрабатывать каждую переменную в кавычках (например,"${ignore[@]}") вместо того, чтобы пытаться вставить кавычки самостоятельно (так же, как вы должны использовать параметризованные операторы вместо экранирования ввода в SQL), потому что трудно быть совершенным путем ручного экранирования; например, предположим, что переменная содержит кавычки.

в этом отношении я бы нацелился на создание массива, где каждое слово аргумента для find становится элементом: ("-o" "-path" "$dir/archive" "-prune" "-o" "-path" "$dir/crl" "-prune" "-o" "-path" "$dir/cfg" "-prune") (a 12-элемент массива).

к сожалению, Bash, похоже, не поддерживает форму расширения параметров, где каждый элемент расширяется до нескольких слов. (p{1,2,3}q увеличивается до p1q p2q p3q, но с a=(1 2 3), p"${a[@]}"q увеличивается до p1 2 3q.) Поэтому вам нужно прибегнуть к циклу:

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[@]}" ...

если я правильно понял,

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

печать

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

работает только с xargs что имеет следующие переключатели:

 -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 [A-Z]* | xargs -J % cp -rp % destdir