web-dev-qa-db-fra.com

Y a-t-il une raison pour laquelle LS n'a pas d'option -zero ou -0

Cette question a été invitée par des questions sur ls '-1 Option et la tendance récurrente des personnes à poser des questions et des réponses qui inclut le traitement de la sortie de ls.

Cette réutilisation de la sortie ls semble compréhensible, par exemple: Si vous savez comment trier une liste de fichiers avec ls Vous pouvez utiliser la sortie de cette manière comme entrée pour autre chose.

Si ces questions et réponses n'incluent pas une référence à la liste de noms de fichiers fabriquée consistant à des noms de fichiers bien comportant (pas de caractères spéciaux tels que des espaces et de nouvelles lignes), ils sont souvent commentés par une personne indiquant que le danger de la séquence de commande ne fonctionne pas lorsqu'il sont des fichiers avec de nouvelles lignes, des espaces, etc.

find, sort et d'autres utilitaires résolvent le problème de la communication des noms de fichiers "difficiles" à par exemple. xargs En utilisant une option pour séparer les noms de fichiers avec le caractère nul/octet qui n'est pas un caractère valide dans le nom du fichier (le seul en plus de /?) sur UNIX/Linux systèmes de fichiers.

J'ai regardé à la page de l'homme pour ls et la sortie pour ls --help (qui a plus d'options répertoriées) et n'a pas pu trouver que ls (à partir de coreutils ) a une option pour spécifier la sortie séparée nul. Il dispose d'une option -1 qui peut être interprétée comme "Noms de fichier de sortie séparés par Newline" )

Q : Est-ce qu'il y a une raison technique ou philosophique pour laquelle ls n'a pas de --zero ou -0 Option qui "générerait des noms de fichier séparés par NUL"?

Si vous faites quelque chose qui génère uniquement les noms de fichiers (et n'utilisez pas E.G. -l) pouvant avoir du sens:

ls -rt -0 | xargs -r0 …

Il me manque quelque chose pourquoi cela ne fonctionnerait pas, ou y a-t-il une alternative pour cet exemple que j'ai négligé et ce n'est pas beaucoup plus compliqué et/ou obscur .


Addenda:

Faire ls -lrt -0 _ N'a probablement pas beaucoup de sens, mais de la même manière que find . -ls -print0 ne le fait pas, donc ce n'est pas une raison de ne pas fournir un -0/-z/--zero option.

38
Timo

Mise à jour (2014-02-02)

Merci à notre propre @ Anthon's Détermination dans suite à l'absence de cette fonctionnalité , nous avons une raison légèrement plus formelle que cette caractéristique manque, ce qui réitère ce que je expliqué plus tôt:

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

Merci beaucoup pour le patch. Si nous devions le faire, ceci est l'interface que nous utiliserions. Cependant, LS est vraiment un outil de consommation directe par un humain et, dans ce cas, le traitement est moins utile. Pour une transformation à fortuber, trouver (1) est plus adapté. C'est bien décrit dans la première réponse au lien ci-dessus.

Je serais donc 70h30 contre l'ajout de cela.

Ma réponse originale


C'est un peu d'opinion personnelle, mais je pense que c'est une décision de conception dans la sortie de ce passage de ls. Si vous remarquez que la commande find a reçu ce commutateur:

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

En laissant cette sortie, les concepteurs impliquaient que vous ne devriez pas utiliser ls Sortie pour autre chose que la consommation humaine. Pour le traitement en aval par d'autres outils, vous devez utiliser find à la place.

Façons d'utiliser trouver

Si vous recherchez simplement les méthodes alternatives, vous pouvez les trouver ici, intitulé: le faire correctement: un résumé rapide . De ce lien, il s'agit probablement des 3 modèles plus courants:

  1. Simple Trouver -exec; Difficile de manier si la commande est grande et crée 1 processus/fichier:
    find . -exec COMMAND... {} \;
    
  2. Simple Recherche -exec avec +, plus rapide si plusieurs fichiers sont corrects pour la commande: [.____]
    find . -exec COMMAND... {} \+
    
  3. Utilisez Rechercher et Xargs avec\0 Séparateurs

    (Extensions communes non standard --Print0 et -0. Fonctionne sur GNU, * BSDS, Busybox)

    find . -print0 | xargs -0 COMMAND
    

Une preuve supplémentaire?

J'ai trouvé ce blog post de Joey Hess 'Blog intitulé: " LS: les options manquantes ". Un des commentaires intéressants dans ce post:

Le seul manque évident est une option A -Z, ce qui devrait rendre les noms de fichiers de sortie NULL résiliés pour la consommation de consultations par d'autres programmes. Je pense que cela serait facile à écrire, mais j'ai été exterminement occupé à IRL (déplacer beaucoup de meubles) et ne l'a pas atteint. Des preneurs pour l'écrire?

Recherche ultérieure, j'ai trouvé cela dans les journaux de validation de l'un des commutateurs supplémentaires que la publication du blog de Joey's mentionne: " nouveau format de sortie -j ", il semblerait donc que le poteau de blog se moque de la notion de toujours ajouter un -z Passer à ls.

Quant aux autres options, plusieurs personnes conviennent que -e est presque presque utile, bien qu'aucun d'entre nous ne puisse trouver une raison de l'utiliser. Mon rapport de bogue a négligé de mentionner que LS-ser est très buggy. -j est clairement une blague.

Les références

39
slm

Comme les réponses de @ SLM entre dans les origines et les raisons possibles, je ne vais pas répéter cela ici. Une telle option est non sur la liste Coreutils liste de fonctions rejetées , mais le correctif ci-dessous est maintenant rejeté par Pádraig Brady Après l'avoir envoyée à la liste de diffusion Coreutils. De la réponse, il est clair que c'est une raison philosophique (ls la production est destinée à la consommation humaine).

Si vous souhaitez essayer si une telle option est raisonnable pour vous-même, faites:

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

appliquez ensuite le correctif suivant contre STÉTIGE 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

Après une autre, vous pouvez le tester avec:

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

Donc, le patch fonctionne et je ne peux pas voir une raison pour laquelle ce ne serait pas, mais ce n'est pas une preuve, il n'y a pas de raison technique de laisser l'option. ls -R0 Cela pourrait ne pas avoir de sens, mais non plus ls -Rm qui ls peut faire de la boîte.

20
Anthon

Si vous avez GNU Xargs (de Coreutils), vous pouvez exécuter:

$ ls … |xargs -d "\n" …

Sinon, après avoir confirmé que votre version de xargs prend en charge -0 (la plupart font, mais le Posix Spec pour Xargs ne le mentionne pas même), vous pouvez exécuter:

$ ls … |tr "\n" "\000" |xargs -0 …

Mon ~/.aliases Fichier, que j'ai fourni par mon ~/.bashrc et mon ~/.zshrc et qui existe sur tous les systèmes que j'utilise (nécessitant ainsi une large compatibilité), contient ceci:

# Like `find ... -print0 |xargs -0` for programs that don't have a -print0
xargsn() {  # defer definition until first use
  if xargs --help 2>&1 |grep -Fqw "d, --delimiter"; then  # GNU
    xargsn() { xargs -d "\n" "$@"; }
  else
    # warning, xargs -0 isn't POSIX, but GNU, BSD/OSX, and Busybox support it
    xargsn() { tr "\n" "\000" |xargs -0 "$@"; }
  fi
  xargsn "$@"
}
if command -v compdef >/dev/null 2>&1; then compdef xargsn=xargs; fi # for zsh

Cela reporte l'appel à xargs --help Jusqu'à la première fois que je manquai xargsn afin d'accélérer mon fichier d'alias (plutôt long), ce qui signifie qu'il vérifie GNU xargs sur sa première invocation puis enregistre ensuite que À partir de ce moment-là, après avoir défini la définition (qui est assez rapide, ~ 0,02 secondes), il termine ensuite cette première demande en tant qu'appel récursif unique.

Cette dernière ligne indique à Z-Shell pour terminer xargsn comme s'il s'agissait de xargs (puisque c'est).

Cette fonction xargsn fonctionne pour toute commande aussi longtemps que vous n'êtes pas itération des articles pouvant contenir des pauses de ligne. AVERTISSEMENT: Les noms de fichiers sur de nombreux systèmes de fichiers sont techniquement autorisés à contenir des pauses de la ligne. Assurez-vous de ne pas laisser aucun problème catastrophique si vous avez un tel nom de fichier. (Voir aussi la "Ne pas analyser ls" arguments.)

0
Adam Katz