过去几十年来公认的观点是,解析ls
( [1] , [2] ) 的输出从来都不是一个好主意。例如,如果我想将文件的修改日期及其名称保存到 shell 变量中,则这不是正确的方法:
$ ls -l file
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:16 file
$ foo=$(ls -l file | awk '{print $9,$6,$7,$8}')
$ echo "$foo"
file Aug 15 19:16
只要文件名稍有不同,该方法就会失败:
$ ls -l file*
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:16 'file with spaces'
$ foo=$(ls -l file* | awk '{print $9,$6,$7,$8}')
$ echo "$foo"
file Aug 15 19:16
如果文件的修改日期与今天不接近,情况会变得更糟,因为这可能会更改时间格式:
$ ls -l
total 0
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:21 file
-rw-r--r-- 1 terdon terdon 0 Aug 15 2018 'file with spaces'
然而,较新版本的 GNU coreutilsls
有两个选项,可以组合起来设置特定的时间格式并生成 NULL 界定的输出:
--time-style=TIME_STYLE
time/date format with -l; see TIME_STYLE below
[...]
--zero end each output line with NUL, not newline
[...]
The TIME_STYLE argument can be full-iso, long-iso, iso, locale, or
+FORMAT. FORMAT is interpreted like in date(1). If FORMAT is FOR‐
MAT1<newline>FORMAT2, then FORMAT1 applies to non-recent files and
FORMAT2 to recent files. TIME_STYLE prefixed with 'posix-' takes ef‐
fect only outside the POSIX locale. Also the TIME_STYLE environment
variable sets the default style to use.
这里再次是设置了这些选项的文件(每行输出末尾的零被替换为#
换行符,以稍微提高可读性):
$ ls -l --zero --time-style=long-iso -- *
-rw-r--r--+ 1 terdon terdon 0 2023-08-16 21:35 a file with a
newline#
-rw-r--r--+ 1 terdon terdon 0 2023-08-15 19:16 file#
-rw-r--r--+ 1 terdon terdon 0 2018-08-15 12:00 file with spaces#
ls
有了这些可用的选项,我可以做许多传统上有害的事情。例如:
将最近修改的文件名放入变量中:
$ touch 'a file with a'$'\n''newline' $ last=$(ls -tr --zero | tail -z -n1) bash: warning: command substitution: ignored null byte in input $ printf -- 'LAST: "%s"\n' "$last" LAST: "a file with a newline"
引发这个问题的例子。另一个问题,在 Ask Ubuntu 上,OP 想要打印文件名和修改日期。有人使用和 一个巧妙的技巧发布了答案,如果我们添加到,它似乎非常强大:
ls
awk
--zero
ls
$ output=$(ls -l --zero --time-style=long-iso -- * | awk 'BEGIN{RS="\0"}{ t=index($0,$7); print substr($0,t+6), $6 }') $ printf 'Output: "%s"\n' "$output" Output: "a file with a newline 2023-08-16"
我找不到一个可以打破这两个例子的名字。所以,我的问题是:
- 是否存在上述两个示例之一会失败的情况?也许有一些奇怪的地方?
- 如果不是,这是否意味着现代版本的 GNU
ls
实际上可以安全地使用任意文件名?