Bash - 使用 glob 打印反向文件列表

nat*_*ath 1 ls bash wildcards shellcheck

有没有办法通过 glob 反转文件列表?

所以我会得到与以下相同的结果:

ls -r *
Run Code Online (Sandbox Code Playgroud)

我在 shell 脚本中使用它并shellcheck不断抱怨:

^--------^ SC2045:迭代 ls 输出是脆弱的。使用球体。

Sté*_*las 6

zsh

使用zshshell,可以使用oC(order)、OC(与^oC反向相同)和n(对于数字)glob 限定符自定义glob的排序顺序,

引用info zsh qualifier

oC

指定文件名的排序方式。如果是 C n ,则按名称排序;如果是L,则根据文件的大小(长度)对它们进行排序;如果l它们是按链接数排序的;if a, m, orc它们分别按照上次访问、修改或inode变化的时间排序;如果d,子目录中的文件在搜索的每个级别出现在当前目录中的文件之前 - 这最好与其他条件结合使用,例如 ' odon' 对同一目录中的文件的名称进行排序;如果N,则不执行排序。请注意am, 和c 将年龄与当前时间进行比较,因此列表中的第一个名称是最年轻的文件。还要注意使用了修饰符^-,因此“ *(^-oL)”给出了所有文件的列表,这些文件按文件大小降序排序,跟在任何符号链接之后。除非oN 使用,否则可能会出现多个顺序说明符来解决联系。

默认排序是n(按名称),除非使用了Yglob 限定符,在这种情况下它是N(未排序的)。

oe并且o+是特殊情况;它们每个都后跟 shell 代码,分别按照eglob 限定符和+glob 限定符分隔(见上文)。为每个匹配的文件执行代码,参数REPLY设置为条目时的文件名globsort并附加到zsh_eval_context. 代码应该REPLY以某种方式修改参数。返回时,使用参数值而不是文件名作为排序的字符串。与其他排序运算符不同,oe并且o+可以重复,但请注意,任何 glob 表达式中可能出现的任何类型的排序运算符的最大数量为 12。

所以在这里,要按逆字母顺序获取文件列表:

list=(*(NOn))
Run Code Online (Sandbox Code Playgroud)

或者

list=(*(N^on))
Run Code Online (Sandbox Code Playgroud)

(这里也使用N(for nullglob) 限定符,这样如果没有匹配的文件,列表就会变空)。

对于按修改时间倒序排序的列表(如ls -rt):

list=(*(NOm))
Run Code Online (Sandbox Code Playgroud)

使用 zsh,您还可以使用oO参数扩展标志对数组的元素进行排序。

list=(*(N))
list_reversed=(${(Oa)list})
Run Code Online (Sandbox Code Playgroud)

也就是说,对rray 成员$list进行反向排序(因为大写Oa

猛击

使用最新版本的bashshell 和 GNU sort,您可以获得反向排序的文件名列表:

readarray -td '' list < <(
   shopt -s nullglob
   set -- *
   (($# == 0)) || printf '%s\0' "$@" | sort -rz)
Run Code Online (Sandbox Code Playgroud)

readarray -td '' array 将 NUL 分隔的记录列表读入数组。

随着 GNU 的实现ls,另一种方法是:

eval "list=($(ls --quoting-style=shell-always -r))"
Run Code Online (Sandbox Code Playgroud)

wherels --quoting-style=shell-always使用单引号引用文件名(并且\'在单引号之外引用单引号本身)。

这种方法也适用于yash(假设所有文件名都是语言环境中的有效文本)、ksh93zshmksh,尽管对于ksh93,请确保事先将变量声明为数组(使用typeset -a list),否则如果ls没有输出,则将创建$list为复合变量而不是数组变量。

注意 ast-open 的ls实现也支持一个--quoting-style=shell-always选项,但它使用了$'...'在所有语言环境中都不能安全使用的引用形式。

POSIXly

POSIXly, in sh, 要反转"$@"数组,您可以执行以下操作:

eval "set -- $(awk 'BEGIN {for (i = ARGV[1]; i; i--) printf " \"${"i"}\""}' "$#")"
Run Code Online (Sandbox Code Playgroud)

这个想法是让 awk 输出类似于set -- "${3}" "${2}" "${1}"when"$@"有 3 个元素

因此,要以相反的顺序获取该文件列表:

set -- *
eval "set -- $(awk 'BEGIN {for (i = ARGV[1]; i; i--) printf " \"${"i"}\""}' "$#")"
echo file list:
printf ' - %s\n' "$@"
Run Code Online (Sandbox Code Playgroud)

当心 POSIXsh没有任何等效的nullglob选项(一项zsh发明)、zsh 的(N)glob 限定符或 ksh93 的~(N)glob 运算符。如果当前目录中没有非隐藏文件,您将得到一个只有一个*元素的列表。解决此问题的常用方法是:

set -- [*] *
case "$1$2" in
  ('[*]*') shift 2;;
  (*)      shift;;
esac
Run Code Online (Sandbox Code Playgroud)

[*]联合*是一种方式之间的区分*是来自不匹配(其中[*]也将扩大到[*])和一个来自一个名为*字面上(其中[*]将扩大到*)。


在任何情况下,如果您要将文件名列表传递给ls它以对其进行排序,就像在ls -r *shell 将扩展传递*给它的方法中一样,您将想要使用该-d选项并标记选项的结尾与--

ls -rd -- *
Run Code Online (Sandbox Code Playgroud)

但是,该输出仍然无法可靠地进行后处理,因为文件名由换行符分隔,并且换行符与文件名中的任何字符一样有效。