¿Hay alguna razón por la cual ls no tiene una opción - cero o -0

37

Esta pregunta fue provocada por preguntas sobre la ls' -1opción y la tendencia recurrente de las personas a hacer preguntas y respuestas que incluyen el procesamiento de la salida de ls.

Esta reutilización de la salida lsparece comprensible, por ejemplo: si sabe cómo ordenar una lista de archivos ls, puede usar la salida de esa manera como entrada para otra cosa.

Si esas preguntas y respuestas no incluyen una referencia a la lista de nombres de archivo producida que consiste en nombres de archivo que se comportan bien (sin caracteres especiales como espacios y líneas nuevas), a menudo alguien los comenta al señalar el peligro de que la secuencia de comandos no funcione cuando hay son archivos con líneas nuevas, espacios, etc.

find, sorty otras utilidades resuelven el problema de comunicar nombres de archivo "difíciles", por ejemplo, xargsmediante el uso de una opción para separar los nombres de archivo con el carácter / byte NUL que no es un carácter válido en el nombre de archivo (¿el único además de /?) en Sistemas de archivos Unix / Linux.

Miré a la página del manual lsy la salida para ls --help(que tiene más opciones enumeradas) y no pude encontrar que ls(desde coreutils) tiene una opción para especificar la salida separada por NUL. Tiene una -1opción que puede interpretarse como "nombres de archivo de salida separados por nueva línea" )

P : ¿Hay lsalguna razón técnica o filosófica por la que no tiene una opción --zerou -0opción que "muestre nombres de archivos separados por NUL"?

Si hace algo que solo genera los nombres de archivo (y no usa, por ejemplo -l), eso podría tener sentido:

ls -rt -0 | xargs -r0 

Podría estar perdiendo algo por qué esto no funcionaría, o hay una alternativa para este ejemplo que pasé por alto y que no es mucho más complicada u oscura .


Apéndice:

Hacerlo ls -lrt -0probablemente no tiene mucho sentido, pero de la misma manera que find . -ls -print0no lo hace, por lo que no es una razón para no proporcionar una opción -0/ -z/ --zero.

Timo
fuente
Lo obvio que debe hacer es escribir y preguntar al responsable de GNU coreutils cuáles son sus pensamientos acerca de tal opción.
Faheem Mitha
1
ls -rtzDefinitivamente sería útil. Contraste la alternativa: superuser.com/a/294164/21402
Tobu

Respuestas:

37

ACTUALIZACIÓN (2014-02-02)

Gracias a nuestra propia determinación de @ Anthon al seguir la falta de esta función , tenemos una razón un poco más formal de por qué falta esta función, que reitera lo que expliqué 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

Muchas gracias por el parche. Si tuviéramos que hacer esto, entonces esta es la interfaz que usaríamos. Sin embargo, ls es realmente una herramienta para el consumo directo de un ser humano, y en ese caso el procesamiento posterior es menos útil. Para un procesamiento posterior, find (1) es más adecuado. Eso está bien descrito en la primera respuesta en el enlace de arriba.

Entonces estaría 70:30 en contra de agregar esto.

Mi respuesta original


Este es un poco de mi opinión personal, pero creo que es una decisión de diseño para dejar ese cambio ls. Si nota que el findcomando tiene este modificador:

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

Al dejar ese interruptor apagado, los diseñadores estaban insinuando que no debería usar la lssalida para nada que no sea el consumo humano. Para el procesamiento posterior por otras herramientas, debe usar en su findlugar.

Formas de usar find

Si solo está buscando los métodos alternativos, puede encontrarlos aquí, titulados: Hacerlo correctamente: un resumen rápido . Desde ese enlace, estos son probablemente los 3 patrones más comunes:

  1. Simple encontrar -exec; difícil de manejar si COMMAND es grande y crea 1 proceso / archivo:
    find . -exec COMMAND... {} \;
  2. Simple find -exec con +, más rápido si varios archivos están bien para COMMAND:
    find . -exec COMMAND... {} \+
  3. Use find y xargs con separadores \ 0

    (Extensiones comunes no estándar -print0 y -0. Funciona en GNU, * BSDs, busybox)

    find . -print0 | xargs -0 COMMAND

¿Más evidencia?

Encontré esta publicación de blog del blog de Joey Hess titulada: " ls: las opciones que faltan ". Uno de los comentarios interesantes en este post:

La única falta obvia ahora es una opción -z, que debería hacer que los nombres de los archivos de salida sean terminados en NULL para que otros programas los consuman. Creo que esto sería fácil de escribir, pero he estado extremadamente ocupado con IRL (moviendo muchos muebles) y no lo logré. Cualquier tomadores para escribirlo?

Al seguir buscando, encontré esto en los registros de confirmación de uno de los modificadores adicionales que menciona la publicación del blog de Joey, " nuevo formato de salida -j ", por lo que parece que la publicación del blog se estaba burlando de la idea de agregar un -zinterruptor ls.

En cuanto a las otras opciones, varias personas están de acuerdo en que -e es casi casi útil, aunque ninguno de nosotros puede encontrar una razón para usarlo. Mi informe de error no mencionó que ls -eR tiene muchos errores. -j es claramente una broma.

Referencias

slm
fuente
Gracias. Soy consciente de las advertencias. Ninguna pregunta sobre el procesamiento de salida de ls está completa sin tener eso señalado ;-)
Timo
@Timo - Sé que lo haces, estaba haciendo más por los futuros lectores de esta Q. Te veo en el sitio, que estos ya habrían aparecido en tus búsquedas a las 8-)
slm
Me di cuenta de eso, y bien que lo hiciste. Debería haber incluido referencias a por qué no (al menos no hasta que -0se implemente) en mi pregunta, para no desviar a las personas.
Timo
Por supuesto, suponiendo que no haya nada realmente exótico como un '\ n' en un nombre de archivo, ls -1 | tr '\012' '\000'se enumerarán los archivos separados por caracteres NULL.
samiam
2
Este artículo aborda las profundidades de los problemas de archivo: dwheeler.com/essays/fixing-unix-linux-filenames.html
slm
20

Como las respuestas de @ slm van a los orígenes y posibles razones, no repetiré eso aquí. Dicha opción no está en la lista de características rechazadas de coreutils , pero el parche a continuación ahora es rechazado por Pádraig Brady después de enviarlo a la lista de correo de coreutils. De la respuesta está claro que esta es una razón filosófica (la lsproducción es para consumo humano).

Si desea probar si dicha opción es razonable para usted, haga lo siguiente:

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

luego aplique el siguiente parche 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 ('\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

Después de otra marca puedes probarlo con:

  src/ls -rtz | xargs -0 -n1 src/ls -ld

Entonces, el parche funciona y no puedo ver una razón por la que no lo haría, pero eso no es una prueba de que no haya una razón técnica para dejar de lado la opción. ls -R0puede que no tenga mucho sentido, pero tampoco lo ls -Rmque lspuede hacer fuera de la caja.

Anthon
fuente
Tener -zy --zeroestá más en línea con la clasificación (también en coreutils.
Anthon