Je vois toujours des réponses citant ce lien indiquant définitivement "Ne pas analyser ls
!" Cela me dérange pour deux raisons:
Il semble que l’information contenue dans ce lien ait été acceptée en gros avec peu de questions, bien que je puisse au moins relever quelques erreurs de lecture occasionnelle.
Il semble également que les problèmes énoncés dans ce lien n’ont suscité aucun désir de trouver une solution.
Du premier paragraphe:
... lorsque vous demandez
[ls]
une liste de fichiers, le problème est énorme: Unix autorise presque tous les caractères d'un nom de fichier, y compris les espaces, les nouvelles lignes, les virgules, les symboles de conduite et à peu près tout ce que vous voudriez utiliser auparavant. délimiteur sauf NUL. ...ls
sépare les noms de fichiers avec des nouvelles lignes. C'est bien jusqu'à ce que vous ayez un fichier avec une nouvelle ligne dans son nom. Et comme je ne connais aucune implémentation dels
cela qui vous permette de terminer les noms de fichiers avec des caractères NUL au lieu de sauts de ligne, nous ne pouvons pas obtenir une liste de noms de fichiers en toute sécuritéls
.
Bummer, non? Comment jamais peut - on gérer un saut de ligne fin ensemble de données pour les données répertorié peuvent contenir des sauts de ligne? Eh bien, si les personnes qui répondent aux questions sur ce site Web ne font pas ce genre de choses tous les jours, je penserais peut-être que nous avions des problèmes.
En réalité, la plupart des ls
implémentations fournissent en réalité une API très simple pour analyser leurs résultats et nous le faisons tous depuis le début sans même nous en rendre compte. Non seulement vous pouvez terminer un nom de fichier par null, vous pouvez également commencer par un null ou par toute autre chaîne arbitraire de votre choix. De plus, vous pouvez affecter ces chaînes arbitraires par type de fichier . Veuillez considérer:
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
Voir cela pour plus.
Maintenant, c’est la partie suivante de cet article qui m’amène vraiment:
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
Le problème est que, à la sortie de
ls
, ni vous ni l'ordinateur ne pouvez déterminer quelles parties de celui-ci constituent un nom de fichier. Est-ce chaque mot? Est-ce chaque ligne? Non. Il n'y a pas de réponse correcte à cette question si ce n'est: vous ne pouvez pas le dire.Vous remarquerez également que
ls
parfois, les données de votre nom de fichier sont corrompues (dans notre cas, le\n
caractère entre les mots "a" et "nouvelle ligne" est transformé en ? Point d' interrogation ......
Si vous voulez juste parcourir tous les fichiers du répertoire courant, utilisez une
for
boucle et un glob:
for f in *; do
[[ -e $f ]] || continue
...
done
L'auteur l'appelle en altérant les noms de fichiers lorsqu'il ls
renvoie une liste de noms de fichiers contenant des globs de shell , puis recommande l'utilisation d'un glob de shell pour récupérer une liste de fichiers!
Considérer ce qui suit:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX définit les opérandes -1
et -q
ls
donc:
-q
- Forcer chaque instance de caractères de nom de fichier non imprimables et<tab>
s à être écrite en tant que caractère de point d'interrogation ('?'
). Les implémentations peuvent fournir cette option par défaut si la sortie est destinée à un terminal.
-1
- (Le chiffre un.) Force la sortie à une entrée par ligne.
Globbing n'est pas sans problèmes: la ?
correspondance avec n'importe quel caractère permet à plusieurs ?
résultats dans une liste de faire correspondre le même fichier plusieurs fois. C'est facilement manipulé.
Bien que la manière de procéder ne soit pas l’essentiel - cela ne prend pas grand chose après tout, comme le montre l’illustration ci-dessous - je me demandais pourquoi pas . Selon moi, la meilleure réponse à cette question a été acceptée. Je vous suggérerais d'essayer de vous concentrer plus souvent sur le fait de dire aux gens ce qu'ils peuvent faire plutôt que ce qu'ils ne peuvent pas. Je pense que vous êtes beaucoup moins susceptible de vous tromper du moins.
Mais pourquoi même essayer? Certes, ma principale motivation était que les autres n'arrêtaient pas de me dire que je ne pouvais pas. Je sais très bien que la ls
sortie est aussi régulière et prévisible que vous le souhaiteriez, à condition de savoir quoi chercher. La désinformation me dérange plus que la plupart des choses.
La vérité est que, à l'exception notable des réponses de Patrick et de Wumpus Q. Wumbley (malgré le formidable traitement de ce dernier) , je considère que la plupart des informations des réponses ici sont globalement correctes: un shell glob est à la fois plus simple à utiliser. et généralement plus efficace pour la recherche dans le répertoire actuel que l'analyse syntaxique ls
. Cependant, ils ne constituent pas, du moins à mon égard, une raison suffisante pour justifier la propagation de la désinformation citée dans l'article ci-dessus, ni une justification acceptable pour " ne jamais analyserls
" .
Veuillez noter que les résultats contradictoires de la réponse de Patrick résultent principalement de son utilisation à ce moment- zsh
là bash
. zsh
- par défaut - la $(
commande de fractionnement de mots substituée ne produit pas les )
résultats de manière portable. Alors, quand il demande où est allé le reste des fichiers? la réponse à cette question est que votre coquille les a mangés. C'est pourquoi vous devez définir la SH_WORD_SPLIT
variable lorsque vous utilisez zsh
et manipulez du code shell portable. Je considère que son omission de noter cela dans sa réponse est terriblement trompeuse.
La réponse de Wumpus ne calcule pas pour moi - dans un contexte de liste, le ?
caractère est un glob shell. Je ne sais pas comment dire autrement.
Afin de gérer un cas de résultats multiples, vous devez limiter la gourmandise du glob. Ce qui suit va juste créer une base de test de noms de fichiers affreux et l'afficher pour vous:
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
SORTIE
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
Maintenant , je vais en sécurité tous les caractères qui n'est pas /slash
, -dash
, :colon
ou caractère alphanumérique dans un glob shell alors sort -u
la liste des résultats uniques. Ceci est sûr car nous ls
avons déjà sauvegardé tous les caractères non imprimables. Regarder:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
SORTIE:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
Ci-dessous, j'aborde à nouveau le problème mais j'utilise une méthodologie différente. Rappelez-vous que, outre \0
null, le /
caractère ASCII est le seul octet interdit dans un chemin d'accès. Je mets globs de côté ici et combine à la place l' -d
option spécifiée par POSIX pour ls
et la -exec $cmd {} +
construction également spécifiée par POSIX pour find
. Etant donné find
qu’il n’en émettra naturellement que /
successivement, les éléments suivants permettent d’obtenir facilement une liste de fichiers récursive et délimitée de manière fiable, y compris toutes les informations de répertoire pour chaque entrée. Imaginez ce que vous pourriez faire avec quelque chose comme ceci:
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
peut être très utile - surtout lorsque l'unicité des résultats est en cause.
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
Ce ne sont que les moyens les plus portables que je puisse penser. Avec GNU, ls
vous pouvez faire:
ls --quoting-style=WORD
Enfin, voici une méthode d' analysels
beaucoup plus simple que j'utilise souvent lorsque j'ai besoin de numéros d'inode:
ls -1iq | grep -o '^ *[0-9]*'
Cela ne fait que renvoyer des numéros d'inode - ce qui est une autre option pratique spécifiée par POSIX.
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s vstime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28sstat
ma réponse, car elle vérifie en réalité que chaque fichier existe. Votre part au bas avec lased
chose ne fonctionne pas.ls
en premier lieu? Ce que vous décrivez est très difficile. Je vais devoir le déconstruire pour tout comprendre et je suis un utilisateur relativement compétent. Vous ne pouvez pas vous attendre à ce que votre Joe moyen soit capable de gérer quelque chose comme ça.ls
sortie est erronée ont été bien couvertes dans le lien d'origine (et dans de nombreux autres endroits). Cette question aurait été raisonnable si OP demandait de l'aide pour le comprendre, mais au lieu de cela, OP essaie simplement de prouver que son utilisation incorrecte est correcte.parsing ls is bad
. Compterfor something in $(command)
sur les mots pour obtenir des résultats précis est une mauvaisecommand's
chose pour la grande majorité de ceux-ci qui ne disposent pas d'une sortie simple.Réponses:
Je ne suis pas du tout convaincu de cela, mais supposons, aux fins d'argument, que vous pourriez , si vous êtes prêt à faire un effort suffisant, analyser le résultat de
ls
manière fiable, même face à un "adversaire" - quelqu'un qui connaît le code que vous avez écrit et choisit délibérément des noms de fichiers conçus pour le casser.Même si vous pouviez le faire, ce serait quand même une mauvaise idée .
Bourne shell n'est pas une bonne langue. Il ne devrait pas être utilisé pour quelque chose de compliqué, à moins que la portabilité extrême soit plus importante que tout autre facteur (par exemple
autoconf
).J'affirme que si vous rencontrez un problème où l'analyse de la sortie de
ls
semble être le chemin de moindre résistance pour un script shell, c'est une forte indication que tout ce que vous faites est trop compliqué pour shell et vous devez le réécrire en entier. Perl ou Python. Voici votre dernier programme en Python:Cela n’a aucun problème avec les caractères inhabituels dans les noms de fichiers - le résultat est ambigu de la même manière que le résultat
ls
est ambigu, mais cela n’aurait pas d’importance dans un "vrai" programme (par opposition à une démo comme celle-ci), ce qui utiliser le résultat deos.path.join(subdir, f)
directement.Tout aussi important, et à la différence de ce que vous avez écrit, cela aura encore un sens dans six mois et il sera facile de le modifier lorsque vous en aurez besoin pour faire quelque chose de légèrement différent. A titre d'illustration, supposons que vous découvriez la nécessité d'exclure les sauvegardes de fichiers de point et les éditeurs, et de tout traiter dans l'ordre alphabétique par nom de base:
la source
for in | for in
parle de récursion? Je ne suis pas sûr. Même si c'est le cas, il ne peut y avoir plus d'un, non? C’est la seule réponse qui me semble logique jusqu’à présent.for
boucles imbriquées .os.walk
fait de gros efforts dans les coulisses, mais vous n’avez pas à vous soucier de cela, pas plus que de votre façon de travaillerls
ou defind
travailler en interne.os.walk
retourne un objet générateur . Les générateurs sont la version Python des listes paresseuses. Chaque fois que la boucle for externe itère, le générateur est appelé et "renvoie" le contenu d'un autre sous-répertoire. Les fonctionnalités équivalentes en Perl sontFile::Find
, si cela aide.ls
sortie.Ce lien est souvent référencé car les informations sont tout à fait exactes et existent depuis très longtemps.
ls
remplace les caractères non imprimables par des caractères globaux oui, mais ces caractères ne figurent pas dans le nom de fichier réel. Pourquoi est-ce important? 2 raisons:Par exemple:
Remarquez comment nous avons 2 fichiers qui se ressemblent exactement. Comment allez-vous les distinguer s'ils sont tous deux représentés
a?b
?Il y a une différence ici. Comme vous pouvez le constater, lorsque vous obtenez un fichier glob, celui-ci peut correspondre à plusieurs fichiers. Toutefois, lorsque vous parcourez les résultats correspondant à un glob, vous récupérez le fichier exact, pas un glob.
Par exemple:
Remarquez comment la
xxd
sortie montre qu’elle$file
contenait les caractères bruts\t
et\n
non?
.Si vous utilisez
ls
, vous obtenez ceci à la place:"Je vais quand même itérer, pourquoi ne pas utiliser
ls
?"Votre exemple que vous avez donné ne fonctionne pas réellement. On dirait que ça marche, mais ça ne marche pas.
Je parle de ceci:
J'ai créé un répertoire avec un tas de noms de fichiers:
Quand je lance votre code, je reçois ceci:
Où est passé le reste des fichiers?
Essayons ceci à la place:
Utilisons maintenant un glob réel:
Avec bash
L'exemple ci-dessus était avec mon shell normal, zsh. Lorsque je répète la procédure avec bash, vous obtenez un autre ensemble de résultats complètement différent avec votre exemple:
Même ensemble de fichiers:
Des résultats radicalement différents avec votre code:
Avec un shell glob, cela fonctionne parfaitement bien:
La raison pour laquelle bash se comporte de cette manière remonte à l'un des points que j'ai mentionnés au début de la réponse: "Le fichier glob peut correspondre à plus d'un fichier".
ls
renvoie le même glob (a?b
) pour plusieurs fichiers, donc chaque fois que nous développons ce glob, nous obtenons chaque fichier correspondant.Comment recréer la liste des fichiers que j'utilisais:
Les codes hexadécimaux sont des caractères UTF-8 NBSP.
la source
ls
. J'ai également demandé que vous testiez votre code car il ne fonctionne pas. Qu'est-ce que zsh a à voir avec tout ça?Essayons de simplifier un peu:
Voir? C'est déjà faux juste là. Il y a 3 fichiers mais bash en signale 4. C'est parce
set
qu'on lui donne les globs générésls
qui sont développés par le shell avant d'être passés àset
. C'est pourquoi vous obtenez:Ou, si vous préférez:
Ce qui précède a été exécuté
bash 4.2.45
.la source
ls -1qRi | grep -o '^ *[0-9]*'
- cela consiste à analyser lals
sortie, mec, et c’est le moyen le plus rapide et le meilleur que je connaisse pour obtenir une liste de numéros d’inodes.La sortie de
ls -q
n'est pas un globe du tout. Cela?
veut dire "Il y a un caractère ici qui ne peut pas être affiché directement". Globs?
voulait dire "n'importe quel caractère est autorisé ici".Les globes ont d'autres caractères spéciaux (
*
et[]
au moins, et à l'intérieur de la[]
paire, il y en a plus). Aucun de ceux-ci n'est échappéls -q
.Si vous traitez la
ls -1q
sortie, il y a un ensemble de globs et les développez, non seulement vous obtiendrezx
deux fois, mais vous manquerez[x]
complètement. En tant que glob, il ne correspond pas à une chaîne.ls -q
est conçu pour protéger vos yeux et / ou votre terminal des personnages loufoques, et non pour produire quelque chose que vous pouvez renvoyer à la coque.la source
La réponse est simple: les cas particuliers que
ls
vous devez gérer l'emportent sur les avantages éventuels. Ces cas particuliers peuvent être évités si vous n'analysez pas lals
sortie.Le mantra ici est de ne jamais faire confiance au système de fichiers de l'utilisateur (l'équivalent de ne jamais faire confiance aux entrées de l'utilisateur ). S'il existe une méthode qui fonctionnera toujours, avec une certitude de 100%, ce devrait être la méthode que vous préférez, même si c'est
ls
la même chose mais avec moins de certitude. Je n'entrerai pas dans les détails techniques, ceux-ci ayant été largement couverts par terdon et Patrick . Je sais qu’en raison des risques liés à l’utilisationls
d’une transaction importante (et peut-être coûteuse) dans laquelle mon travail / mon prestige est en jeu, je préférerai toute solution ne comportant pas un degré d’incertitude si elle peut être évitée.Je sais que certaines personnes préfèrent certains risques à la certitude , mais j'ai déposé un rapport de bogue .
la source
La raison pour laquelle les gens disent de ne jamais faire quelque chose n'est pas nécessairement parce que cela ne peut absolument pas être fait correctement. Nous pourrons peut-être le faire, mais ce sera peut-être plus compliqué, moins efficace, que ce soit en termes d'espace ou de temps. Par exemple, il serait parfaitement correct de dire "Ne construisez jamais un backend e-commerce volumineux dans un assemblage x86".
Passons maintenant au problème: comme vous l’avez démontré, vous pouvez créer une solution qui analyse et donne le résultat voulu. L’exactitude n’est donc pas un problème.
Est-ce plus compliqué? Oui, mais nous pouvons cacher cela derrière une fonction d'assistance.
Alors maintenant à l'efficacité:
Gain d'espace: votre solution repose sur
uniq
le filtrage des doublons. Par conséquent, nous ne pouvons pas générer les résultats paresseusement. Donc, soitO(1)
contreO(n)
ou les deux ontO(n)
.Gain de temps: dans le meilleur des cas, on
uniq
utilise une approche de hachage, de sorte que nous avons toujours unO(n)
algorithme dans le nombre d'éléments obtenus , probablement bienO(n log n)
.Maintenant le vrai problème: bien que votre algorithme n’ait toujours pas l’air mauvais, j’ai vraiment fait attention à utiliser des éléments obtenus et non des éléments pour n. Parce que cela fait une grande différence. Supposons que vous ayez un fichier
\n\n
qui donnera un glob pour??
correspondre à tous les fichiers de 2 caractères de la liste. Bizarrement, si vous avez un autre fichier\n\r
qui aboutira??
et renverra également tous les fichiers de 2 caractères .. voyez où cela se passe? Le comportement exponentiel au lieu du comportement linéaire peut certainement être qualifié de «pire comportement à l'exécution». C'est la différence entre un algorithme pratique et un algorithme sur lequel vous écrivez des articles dans des revues théoriques CS.Tout le monde aime les exemples, non? Et c'est parti. Créez un dossier appelé "test" et utilisez ce script python dans le même répertoire que le dossier.
La seule chose à faire est de générer tous les produits de longueur 3 pour 7 caractères. Les mathématiques au lycée nous disent qu'il devrait y avoir 343 fichiers. Cela devrait être très rapide à imprimer, alors voyons:
Essayons maintenant votre première solution, car je ne peux vraiment pas obtenir ceci.
chose ici pour travailler sur Linux mint 16 (ce qui en dit long sur la facilité d’utilisation de cette méthode).
Quoi qu'il en soit, puisque ce qui précède ne filtre que le résultat après l'avoir obtenu, la solution précédente devrait être au moins aussi rapide que la dernière (aucune astuce d'inode dans celle-ci - mais celles-ci ne sont pas fiables, vous abandonneriez la correction).
Alors maintenant combien de temps
prendre? Eh bien, je ne sais vraiment pas, il faut un certain temps pour vérifier les noms de fichiers 343 ^ 343 - je vous le dirai après la mort de l'univers par la chaleur.
la source
Intention déclarée de l'OP adressée
Préface et justification de la réponse originale † Mis à jour le 2015-05-18
Dans la dernière mise à jour de sa question, mikeserv (le PO) a déclaré: «J’estime dommage que j’ai d’abord posé cette question pour signaler une source de désinformation et, malheureusement, la réponse la plus votée est en grande partie trompeuse. "
Bien, OK; Je pense qu'il était un peu dommage que j'ai passé tellement de temps à essayer de comprendre comment expliquer mon sens pour constater que que je relis la question. Cette question a fini par "[générer] une discussion plutôt que des réponses" ‡ et a fini par peser à environ 18K de texte (pour la question seule, pour que tout soit clair), ce qui serait long même pour un article de blog.
Mais StackExchange n'est pas votre tribune et ce n'est pas votre blog. Cependant, en réalité, vous l'avez utilisé au moins un peu des deux. Les gens finissaient par passer beaucoup de temps à répondre à votre question au lieu de répondre à leurs questions. À ce stade, je signalerai que la question ne convient pas à notre format, étant donné que le PO a explicitement indiqué qu'il ne s'agissait même pas du tout d'une question.
À ce stade, je ne suis pas sûr si ma réponse était exacte ou non; probablement pas, mais cela visait certaines de vos questions et cela pourrait peut-être être une réponse utile à quelqu'un d'autre; les débutants prennent courage, certains de ces "ne" deviennent pas "faire" parfois une fois que vous avez plus d'expérience. :)
En règle générale...
veuillez pardonner les aspérités restantes; J'ai déjà passé beaucoup trop de temps là-dessus ... plutôt que de citer directement l'OP (comme prévu à l'origine), je vais essayer de résumer et de paraphraser.
[en grande partie retravaillée à partir de ma réponse initiale]
après examen, je crois avoir mal interprété l'accent mis par le PO sur les questions auxquelles j'ai répondu; cependant, les points abordés ont été soulevés et j'ai laissé les réponses en grande partie intactes, car je pense qu'elles sont claires et qu'elles abordent des problèmes que j'ai vus soulevés dans d'autres contextes, en ce qui concerne les conseils aux débutants.
Le message original demandait, de plusieurs manières, pourquoi différents articles donnaient des conseils tels que «Ne pas analyser la
ls
sortie» ou «Vous ne devriez jamais analyser lals
sortie», etc.Ma solution suggérée au problème est que les exemples de ce type d’énoncé ne sont que des exemples d’idiomes, formulés de manière légèrement différente, dans lesquels un quantificateur absolu est associé à un impératif [par exemple, «ne jamais [jamais] X», «[Vous devriez] toujours Y», «[il ne faut jamais» Z »] pour former des énoncés destinés à être utilisés comme règles générales ou directives, en particulier lorsqu'ils sont donnés à des personnes novices, plutôt que comme des vérités absolues, forme apparente de ces déclarations nonobstant.
Lorsque vous commencez à apprendre de nouveaux sujets, et à moins de bien comprendre pourquoi vous devez faire autrement, il est judicieux de simplement suivre les règles générales acceptées sans exception - à moins que vous ne soyez guidé par quelqu'un de plus expérimenté. que vous-même. Avec de plus en plus de compétences et d'expérience, vous serez en mesure de déterminer quand et si une règle s'applique dans une situation donnée. Une fois que vous aurez atteint un niveau d’expérience significatif, vous comprendrez probablement le raisonnement qui sous-tend la règle générale et vous pourrez alors commencer à utiliser votre jugement pour déterminer si et à quel niveau les motifs de la règle s’appliquent cette situation et aussi s’il existe peut-être des préoccupations primordiales.
Et c'est à ce moment-là qu'un expert, peut-être, pourrait choisir de faire des choses en violation du "Règlement". Mais cela ne les rendrait pas moins "Les règles".
Et donc, pour le sujet à traiter: à mon avis, juste parce qu’un expert peut violer cette règle sans se faire complètement abattre, je ne vois aucune façon de justifier que vous disiez à un débutant que "parfois", c’est ok pour analyser la
ls
sortie, parce que: ce n'est pas . Ou du moins, certainement, ce n'est pas correct pour un débutant.Vous mettez toujours vos pions au centre; dans le premier morceau, un mouvement; château à la première occasion; les chevaliers avant les évêques; un chevalier sur le rebord est sinistre; et assurez-vous toujours que vous pouvez voir votre calcul jusqu'au bout! (Oups, désolé, je suis fatigué, c'est pour les échecs StackExchange.)
Règles, destinées à être brisées?
Lorsque vous lisez un article sur un sujet qui est destiné aux débutants ou sur lequel il est susceptible d'être lu, vous verrez souvent des choses comme ceci:
Bien que ces déclarations semblent certes énoncer des règles absolues et intemporelles, elles ne le sont pas; à la place, c’est une façon d’énoncer des règles générales [c.-à-d. «directives», «règles générales», «les bases», etc.] qui est du moins sans doute une façon appropriée de les énoncer pour les débutants qui pourraient lire ces articles. Cependant, juste parce qu’elles sont énoncées comme absolues, les règles ne lient certainement pas les professionnels et les experts [qui sont probablement ceux qui ont résumé ces règles au départ - comme moyen de consigner et de transmettre les connaissances acquises lorsqu’elles ont été traitées de manière récurrente. questions dans leur métier particulier.]
Ces règles ne vont certainement pas révéler la manière dont un expert traiterait un problème complexe ou nuancé dans lequel, par exemple, ces règles sont en conflit; ou dans lequel les préoccupations qui ont conduit à la règle en premier lieu ne s'appliquent tout simplement pas. Les experts n'ont pas peur (ou ne devraient pas avoir peur!) De simplement enfreindre les règles qu'ils savent ne pas avoir de sens dans une situation donnée. Les experts doivent constamment trouver un équilibre entre les risques et les préoccupations de leur métier et doivent fréquemment user de leur jugement pour choisir de ne pas respecter ce type de règles. Ils doivent équilibrer divers facteurs et ne pas être en mesure de se fier à un tableau de règles à suivre. Prenons
Goto
un exemple: il y a eu un long débat récurrent sur leur nocivité. (Ouais, ne jamais utiliser gotos.; D)Une proposition modale
Une caractéristique étrange, du moins en anglais, et j'imagine dans de nombreuses autres langues, de règles générales, est qu'elles sont énoncées sous la même forme qu'une proposition modale, alors que les experts dans un domaine sont disposés à donner une règle générale tout en sachant qu’ils enfreindront la règle le cas échéant. Il est donc clair que ces instructions ne sont pas censées être équivalentes aux mêmes instructions de la logique modale.
C'est pourquoi je dis qu'ils doivent simplement être idiomatiques. Plutôt que d'être véritablement une situation "jamais" ou "toujours", ces règles servent généralement à codifier des orientations générales qui ont tendance à être appropriées dans un large éventail de situations et qui, lorsque les débutants les suivent aveuglément, risquent de se traduire par des de meilleurs résultats que le débutant qui choisit de les affronter sans bonne raison. Parfois, ils codifient des règles menant simplement à des résultats inférieurs aux normes plutôt qu’aux échecs absolus qui accompagnent des choix incorrects lorsqu’on les enfreint.
Ainsi, les règles générales ne sont pas les propositions modales absolues qu’elles semblent être à la surface, mais plutôt un moyen simple de donner la règle avec un passe-partout standard, comme ce qui suit:
où, bien sûr, vous pouvez remplacer "jamais d'analyser la
ls
sortie" à la place de $ {RULE}. :)Oh oui! Qu'en est -il de la
ls
sortie d' analyse ?Eh bien, alors, vu tout cela ... je pense qu'il est assez clair que cette règle est bonne. Tout d'abord, il faut comprendre que la vraie règle est idiomatique, comme expliqué ci-dessus ...
Mais en plus, il n’est pas seulement nécessaire d’être très bon avec les scripts shell pour savoir s’il peut être cassé, dans certains cas. C'est aussi qu'il faut autant de talent pour dire que vous vous êtes trompé lorsque vous essayez de le casser en test! Et, je dis en toute confiance qu'une très grande majorité du public probable de tels articles (donnant des conseils tels que «Ne pas analyser la sortie de
ls
!») Ne peut pas faire ces choses , et ceux qui ont une telle compétence se rendront probablement compte que ils s'en rendent compte par eux-mêmes et ignorent la règle quand même.Mais ... regardez juste cette question, et comment même les personnes qui ont probablement les compétences ont pensé que c'était un mauvais appel à le faire; et combien d'efforts l'auteur de la question a-t-il dépensé pour atteindre le meilleur exemple actuel! Je vous garantis que sur un problème difficile, 99% des gens se tromperaient et avec des résultats potentiellement très mauvais! Même si la méthode choisie se révèle bonne; tant que cette
ls
idée d'analyse (ou une autre) ne sera pas adoptée par l'ensemble des informaticiens / développeurs, résistera à de nombreux tests (en particulier à l'épreuve du temps) et, finalement, parviendra à obtenir le statut de «technique commune», il est probable qu'un Beaucoup de gens pourraient essayer, et se tromper ... avec des conséquences désastreuses.Donc, je vais répéter une dernière fois ... que, en particulier dans ce cas , c’est pourquoi " ne jamais analyser la
ls
sortie!" est décidément la bonne façon de le formuler.[MISE À JOUR 2014-05-18: clarification du raisonnement de la réponse (ci-dessus) pour répondre à un commentaire de OP; l'ajout suivant est une réponse aux ajouts du PO à la question d'hier]
[MISE À JOUR 2014-11-10: ajout d'en-têtes et de contenu réorganisé / refactoré; et aussi: reformater, reformuler, clarifier, et euh ... "concis-ifying" ... ... je voulais que ce soit simplement un nettoyage, bien que cela se soit transformé en une retouche. Je l'avais laissée dans un état déplorable, j'ai donc surtout essayé de lui donner un ordre. j’ai pensé qu’il était important de laisser en grande partie la première section intacte; il n'y a donc que deux modifications mineures: redondant 'mais' supprimé et 'cela' souligné.]
† Au départ, je voulais utiliser ceci uniquement à titre de clarification de mon original; mais a décidé d'autres ajouts après réflexion
‡ voir https://unix.stackexchange.com/tour pour des instructions sur les publications
la source
ls
!" Un conseil correct: 1. Démontrez (à votre satisfaction) que chaque cas d'utilisation où l'on pourrait analyser unels
sortie dispose d'une autre solution disponible, supérieure d'une manière ou d'une autre, sans le faire. 2. montrer que, dans les cas cités, la déclaration n’est pas littérale.ls
est un utilitaire informatique - vous pouvez analyser la sortie de l'ordinateur.Est-il possible d'analyser la sortie de
ls
dans certains cas? Sûr. L'idée d'extraire une liste de numéros d'inodes à partir d' un répertoire est un bon exemple - si vous savez que votre de mise en œuvre desls
supports-q
, et donc chaque fichier produira exactement une ligne de sortie, et tout ce dont vous avez besoin sont les numéros d'inodes, les analyse surls -Rai1q
la sortie est certainement une solution possible. Bien sûr, si l'auteur n'avait pas vu un conseil du genre "Ne jamais analyser la sortie de ls" auparavant, il ne penserait probablement pas aux noms de fichiers contenant des nouvelles lignes, et laisserait probablement le "q" en conséquence, et le code serait subtilement cassé dans ce cas limite - ainsi, même dans les cas oùls
la sortie de l' analyse syntaxique est raisonnable, ce conseil est toujours utile.Le point le plus large est que, lorsqu'un débutant en scripts shell essaie de faire comprendre à un script quel est le fichier le plus volumineux d'un répertoire ou le fichier le plus récemment modifié dans un répertoire, son premier instinct est d'analyser
ls
le fichier . output - compréhensible, carls
est l’une des premières commandes apprises par les débutants.Malheureusement, cet instinct est faux et cette approche est rompue. Encore plus malheureusement, il est subtilement cassé - cela fonctionnera la plupart du temps, mais échouera dans les cas extrêmes qui pourraient peut-être être exploités par une personne connaissant le code.
Le débutant pourrait penser
ls -s | sort -n | tail -n 1 | awk '{print $2}'
à un moyen d’obtenir le plus gros fichier d’un répertoire. Et ça marche, jusqu’à ce que vous ayez un fichier avec un espace dans le nom.OK, alors
ls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//'
? Fonctionne bien jusqu'à ce que vous ayez un fichier avec une nouvelle ligne dans le nom.L'ajout
-q
d'ls
arguments à aide en cas de nouvelle ligne dans le nom du fichier. Cela peut ressembler à cela, jusqu'à ce que vous ayez 2 fichiers différents contenant un caractère non imprimable au même endroit du nom du fichier, et quels
la sortie ne vous permette pas de distinguer lequel de ceux-ci était le plus grand. Pire encore, pour élargir le "?", Il a probablement recours à celui de son shell,eval
ce qui posera problème s'il frappe un fichier nommé, par exemple:Est-ce que ça
--quoting-style=shell
aide (si votrels
soutien le soutient)? Non, affiche toujours? pour les caractères non imprimables, il est donc toujours ambigu de savoir lequel des multiples correspondances était le plus gros.--quoting-style=literal
? Non, pareil.--quoting-style=locale
ou--quoting-style=c
peut aider si vous avez juste besoin d'imprimer le nom du plus gros fichier sans ambiguïté, mais probablement pas si vous devez faire quelque chose avec le fichier par la suite - ce serait un tas de code pour annuler la citation et revenir au vrai nom de fichier afin que vous pouvez le transmettre à, par exemple, gzip.Et à la fin de tout ce travail, même si ce qu'il a est sûr et correct pour tous les noms de fichiers possibles, il est illisible et incontrôlable, et aurait pu être effectué beaucoup plus facilement, en toute sécurité et de manière lisible, en python, en perl ou en ruby.
Ou même en utilisant d’autres outils de coquillage, je pense que cela devrait suffire:
Et devrait être au moins aussi portable qu’il
--quoting-style
est.la source