包含“*”(星号)的数组元素的自动扩展问题

包含“*”(星号)的数组元素的自动扩展问题

我正在尝试编写一个find脚本,该脚本稍后应该能够读取要从外部文件中排除的目录列表。虽然我可以自己完成这部分,但烦人的数组扩展使任务变得困难。首先,为了获得合适的样本目录树,需要进行一些“准备”:

$ mkdir tmp && cd tmp
$ mkdir excl1_dirx excl2_dirx excl3_dirx
$ touch excl1_dirx/dummy1.txt excl2_dirx/dummy2.txt excl3_dirx/dummy3.txt
$ mkdir excl1_diry excl2_diry excl3_diry
$ touch excl1_diry/dummy4.txt excl2_diry/dummy5.txt excl3_diry/dummy6.txt
$ touch dummy00.txt dummy01.txt

如果脚本有效,则只能显示dummy00.txt和。dummy01.txt

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")

find_str=" . -type f ! ( "

for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=" -o "
   fi

  find_str+=" -path \"./${excl_d[i]}/*\""
done

find_str+=" )"

# this is just for debugging
echo "[debug] value of str = find $find_str"

find $find_str

首先:为什么“ done”之前的行如此(看似)复杂?好吧,bash有时喜欢通过做用户意想不到的事情来惹恼他们。如果没有这些引号,它将扩张每个数组元素;例如excl*_dirx将变成excl1 dirx excl2 dirx excl3 dirx,这(显然)打破了-path界限!尽管我在每个数组元素中使用了一对双引号,但这实际上是为了防止 bash 进行扩展恶作剧!

然而,最好的尚未到来:倒数第二行(当转义( )到时\( \))将在 shell 中正常工作,但不能在独立脚本中正常工作。即使它不会抛出任何错误,但后一个实现的结果将是错误的。

我尝试过各种带有单引号和双引号的组合

find_str+=" -path \"./${excl_d[i]}/*\""

行,但我就是无法让它工作,即使它在倒数第二行显示时看起来绝对完美。似乎bash在内部对待我的转义引号\"与非转义引号不同。啊,引号内的引号几乎在任何地方都可以工作由于某种原因,在使用该运算符时会被过滤掉+=

我不仅在寻找对此行为的解释,而且还在寻找如何使其在独立脚本中工作的解决方案。这一定是我犯的一个愚蠢的错误。 :-/

答案1

这里的问题是 是find_str一个字符串,然后将其用作字符串列表。jw013的评论是正确的,并阅读我试图将命令放入变量中,但复杂的情况总是失败!。您没有将整个命令放入变量中,但是当您尝试将多个单词填充到字符串变量中时,问题就会出现。

在 Bourne/POSIX shell 中,这是不可避免的邪恶。但在 ksh/bash/zsh 中,有更好的方法:使用数组。

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")
find_str=( . -type f \! \( )
for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=( -o )
   fi
  find_str+=( -path "./${excl_d[i]}/*" )
done
find_str+=( \) )
find "$find_str[@]}"

有一种更简单的方法来表达这样的过滤器。

#!/bin/bash
exclude_patterns=("excl*_dirx" "excl*_diry")
exclude_args=()
while [[ ${#exclude_patterns} -gt 0 ]]; do
  exclude_args+=( -path "./${exclude_patterns[1]}/*" -prune -o )
  shift exclude_patterns
done
find "${exclude_args[@]}" -type f

答案2

给你:

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")

find_str=". "

for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
    if [[ $i > 0 ]]; then
    find_str+=" -o "
    fi

    find_str+=" -path \"./\${excl_d[i]}\" -prune "
done

find_str+=" -o -type f -print "

# this is just for debugging
echo "[debug] value of str = find $find_str"

eval "find ${find_str}"

这里的问题是,当 bash 执行脚本中的一行时,find ${find_str}它会只使用一个参数执行 find,即 find 中 main 函数的 argc 参数将等于 2。eval相反,组成字符串,然后在 bash 中传递标记化。

相关内容