解析由ls

时间:2018-04-15 14:55:44

标签: c parsing unix

TL; DR:ls的输出是否已标准化,以便有一种完美的方法将其解析为文件名数组?

我必须编写一个处理某些文件的程序,程序规范说明了这一点:

  

您的程序应该从标准条目中读取文件列表

并给出了如何使用程序的例子:

ls /usr/include/std*.h | ./distribuer 3

distribuer是我的计划的名称。

从我的测试中,我看到ls在使用包含通配符的这种参数调用时,使用制表符分隔文件名,这是行为标准吗?或者ls有时候使用简单的空格字符甚至是换行符时使用类似的通配符参数? 最后,虽然这可能是一个边缘情况,但我也担心,因为Unix允许文件名中的制表符和空格,实际上不可能可靠地解析ls的输出,这是正确的吗?

1 个答案:

答案 0 :(得分:1)

  

ls的输出是否已标准化,以便有一种完美的方法将其解析为文件名数组?

ls的输出肯定是标准化的Posix standard。在STDOUT部分中,描述了标准化格式:

  

默认格式是每行列出一个条目到标准输出;例外情况是终端或指定了-C-m-x选项之一。

除了关于输出标准化的重要背景的警示:

  

如果输出是终端,则格式是实现定义的。

(关于格式如何随着不同的命令行参数而变化的规范有很多,我没有引用它,因为它在这里没有立即相关。)

所以标准格式是适用的,如果stdout没有定向到终端,并且没有提供命令行选项(或者提供了-1选项,即使stdout也是如此是一个终端)是每行打印一个条目。

不幸的是,这并没有提供一种完美的方式"解析输出,因为文件名包含换行符是合法的,包含换行符的文件名显然会跨越多行。如果只有ls输出,则没有100%可靠的方法来判断换行符(除了最后一行)是否表示文件名的结尾或者是文件名中的换行符。

出于分配的目的,简单的策略就是忽略这种不完美(或者更好的是,记录它然后忽略它),这与许多Unix实用程序使用的策略相同。名称中包含换行符的文件在野外非常罕见,而在名称中创建带换行符的文件的人可能会遇到他们自己会遇到的问题。但是,你会发现很多人(包括我,有时)建议脚本应该与所有合法的文件名一起正常工作。所以这个答案的其余部分讨论了对这种迂腐的一些可能的回应。请注意,它们都不是"完美"。

一个不完美的解决方案是试图弄清楚是否嵌入了给定的换行符。如果您知道列表是由ls生成的,没有任何排序选项,那么在大多数情况下,您可以通过使用ls显示按当前区域设置排序的文件排序的事实来正确猜测规则。因此,如果一行不按顺序(小于前一行或大于后一行),则猜测它是文件名的延续是合适的。这不会一直有效,而且我不知道任何试用它的实用工具,但值得一提。

如果您自己运行ls,则可以利用-q选项,这会导致非打印字符(包括制表符和换行符)替换为在输出中。这会强制文件名打印在一行上,但缺点是您不知道替换前文件名是什么,因为有多种字符可以用问号代替(包括问号本身) )。您可以查询文件系统以查找文件的真实姓名,但是由于本段的前提不适用于实际问题,因此我不打算进入很多极端情况。< / p>

最常见的解决方案是允许用户告诉您的实用程序文件名是用NUL字符而不是换行符分隔的。这是100%可靠的,因为文件名不能包含NUL字符 - 实际上,这是他们不能包含的唯一字符。遗憾的是,ls未提供以此格式生成输出的选项,但用户可以使用find实用程序生成与ls相同的列表,然后使用非标准但广泛实施的-print0选项,用NUL终结符写出文件名。 (如果只有find的Posix标准选项可用,您仍然可以使用-exec并使用适当的命令输出名称来生成输出。)

许多接受标准输入上的文件名列表的实用程序都有(非标准)选项来指定分隔符,或者指定分隔符是NUL而不是换行符。例如,请参阅xargs -0sort -z(Gnu或BSD)或read -d(bash)。因此,如果您对编码感兴趣,这可能是一个合理的增强。

值得注意的是,大多数标准shell实用程序都没有提供通过标准输入获取文件名列表的选项。大多数实用程序更喜欢将文件名作为命令行参数接收。这很有效,因为当外壳扩展时,#glo;&#34; (如*)在命令行中指定,它不会在输出上重新运行字拆分;每个文件名都成为一个参数。这意味着

./distribute *

作为将文件名列表传递给实用程序的方法几乎是完美的。但它仍然不是很完美,因为您可以在单个命令行中提供的命令行参数数量有限。因此,如果目录中包含大量文件,则*的扩展可能会超出该限制,从而导致实用程序执行失败。 find也只是将文件名作为单个参数传递给-exec而不进行分词,并且使用{}+作为-exec命令终止符会将文件名拆分为多个集合。足够小,他们不会超过命令行限制。这比./distribute *更安全,但它确实意味着可以多次调用该实用程序,每次调用一次。 (并且让find谓词准确地为您提供所需内容也有点烦人。)