Bash star *通配符是否总是产生(升序)排序列表?


53

我有一个目录,其中填充了一些文件名logXX,其中XX是一个两个字符,零填充的大写十六进制数字,例如:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

通常,总共少于20个或30个文件。我的特定系统上的日期和时间不可靠(嵌入式系统没有可靠的NTP或GPS时间源)。但是,文件名将可靠地增加,如上所示。

我希望grep浏览所有文件中某个类型的最新日志条目,我希望将cat这些文件放在一起,例如...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

然而,它发生,我认为不同版本bashshzsh等可能有关于如何不同的想法*展开。

man bash页面没有说明的扩展是否*一定是匹配文件名的绝对升序字母列表。每当我在所有可用的系统上尝试过该功能时,它的数量似乎都会不断增加,但是它是行为定义的还是特定于实现的?

换句话说,我可以绝对依赖cat /tmp/logs/log*于按字母顺序将所有日志文件连接在一起吗?


1
@ADDB的默认排序顺序sort与Shell扩展文件名globbing模式时的排序顺序相同。
库萨兰达

9
那是可怕的文件命名习惯。为什么以log(0)=-infty开始运行?
EP

14
@EP我们的文件系统是一个复杂的7维超环面,具有超大的索引节点编号。它的祖父是用
不知名

1
您可以避免cat使用with grep -h pattern /tmp/logs/log*取消匹配的文件名。(至少对于GNU grep,我没有检查POSIX或busybox。)
Peter Cordes

1
@Kusalananda您听说过无用的用法cat,这是无用的用法sort
cat

Answers:


52

在所有shell中,默认情况下对glob进行排序。肯·汤普森(Ken Thompson)的外壳呼唤/etc/glob助手已经在70年代初的Unix的第一个版本中扩展了glob(并为glob命名)。

对于sh,POSIX确实要求使用来对它们进行排序strcoll(),即使用用户语言环境中的排序顺序,就像ls有些人仍然通过strcmp(),仅基于字节值。

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

您可能会在上面注意到,对于那些基于语言环境进行排序的shell,在具有语言环境的GNU系统上en_GB.UTF-8-文件名中的会被忽略以进行排序(大多数标点符号会这样做)。该ó以更期望的方式(至少英国人)排序,并忽略大小写(当谈到决定关系除外)。

但是,您会注意到log①log②的某些不一致之处。这是因为①和②的排序顺序未在GNU语言环境中定义(当前;希望有一天会固定)。它们的排序相同,因此您将获得随机结果。

更改语言环境将影响排序顺序。您可以将语言环境设置为C以获得类似strcmp()的排序:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

请注意,即使对于全ASCII全数字字符串,某些语言环境也会引起一些混乱。与捷克语(至少在GNU系统上)ch类似,其中的归类元素排在后面h

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

或者,正如@ninjalj所指出的,甚至在匈牙利语言环境中也很奇怪:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

在中zsh,您可以选择使用glob限定词进行排序。例如:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

echo *(n)也可以使用以下numericglobsort选项全局启用数字排序:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

如果您(像我一样)在特定情况下(在此使用我的英国语言环境)对该命令感到困惑,请参阅此处以获取详细信息。


1
“ ch”的情况甚至可能更奇怪:某些语言环境可以确定“ ch”,“ Ch”和“ CH”分别是1个整理元素,而“ cH”是两个整理元素。请参阅:unicode.org/cldr/trac/ticket/889当前的CLDR似乎并不完全一致:当前的匈牙利语(unicode.org/cldr/trac/browser/trunk/common/collat​​ion/hu.xml)具有类似的规则&C<cs<<<Cs<<<CS,而&C<cs<<<cS<<<Cs<<<CS被标记为建议的实验草案。从导入到CLDR中的一些较旧的数据来看,较旧的AIX和MS似乎更喜欢“小写然后大写是2个不同的整理元素”视图。
ninjalj

而且我已经看到了无法正常运行的系统。:(
约书亚

38

bash的手册页确实指定:

路径名扩展

词的拆分之后,除非该-f选项已被设置,bash将扫描的文字每个字*?[。如果出现这些字符之一,则将该单词视为一个模式,并替换为与该模式[…]匹配的按字母顺序排序的文件名列表。


1
刚刚在腻子或man文本渲染中发现了一个有趣的错误...如果我要搜索的文本被“自动换行”,那么/ search命令将找不到它。只需最大化我的终端即可:)
Wossname

2
你覆盖了bash。Tho OP也对“ zsh等”感兴趣。
库桑兰达

29

除非您在某些外壳程序中触发了一些非常特定的外壳程序选项,否则输出肯定是相同的。

订单在POSIX标准中指定:

如果该模式与任何现有的文件名或路径名匹配,则该模式应替换为那些文件名和路径名,并根据当前语言环境中有效的整理顺序进行排序。如果此整理序列没有所有字符的总排序(请参阅XBD LC_COLLATE),则应使用POSIX语言环境的整理序列逐字节逐字节比较所有相同整理的文件名或路径名。

另请参阅POSIX语言环境中的LC_COLLATE类别,简而言之,如果表示LC_COLLATE=C,则事物以ASCII顺序排序。


bash手册中提到

LC_COLLATE

此变量确定排序路径名扩展结果时使用的排序规则顺序,并确定范围表达式,等价类以及路径名扩展和模式匹配内的整理序列的行为。

ksh93并且zsh具有类似的措辞,这使我相信他们在这方面遵循POSIX标准。

其他shell,例如pdkshdash不说关于文件名遍历导致的文件名排序的任何内容。我很想相信这意味着它们至少在使用POSIX语言环境时仍遵守相同的标准。以我的经验,我还没有遇到过对ASCII文件名进行任何“奇怪”排序的外壳。


2
请参阅该numericglobsort选项,zsh因为它会影响排序。尽管我宁愿像echo *(n)在全局范围内启用它一样,也不愿在全局范围内启用该选项。
斯特凡Chazelas

顽皮。Bash在默认模式下不兼容Posix。
fpmurphy

@ fpmurphy1说更多。
库桑兰达

@Kusalananda。Bash从未被认证为POSIX投诉。要在Bash中获得“ POSIX-compliance”,必须使用--posix命令行选项调用Bash 或执行set -o posix
fpmurphy

@ fpmurphy1是的,但是文件名通配符扩展名的排序不受Bash posix模式的影响。请参阅gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html。 这使我相信(希望如此)排序符合POSIX。
库萨兰达

1

如果主要目标是按输入文件的年龄(最早的)排序,则可以编写

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

如果还涉及轮转和压缩日志:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever

4
提到文件的时间戳是不可信的。
库萨兰达

3
@Kusalananda,是的,我们的系统时间通常被认为是一个随机数生成器:)
Wossname
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.