列出带有通配符和空格的文件名

列出带有通配符和空格的文件名

我需要能够列出变量中指定的文件,如下例所示:

X="myfile.txt"
$ ls ${X}

遗憾的是,有些人在文件名中使用空格,因此可能是这种情况:

X="my file.txt"

可以用以下方法解决ls "${X}"

然而,这也可能发生:

X="my file.*"

这会破坏,因为通配符不会在双引号内扩展。

有什么东西可以适用于所有情况吗?

编辑

声明X为数组,如

X=("my file."*)
ls "$X[@]"

是不够的,因为它在声明时扩展了通配符。我需要它在被使用时扩展ls

答案1

巴什

在 中bash,将变量不加引号有时称为 split+glob 运算符。

分割部分可以使用$IFS特殊参数进行调整,全局部分可以使用以下noglob选项进行调整:

仅对于通配符,设置$IFS为空字符串:

file_pattern='foo bar *.txt'
IFS= # disable splitting
set +o noglob # make sure glob is enabled
ls -ld -- $file_pattern # split+glog with split disabled.

请注意,如果模式与任何文件都不匹配,ls将收到一个文字foo bar *.txt参数并抱怨该文件不存在。

仅用于拆分(此处以拆分冒号分隔的$PATH变量为例),设置noglob选项:

IFS=: # split on :
set -o noglob
ls -ld -- $PATH # split+glob with glob disabled

$var为空(或未设置)时,请注意,即使为空,其展开也不产生任何参数,而是一个空参数$IFS

这里可以接受,因为$PATH$PATH(但不是 unset $PATH)意味着在当前工作目录中搜索命令,并且ls在未传递任何参数时也恰好列出当前工作目录。

$PATH如果(如)中有空元素/bin::/usr/bin,这也意味着当前工作目录,则会传递一个相应的空参数,ls该参数将对此进行抱怨。一个例外是,如果该空元素是最后一个(如 中/bin:/usr/bin:),则最后一个元素将被丢弃。

分割类似变量的更正确方法$PATH是使用:

IFS=:
set -o noglob
printf '<%s>\n' $PATH''

这一额外的部分''可以防止删除尾随的空元素,并确保将空元素$PATH拆分为一个空元素,而不是根本没有元素。

桀骜

在 zsh 中,分割和通配符并不是在参数扩展时隐式完成的,您必须使用$=varfor $IFS-splitting、${(s[separator])var}基于任意分隔符的分割、$~var通配符(或$=~var组合$IFS-splitting 和通配符)显式请求它们。

所以:

file_pattern='foo bar *.txt'
ls -ld -- $~file_pattern

(如果模式与任何文件都不匹配,则命令ls将中止²)。

IFS=:
ls -ld -- $=PATH # preserves all empty elements even trailing ones
ls -ld -- ${(s[:])PATH}    # split, discarding empty elements
ls -ld -- "${(@s[:])PATH}" # split, preserving empty elements

¹ 除非启用该选项(来自由而不是failglob管理的另一组选项),在这种情况下 bash 将中止(退出子 shell 或如果不在子 shell 中运行则跳过它已读取的所有代码),或者启用该选项在这种情况下,它将扩展为空,导致列出当前工作目录。shoptsetnullglob shoptls

² 在 zsh 中,失败的 glob 的处理方式类似于语法错误,相应的 shell 进程会以错误和失败退出状态退出(尽管在交互式 shell 中,它将返回到提示符而不是退出主 shell)。在这里,与ls外部命令一样,只有为执行它而生成的子进程才会被中止,因为这就是全局扩展发生的地方。ls例如,如果您重新定义为 shell 函数,情况会有所不同。

答案2

如果您想使用通配符,那么使用变量并不是一个好主意,因为它可能会匹配多个文件。

解决办法:使用数组!

X=("my file."*)
ls "${X[@]}"

# or

for f in "${X[@]}"; do
   some_command "$f"
done

请注意,在赋值时,*必须位于引号之外。

查看这里用于仅在变量扩展时扩展全局的解决方案。

相关内容