Bash数组:向数组中的每个元素添加前缀或后缀

11

我想构建一个涉及到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}

但是我不知道如何将它们组合起来并同时在数组的每个元素前缀和后缀添加内容。


听起来是个好主意。我认为你需要有多个级别(副本)的数组来支持每个${var/x/y}替换。我曾经使用类似于$( echo $(echo ${ig[@]} | sed 's/a/b/g;s/c/d/g;s/d/e$/;s/^f/g/') ) 的东西做过类似的事情。祝你好运! - shellter
4个回答

16

你无法同时轻松地做到这一点。幸运的是,你不需要这么做:

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

echo ${ignore[@]}

注意括号和双引号 - 它们确保在每次替换后,数组包含三个元素,即使涉及空格。


更好的是,祝大家好运。 - shellter
很好,你试图引用名称,但并不完美,例如如果 ignore 包含一些奇怪的东西,比如 ". - musiphil
"同时进行不可行"的说法是错误的,请参考 https://dev59.com/y2Mm5IYBdhLWcg3wdeua#35550176。 - setempler

3

一般来说,应该始终将引用形式的每个变量(例如 "${ignore[@]}")视为一个整体,而不是试图自己插入引号(就像在 SQL 中应该使用参数化语句而不是转义输入一样),因为手动转义很难做到完美;例如,假设一个变量包含一个引号。

在这方面,我建议创建一个数组,使find中的每个参数单词成为一个元素:("-o" "-path" "$dir/archive" "-prune" "-o" "-path" "$dir/crl" "-prune" "-o" "-path" "$dir/cfg" "-prune") (一个 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[@]}" ...

3

请看printf,它同样可以完成这项工作:

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


2
如果我理解正确,
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

哇,这也真的很性感。 - jh314
1
这将打印出-o -path "$dir/archive crl cfg" -prune - musiphil
@jm666:我真的把整个部分拖过来,复制了下来,然后粘贴到我的终端里。结果还是一样。 - musiphil
@musiphil 你的操作系统是什么?我的是OS X - 所以是BSD...也许GNU版本需要 -J 而不是 -I - clt60
我可以通过“修复”输入端来使其工作:不使用echo ${ignore[@]},而是使用printf "%s\n" "${ignore[@]}"。(尽管我仍然认为使用外部进程并不是最优解。) - musiphil
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接