ls 没有 --zero 或 -0 选项是否有原因

Tim*_*imo 41 command-line ls shell

这个问题是由关于ls'-1选项的问题以及人们反复提出问题和答案的倾向引起的,包括处理ls.

输出的这种重用ls似乎是可以理解的,例如:如果您知道如何对文件列表进行排序,ls那么您可能希望以这种方式使用输出作为其他内容的输入。

如果这些问答不包含对由行为良好的文件名(没有空格和换行符之类的特殊字符)组成的文件名列表的引用,则经常有人评论它们,指出命令序列在出现时不起作用的危险是带有换行符、空格等的文件。

find,sort和其他实用程序解决了将“困难”文件名传达给的问题,例如,xargs通过使用一个选项将文件名与 NUL 字符/字节分开,这不是文件名中的有效字符(除了/?之外的唯一一个) Unix/Linux 文件系统。

我查看了手册页ls和输出ls --help(列出了更多选项),但找不到ls(from coreutils) 有一个选项来指定 NUL 分隔的输出。它确实有一个-1选项可以解释为“用换行符分隔的输出文件名”

:是否有技术或哲学原因为什么ls没有“输出由 NUL 分隔的文件名”的--zero-0选项?

如果您执行的操作仅输出文件名(而不使用 eg -l),则可能有意义:

ls -rt -0 | xargs -r0 …
Run Code Online (Sandbox Code Playgroud)

我可能会遗漏一些为什么这不起作用的东西,或者我忽略了这个例子的替代方案,而且不是更复杂和/或模糊


附录:

这样做ls -lrt -0可能没有多大意义,但在以同样的方式find . -ls -print0不对,所以这不是一个理由不提供-0/ -z/--zero选项。

slm*_*slm 40

更新 (2014-02-02)

感谢我们自己的@Anthon决心跟进缺少此功能,我们对缺少此功能的原因有一个稍微正式的理由,这重申了我之前的解释:

Re: [PATCH] ls: adding --zero/-z option, including tests

From:      Pádraig Brady
Subject:   Re: [PATCH] ls: adding --zero/-z option, including tests
Date:      Mon, 03 Feb 2014 15:27:31 +0000
Run Code Online (Sandbox Code Playgroud)

非常感谢补丁。如果我们要这样做,那么这就是我们将使用的接口。然而 ls 确实是一个供人类直接消费的工具,在这种情况下,进一步处理就没那么有用了。对于进一步处理, find(1) 更适合。这在上面链接的第一个答案中得到了很好的描述。

所以我 70:30 反对添加这个。

我的原答案


这是我个人的一点意见,但我相信这是一个设计决定,将开关排除在ls. 如果您注意到该find命令确实具有此开关:

-print0
      True; print the full file name on the standard output, followed by a 
      null character (instead of the newline character that -print uses).  
      This allows file  names  that  contain  newlines or other types of white 
      space to be correctly interpreted by programs that process the find 
      output.  This option corresponds to the -0 option of xargs.
Run Code Online (Sandbox Code Playgroud)

通过忽略该开关,设计师暗示您不应将ls输出用于人类消费以外的任何其他用途。对于其他工具的下游处理,您应该find改用。

使用方法查找

如果您只是在寻找替代方法,您可以在此处找到它们,标题为:正确操作:快速总结。从该链接,这些可能是 3 种更常见的模式:

  1. 简单的 find -exec; 如果 COMMAND 很大,则笨拙,并创建 1 个进程/文件:
    find . -exec COMMAND... {} \;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 简单的 find -exec with +,如果多个文件都可以用于 COMMAND,则更快:
    find . -exec COMMAND... {} \+
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用带有 \0 分隔符的 find 和 xargs

    (非标准通用扩展 -print0 和 -0。适用于 GNU、*BSD、busybox)

    find . -print0 | xargs -0 COMMAND
    
    Run Code Online (Sandbox Code Playgroud)

进一步的证据?

我从 Joey Hess 的博客中找到了这篇博客文章,标题为:“ ls:缺少的选项”。这篇文章中有趣的评论之一:

现在唯一明显的缺失是 -z 选项,它应该使输出文件名以 NULL 终止以供其他程序使用。我认为这会很容易写,但我一直在忙于 IRL(移动很多家具)并且没有完成。有没有人来写?

进一步搜索,我在 Joey 的博客文章中提到的附加开关之一的提交日志中发现了这一点,“新的输出格式 -j ”,因此该博客文章似乎在取笑将-z开关添加到ls.

至于其他选项,很多人都同意 -e 几乎是有用的,尽管我们没有人能找到使用它的理由。我的错误报告忽略了 ls -eR 是非常错误的。-j 显然是个笑话。

参考

  • 这篇文章深入探讨了文件命名问题:http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html (3认同)
  • 谢谢。我知道这些注意事项。如果没有指出,关于 ls 输出处理的问题就不是完整的;-) (2认同)

Ant*_*hon 20

由于@slm 的答案涉及起源和可能的原因,我不会在这里重复。这样的选项不在coreutils拒绝的功能列表中,但低于该补丁 现在哈灵顿布雷迪拒绝将其发送到coreutils的邮件列表之后。从答案中可以明显看出这是一个哲学原因(ls输出用于人类消费)。

如果你想试试这样的选择对你自己是否合理,请执行以下操作:

git clone git://git.sv.gnu.org/coreutils
cd coreutils
./bootstrap
./configure
make
Run Code Online (Sandbox Code Playgroud)

然后针对提交 b938b6e289ef78815935ffa705673a6a8b2ee98e dd 2014-01-29 应用以下补丁:

From 6413d5e2a488ecadb8b988c802fe0a5e5cb7d8f4 Mon Sep 17 00:00:00 2001
From: Anthon van der Neut <address@hidden>
Date: Mon, 3 Feb 2014 15:33:50 +0100
Subject: [PATCH] ls: adding --zero/-z option, including tests

* src/ls.c has the necessary changes to allow -z/--zero option to be
  specified, resulting in a NUL seperated list of files. This
  allows the output of e.g. "ls -rtz" to be piped into other programs

* tests/ls/no-args.sh was extended to test the -z option

* test/ls/rt-zero.sh was added to test both the long and short option
  together with "-t"

This patch was inspired by numerous questions on unix.stackexchange.com
where the output of ls was piped into some other program, invariably
resulting in someone pointing out that is an unsafe practise because of
possible newlines and other characters in the filenames.
---
 src/ls.c            |   31 +++++++++++++++++++++++++------
 tests/ls/no-arg.sh  |    7 ++++++-
 tests/ls/rt-zero.sh |   38 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 7 deletions(-)
 create mode 100755 tests/ls/rt-zero.sh

diff --git a/src/ls.c b/src/ls.c
index 5d87dd3..962e6bb 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -381,6 +381,7 @@ static int file_size_width;
    many_per_line for just names, many per line, sorted vertically.
    horizontal for just names, many per line, sorted horizontally.
    with_commas for just names, many per line, separated by commas.
+   with_zero for just names, one per line, separated by NUL.

-l (and other options that imply -l), -1, -C, -x and -m control

    this parameter.  */
@@ -391,7 +392,8 @@ enum format
     one_per_line,              /* -1 */
     many_per_line,             /* -C */
     horizontal,                        /* -x */
-    with_commas                        /* -m */
+    with_commas,               /* -m */
+    with_zero,                 /* -z */
   };

static enum format format;

@@ -842,6 +844,7 @@ static struct option const long_options[] =
   {"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
   {"context", no_argument, 0, 'Z'},
   {"author", no_argument, NULL, AUTHOR_OPTION},
+  {"zero", no_argument, NULL, 'z'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -850,12 +853,12 @@ static struct option const long_options[] =
 static char const *const format_args[] =
 {
   "verbose", "long", "commas", "horizontal", "across",
-  "vertical", "single-column", NULL
+  "vertical", "single-column", "zero", NULL
 };
 static enum format const format_types[] =
 {
   long_format, long_format, with_commas, horizontal, horizontal,
-  many_per_line, one_per_line
+  many_per_line, one_per_line, with_zero
 };
 ARGMATCH_VERIFY (format_args, format_types);

@@ -1645,7 +1648,7 @@ decode_switches (int argc, char **argv)

     {
       int oi = -1;
       int c = getopt_long (argc, argv,
-                           "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1",
+                           "abcdfghiklmnopqrstuvw:xzABCDFGHI:LNQRST:UXZ1",
                            long_options, &oi);
       if (c == -1)
         break;
@@ -1852,6 +1855,10 @@ decode_switches (int argc, char **argv)
             format = one_per_line;
           break;

+ case 'z':

+          format = with_zero;
+          break;
+
         case AUTHOR_OPTION:
           print_author = true;
           break;
@@ -2607,7 +2614,8 @@ print_dir (char const *name, char const *realname, bool 
command_line_arg)
                  ls uses constant memory while processing the entries of
                  this directory.  Useful when there are many (millions)
                  of entries in a directory.  */
-              if (format == one_per_line && sort_type == sort_none
+              if ((format == one_per_line || format == with_zero)
+                      && sort_type == sort_none
                       && !print_block_size && !recursive)
                 {
                   /* We must call sort_files in spite of
@@ -3598,6 +3606,14 @@ print_current_files (void)
         }
       break;

+ case with_zero:

+      for (i = 0; i < cwd_n_used; i++)
+        {
+          print_file_name_and_frills (sorted_file[i], 0);
+          putchar ('\0');
+        }
+      break;
+
     case many_per_line:
       print_many_per_line ();
       break;
@@ -4490,6 +4506,7 @@ print_many_per_line (void)
           indent (pos + name_length, pos + max_name_length);
           pos += max_name_length;
         }
+      putchar ('X'); // AvdN
       putchar ('\n');
     }
 }
@@ -4780,7 +4797,8 @@ Sort entries alphabetically if none of -cftuvSUX nor 
--sort is specified.\n\
   -F, --classify             append indicator (one of */=>@|) to entries\n\
       --file-type            likewise, except do not append '*'\n\
       --format=WORD          across -x, commas -m, horizontal -x, long -l,\n\
-                               single-column -1, verbose -l, vertical -C\n\
+                               single-column -1, verbose -l, vertical -C,\n\
+                               zeros -z\n\
       --full-time            like -l --time-style=full-iso\n\
 "), stdout);
       fputs (_("\
@@ -4888,6 +4906,7 @@ Sort entries alphabetically if none of -cftuvSUX nor 
--sort is specified.\n\
   -X                         sort alphabetically by entry extension\n\
   -Z, --context              print any security context of each file\n\
   -1                         list one file per line\n\
+  -z, --zero                 list files separated with NUL\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
diff --git a/tests/ls/no-arg.sh b/tests/ls/no-arg.sh
index e356a29..da28b96 100755
--- a/tests/ls/no-arg.sh
+++ b/tests/ls/no-arg.sh
@@ -30,11 +30,16 @@ out
 symlink
 EOF

-

 ls -1 > out || fail=1

compare exp out || fail=1 +/bin/echo -en "dir\00exp\00out\00symlink\00" > exp || framework_failure_

+
+ls --zero > out || fail=1
+
+compare exp out || fail=1
+
 cat > exp <<\EOF
 .:
 dir
diff --git a/tests/ls/rt-zero.sh b/tests/ls/rt-zero.sh
new file mode 100755
index 0000000..cdbd311
--- /dev/null
+++ b/tests/ls/rt-zero.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Make sure name is used as secondary key when sorting on mtime or ctime.
+
+# Copyright (C) 1998-2014 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls touch
+
+date=1998-01-15
+
+touch -d "$date" c || framework_failure_
+touch -d "$date" a || framework_failure_
+touch -d "$date" b || framework_failure_
+
+
+ls -zt a b c > out || fail=1
+/bin/echo -en "a\00b\00c\00" > exp
+compare exp out || fail=1
+
+rm -rf out exp
+ls -rt --zero a b c > out || fail=1
+/bin/echo -en "c\00b\00a\00" > exp
+compare exp out || fail=1
+
+Exit $fail
--
1.7.9.5
Run Code Online (Sandbox Code Playgroud)

在另一个 make 之后,您可以使用以下命令对其进行测试:

  src/ls -rtz | xargs -0 -n1 src/ls -ld
Run Code Online (Sandbox Code Playgroud)

所以补丁确实有效,我看不出有什么原因不能,但这并不能证明没有技术上的理由不考虑这个选项。ls -R0可能没有多大意义,但也没有ls -Rm哪个ls能做到开箱即用。