Existe uma razão pela qual o ls não possui uma opção --zero ou -0?

32

Esta pergunta foi motivada por perguntas sobre a opção ls ' -1 e a tendência recorrente de as pessoas fazerem perguntas e respostas que incluem o processamento da saída de ls .

Essa reutilização da saída ls parece ser compreensível, por exemplo: se você souber como classificar uma lista de arquivos com ls , convém usar a saída dessa maneira como entrada para outra coisa.

Se esses Q & A não incluírem uma referência à lista de nomes de arquivos produzida consistindo em nomes de arquivos com bom comportamento (sem caracteres especiais como espaços e novas linhas), eles são frequentemente comentados por alguém apontando o perigo do comando seqüência não funciona quando existem arquivos com novas linhas, espaços etc.

find , sort e outras utilidades resolvem o problema de comunicar nomes de ficheiros "difeis" para p. xargs usando uma opção para separar os nomes dos arquivos com o caractere NUL / byte que não é um caractere válido no nome do arquivo (o único além de / ?) em sistemas de arquivos Unix / Linux.

Eu olhei para a página de manual para ls e a saída para ls --help (que tem mais opções listadas) e não consegui encontrar que ls (de coreutils ) tem uma opção para especificar saída separada NUL. Ele tem uma opção -1 que pode ser interpretada como "nomes de arquivos de saída separados por nova linha" )

Q : Existe uma razão técnica ou filosófica para que ls não tenha uma opção --zero ou -0 que "produziria nomes de arquivos separados por NUL"?

Se você fizer algo que produza apenas os nomes dos arquivos (e não use, por exemplo, -l ), isso pode fazer sentido:

ls -rt -0 | xargs -r0 …

Eu poderia estar perdendo algo porque isso não funcionaria, ou existe uma alternativa para este exemplo que eu ignorei e que não é muito mais complicado e / ou obscuro .

Adendo:

Fazer ls -lrt -0 provavelmente não faz muito sentido, mas da mesma forma que find . -ls -print0 não faz, por isso não é um motivo para não fornecer uma opção -0 / -z / --zero . / p>     

por Timo 02.02.2014 / 09:38

2 respostas

34

UPDATE (2014-02-02)

Graças à nossa própria determinação @ Anthon em após a falta deste recurso , temos uma razão um pouco mais formal de por que esse recurso está faltando, o que reitera o que expliquei anteriormente:

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

Thanks a lot for the patch. If we were to do this then this is the interface we would use. However ls is really a tool for direct consumption by a human, and in that case further processing is less useful. For futher processing, find(1) is more suited. That is well described in the first answer at the link above.

So I'd be 70:30 against adding this.

Minha resposta original

Isso é um pouco da minha opinião pessoal, mas acredito que seja uma decisão de design deixar essa opção em ls . Se você perceber que o comando find tem essa opção:

-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.

Ao deixar essa opção, os designers estavam sugerindo que você não deveria usar ls output para outra coisa que não seja o consumo humano. Para processamento downstream por outras ferramentas, você deve usar find .

Formas de usar o find

Se você está apenas procurando pelos métodos alternativos, você pode encontrá-los aqui, intitulados: Doing corretamente: Um resumo rápido . A partir desse link, esses são provavelmente os três padrões mais comuns:

  1. Simples encontrar -exec; difícil de gerenciar se o comando for grande e criar 1 processo / arquivo:
    find . -exec COMMAND... {} \;
    
  2. Simples encontre -exec com +, mais rápido se vários arquivos estiverem corretos para COMMAND:
    find . -exec COMMAND... {} \+
    
  3. Use find e xargs com separadores \ 0

    (extensões comuns não padrão -print0 e -0. Funciona no GNU, * BSDs, busybox)

    find . -print0 | xargs -0 COMMAND
    

Outras evidências?

Eu encontrei este post do blog Joey Hess intitulado: " ls: as opções ausentes ". Um dos comentários interessantes deste post:

The only obvious lack now is a -z option, which should make output filenames be NULL terminated for consuption by other programs. I think this would be easy to write, but I've been extermely busy IRL (moving lots of furniture) and didn't get to it. Any takers to write it?

Pesquisas adicionais Encontrei isso nos logs de commit de um dos switches adicionais mencionados na postagem do blog do Joey, " novo formato de saída -j ", parece que o post do blog estava ridicularizando a ideia de adicionar uma -z switch a ls .

As to the other options, multiple people agree that -e is nearly almost useful, although none of us can quite find a reason to use it. My bug report neglected to mention that ls -eR is very buggy. -j is clearly a joke.

Referências

por 02.02.2014 / 09:50
19

Como respostas de @ slm vai para as origens e possíveis razões não vou repetir isso aqui. Tal opção é não na lista de recursos rejeitados do coreutils, mas o patch abaixo é agora rejeitado por Pádraig Brady após o envio para a lista de discussão do coreutils. A partir da resposta, fica claro que essa é uma razão filosófica ( ls output é para consumo humano).

Se você quiser experimentar se essa opção for razoável para você, faça o seguinte:

git clone git://git.sv.gnu.org/coreutils
cd coreutils
./bootstrap
./configure
make

em seguida, aplique o seguinte patch contra commit 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 ('
  src/ls -rtz | xargs -0 -n1 src/ls -ld
'); + } + 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
git clone git://git.sv.gnu.org/coreutils
cd coreutils
./bootstrap
./configure
make
exp
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 ('
  src/ls -rtz | xargs -0 -n1 src/ls -ld
'); + } + 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%pre%exp%pre%out%pre%symlink%pre%" > 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%pre%b%pre%c%pre%" > exp +compare exp out || fail=1 + +rm -rf out exp +ls -rt --zero a b c > out || fail=1 +/bin/echo -en "c%pre%b%pre%a%pre%" > exp +compare exp out || fail=1 + +Exit $fail -- 1.7.9.5
out%pre%symlink%pre%" > 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%pre%b%pre%c%pre%" > exp +compare exp out || fail=1 + +rm -rf out exp +ls -rt --zero a b c > out || fail=1 +/bin/echo -en "c%pre%b%pre%a%pre%" > exp +compare exp out || fail=1 + +Exit $fail -- 1.7.9.5

Depois de outro teste, você pode testá-lo com:

%pre%

Assim, o patch funciona e não consigo ver uma razão pela qual não funcionaria, mas isso não é uma prova de que não haja uma razão técnica para deixar de fora a opção. ls -R0 pode não fazer muito sentido, mas o ls -Rm que ls não pode ser feito imediatamente.

    
por 03.02.2014 / 06:03