glob 变量必须放在双引号中

glob 变量必须放在双引号中

这个链接:引号内的通配符 就我而言,无法解决问题。
文件“a.sh”包含:

file="path with wildcard*"

文件“b.sh”包含:

. a.sh
[ -f "$file" ] && echo ok

因为

"$file"

不扩展通配符,而是

$file

展开通配符,但出现错误:“需要二元运算符”

我该如何解决这个难题?无法从双引号中取出通配符。

编辑:
我想通过通配符实现单个匹配。如果有更多匹配项,则条件必须返回 false,无论如何都必须返回错误。

答案1

如果你想存储在大批存储在变量 in 中的 glob 模式扩展所产生的文件列表,即bash

pattern='path with wildcard*'

IFS= # disable splitting

shopt -s nullglob # make globs that don't match any file expand to nothing
                  # instead of the unexpanded pattern.

files=($pattern) # here using split+glob (unquoted expansion in list context)
                 # with the splitting disabled above.

或者直接分配$files数组而不使用$pattern标量变量:

shopt -s nullglob # make globs that don't match any file expand to nothing
                  # instead of the unexpanded pattern.

files=('path with wildcard'*)

然后您可以使用以下命令测试该列表是否为空:

if [ "${#files[@]}" -gt 0 ]; then
  echo it did find some files
fi

如果您想检查这些文件中至少有一个是常规文件(在符号链接解析之后),您可以执行以下操作:

has_regular_files() {
  local file
  for file do
    [ -f "$file" ] && return
  done
  false
}

if has_regular_files "${files[@]}"; then
  echo there was at least one regular file in the list
fi

要检查它是否仅匹配一个文件并且该文件是常规文件:

if [ "${#files[@]}" -eq 1 ] && [ -f "${files[0]}" ]; then
  echo one and only one file matches and it is regular.
fi

要检查它是否至少匹配一个常规文件并且所有匹配的文件都是常规文件:

only_regular_files() {
  [ "$#" -gt 0 ] || return
  local file
  for file do
    [ -f "$file" ] || return
  done
}

if only_regular_files "${files[@]}"; then
  echo at least one regular file and all are regular.
fi

通过zshshell,您可以使用 glob 限定符来按文件类型进行匹配:

if ()(($#)) $~pattern(N-.); then
  print at least one regular file in the expansion of the pattern.
fi
  • 与 相反bashzsh不会在未加引号的参数扩展上执行隐式 split+glob。在这里,我们要求使用 进行通配(但不是拆分)$~pattern
  • 我们附加(N-.)glob 限定符,N这样nullglob如果 glob 不匹配任何文件,它就会扩展为没有任何内容.来测试常规的仅文件(排除任何其他类型的文件),-以便完成测试符号链接解析,因此它也会匹配作为常规文件的符号链接的文件(就像您[ -f "$file" ]希望的那样)。
  • 该 glob 的扩展作为参数传递给匿名函数,() {body} args其中{body}is (($#))
  • ((expr))是一个 ksh 风格的算术表达式求值运算符,如果表达式求值为 0 以外的数字,则返回 true。这里,表达式是$#,它是一个特殊参数,它扩展为位置参数的数量,在本例中为位置参数的数量该匿名函数的参数,即该 glob 扩展产生的文件数。

答案2

[ -f $file ] && echo ok

这将扩展变量,wordsplit,并扩展全部匹配文件。您最终可能会得到[ -f filethis filethat ], 并且filethis不是二元运算符,因此您会收到错误。

即使没有匹配项,并且$file扩展为空,您也会得到[ -f ]这是一个测试,看看是否-f是一个非空字符串。是的,所以结果是正确的。

将文件名展开为数组或位置参数并计算匹配项:

file="path with wildcard*"
IFS=
set -- $file
echo "matched $# files"

如果模式中有空格,则必须IFS在此处进行更改,因为拆分发生在文件名扩展之前,因此上面将有两个常量字和一个全局模式。此外,不匹配的模式将保持原样,因此我们需要检查这一点。

您可以将其隐藏在函数中:

only_one() {
    local IFS=
    set -- $1
    if [ $# = 1 ] && [ -f "$1" ]; then return 0; fi
    return 1
}

然后运行

if only_one "$file"; then 
    ...
fi

答案3

我已经解决了:
文件“a.sh”包含:

file=('path with wildcard'*)

文件“b.sh”包含:

. a.sh
[ -f "$file" ] && echo ok

结果:好的

相关内容