将变量中的shell通配符扩展为数组

7
在一个bash脚本中,我有一个变量包含一个shell glob表达式,我希望将其扩展为匹配的文件名数组(nullglob已打开),就像这样:
pat='dir/*.config'
files=($pat)

即使在$pat中有多个模式(例如pat="dir/*.config dir/*.conf"),这个方法也可以很好地工作,但是我不能在模式中使用转义字符。理想情况下,我希望能够执行以下操作:

pat='"dir/*" dir/*.config "dir/file with spaces"'

要包括文件*,所有以.config结尾的文件和带有空格的文件。是否有简单的方法可以做到这一点?(如果可能的话,不要使用eval。)由于模式是从文件中读取的,因此我不能直接将其放置在数组表达式中,如此答案(以及其他各处)所建议的那样。

编辑:

为了让事情更清楚:我正在尝试逐行读取模板文件并处理所有类似于#include pattern的行。然后,使用shell glob解析这些包含文件。由于此工具旨在通用,因此我希望能够包括带有空格和奇怪字符(例如*)的文件。

"主"循环如下:

    template_include_pat='^#include (.*)$'
    while IFS='' read -r line || [[ -n "$line" ]]; do
        if printf '%s' "$line" | grep -qE "$template_include_pat"; then
            glob=$(printf '%s' "$line" | sed -nrE "s/$template_include_pat/\\1/p")
            cwd=$(pwd -P)
            cd "$targetdir"
            files=($glob)
            for f in "${files[@]}"; do
                printf "\n\n%s\n" "# FILE $f" >> "$tempfile"
                cat "$f" >> "$tempfile" ||
                    die "Cannot read '$f'."
            done
            cd "$cwd"
        else
            echo "$line" >> "$tempfile"
        fi
    done < "$template"

@anishsane:刚刚看到了。不知道为什么他们不想使用它。 - Inian
@anishsane 是的,那样可以运行,但是与 eval 相同具有安全隐患... 如果 Pattern 是类似于 $(echo GOTCHA >&2) 这样的东西,我肯定不想执行 $(...) 中的内容。 - steiny
如果'pat = 'dir a / *.config''之类的内容,则此操作将失败。不要指望未引用的参数展开可以按照您的意愿进行。 - chepner
你应该意识到,一旦你使用dir/*,所有其他的添加都已经包含在内了,即使你的文件名中有空格。 - grail
“same security implications of eval”: 没错,这就是为什么我说它只是 eval 的等价物。 - anishsane
显示剩余2条评论
1个回答

0
使用Python的glob模块:
#!/usr/bin/env bash

# Takes literal glob expressions on as argv; emits NUL-delimited match list on output
expand_globs() {
  python -c '
import sys, glob
for arg in sys.argv[1:]:
  for result in glob.iglob(arg):
    sys.stdout.write("%s\0" % (result,))
' _ "$@"
}

template_include_pat='^#include (.*)$'
template=${1:-/dev/stdin}

# record the patterns we were looking for
patterns=( )

while read -r line; do
  if [[ $line =~ $template_include_pat ]]; then
    patterns+=( "${BASH_REMATCH[1]}" )
  fi
done <"$template"

results=( )
while IFS= read -r -d '' name; do
  results+=( "$name" )
done < <(expand_globs "${patterns[@]}")

# Let's display our results:
{
  printf 'Searched for the following patterns, from template %q:\n' "$template"
  (( ${#patterns[@]} )) && printf ' - %q\n' "${patterns[@]}"
  echo
  echo "Found the following files:"
  (( ${#results[@]} )) && printf ' - %q\n' "${results[@]}"
} >&2

好的,没意识到那已经被涵盖了。改用 Python 的 glob 模块实现。 - Charles Duffy
我应该开始用Python编写所有的脚本...感谢您的建议。还剩下什么:引号处理,即字面匹配模式,例如 dir/the file.config - steiny
#include dir/the file.config 应该原封不动地工作(匹配 the file.config),不需要任何修改。 - Charles Duffy
如果你真的想允许有限的引用/转义,这是可能的——请参见我的答案bash:从字符串中正确读取引用/转义参数 - Charles Duffy
是的,我认为Python的shlex是最好的选择。这样我就可以在一行上指定多个glob(用空格分隔),并匹配带有空格的文件名。虽然我怀疑我永远不需要处理带有空格的文件名--但谁知道呢? - steiny
显示剩余2条评论

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