TL; DR:ls
的输出是否标准化,以便有一种完美的方法将其解析为文件名数组?
我必须编写一个处理某些文件的程序,程序规范指出:
您的程序应从标准条目中读取文件列表
并给出了如何编程的示例:
ls /usr/include/std*.h | ./distribuer 3
其中
distribuer
是我的程序的名称。从我的测试中,我看到
ls
用此类包含通配符的参数调用时,用制表符分隔文件名,这是行为标准吗?还是ls
在通过类似的通配符参数调用时有时会使用简单的空格字符甚至换行符?最后,尽管这可能是一个极端的情况,但我也担心由于Unix允许在文件名中使用制表符和空格,因此实际上可能无法可靠地解析
ls
的输出,这是正确的吗?
最佳答案
ls
的输出是否标准化,以便有一种完美的方法将其解析为文件名数组?ls
的输出肯定是由Posix standard标准化的。在STDOUT
部分中,描述了标准化格式:
默认格式是在标准输出中每行列出一个条目。终端或指定了-C
,-m
或-x
选项之一的情况除外。
以及关于未标准化输出的重要上下文的警告说明:
如果输出到终端,则格式是实现定义的。
(关于格式如何使用不同的命令行参数进行更改,有很多规范,在此不作赘述,因为在这里它并不立即相关。)
因此,如果不将stdout
定向到终端并且没有提供命令行选项(或者即使-1
是终端,则提供了stdout
选项),则将打印一个条目的标准格式。每行。
不幸的是,这没有提供“完美的方法”来解析输出,因为文件名包含换行符是合法的,而包含换行符的文件名显然将跨越多行。如果只提供ls
输出,则没有100%可靠的方法来判断换行符(除最后一个换行符以外)指示文件名的结尾还是文件名中的换行符。
就您的任务而言,简单的策略就是忽略该缺陷(或者更好地记录下来然后忽略它),这与许多Unix实用程序使用的策略相同。名称中包含换行符的文件在野外极为罕见,而使用其名称中包含换行符的文件创建的人可能应该承担自己所引起的问题。但是,您会在这里发现很多人(有时包括我在内)建议脚本应正确使用所有合法文件名。因此,本答案的其余部分讨论了对此脚踏术的一些可能的响应。请注意,它们都不是“完美的”。
一种不完善的解决方案是尝试弄清楚是否嵌入了给定的换行符。如果您知道列表是由ls
生成的,没有任何排序选项,则在大多数情况下,您可以利用ls
呈现按当前语言环境的归类规则排序的文件的事实来正确猜测。因此,如果一行不正确(小于前一行或大于后一行),则可以猜测它是文件名的延续。那并不总是可行,而且我不知道有任何实用工具可以尝试它,但是值得一提。
如果您自己运行ls
,则可以利用-q
选项,该选项将使非打印字符(包括制表符和换行符)替换为?。在输出中。这会强制将文件名打印在一行上,但缺点是您不再知道替换前的文件名,因为可以用问号(包括问号本身)替换各种字符)。您可能可以查询文件系统以找到文件的真实名称,但是由于本段的前提不适用于实际问题,因此我不涉及很多特殊情况。
最常见的解决方案是允许用户告诉实用程序文件名用NUL字符而不是换行符分隔。这是100%可靠的,因为文件名不能包含NUL字符-实际上,这是它们不能包含的唯一字符。不幸的是,ls
没有提供以这种格式产生输出的选项,但是用户可以使用find
实用工具生成与ls
相同的列表,然后使用非标准但广泛使用的-print0
选项以NUL终止符写出文件名。 (如果只有find
的Posix标准选项可用,您仍然可以通过将-exec
与相应的命令一起使用来输出输出名称)。
许多接受标准输入中文件名列表的实用程序都具有(非标准)选项,用于指定分隔符或指定分隔符为NUL而不是换行符。例如,参见xargs -0
,sort -z
(Gnu或BSD)或read -d
(bash)。因此,如果您有兴趣对其进行编码,那么这可能是一个合理的增强。
值得注意的是,大多数标准Shell实用程序都没有提供通过标准输入获取文件名列表的选项。大多数实用程序都喜欢将文件名作为命令行参数来接收。这行之有效,是因为当外壳程序扩展命令行上指定的“ glob”(例如*
)时,它不会在输出上重新运行单词拆分。每个文件名成为一个参数。那意味着
./distribute *
作为将文件名列表传递到实用程序的一种方法,它几乎是完美的。但这还不是很完美,因为您可以在单个命令行中提供的命令行参数数量有限。因此,如果目录中包含大量文件,则
*
的扩展可能会超过该限制,从而导致实用程序执行失败。 find
只是将文件名作为单个参数传递给-exec
,而无需分词,使用{}+
作为-exec
命令终止符会将文件名分成足够小的集,以至于它们不会超过命令行限制。这比./distribute *
安全,但这确实意味着可以多次调用该实用程序,每组调用一次。 (而且,获取find
谓词确切地提供您想要的内容也很烦人。)
关于c - 解析ls提供的文件的stdin,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49843308/