Notez que les fichiers peuvent également avoir des retours à la ligne dans leur nom de fichier. Voilà pourquoi il y a find -print0et xargs -0.
Daniel Beck
Réponses:
12
Idéalement, vous ne le faites pas du tout, car analyser correctement les noms de fichiers dans un script shell est toujours difficile (corrigez-le pour les espaces, vous aurez toujours des problèmes avec d'autres caractères incorporés, en particulier les sauts de ligne). Ceci est même répertorié comme la première entrée de la page BashPitfalls.
Cela dit, il existe un moyen de faire presque ce que vous voulez:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
N'oubliez pas de citer également $ilors de son utilisation, afin d'éviter d'autres choses d'interprétation ultérieure des espaces. N'oubliez pas également de $IFSreculer après l'avoir utilisé, car ne pas le faire entraînera des erreurs déroutantes plus tard.
Cela a une autre mise en garde attachée: ce qui se passe à l'intérieur de la whileboucle peut avoir lieu dans un sous-shell, selon le shell exact que vous utilisez, donc les paramètres variables peuvent ne pas persister. La forversion en boucle évite cela, mais au prix que, même si vous appliquez la $IFSsolution pour éviter les problèmes d'espace, vous aurez alors des ennuis si le findretourne trop de fichiers.
À un moment donné, le correctif correct pour tout cela devient de le faire dans un langage tel que Perl ou Python au lieu de shell.
J'aime l'idée d'utiliser simplement Python pour éviter tout cela.
Scott C Wilson
12
Utilisez-le find -print0et dirigez-le vers xargs -0, ou écrivez votre propre petit programme C et dirigez-le vers votre petit programme C. C'est pour cela -print0et ils -0ont été inventés.
Les scripts shell ne sont pas le meilleur moyen de gérer les noms de fichiers avec des espaces: vous pouvez le faire, mais cela devient maladroit.
Vous pouvez définir le "séparateur de champ interne" ( IFS) sur autre chose que de l'espace pour la division des arguments de boucle, par exemple
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Je réinitialise IFSaprès son utilisation dans la recherche, principalement parce qu'il a l'air sympa, je pense. Je n'ai vu aucun problème à le régler sur newline, mais je pense que c'est "plus propre".
Une autre méthode, en fonction de ce que vous voulez faire avec la sortie find, consiste soit à l'utiliser directement -execavec la findcommande, soit à l'utiliser -print0et à la diriger vers xargs -0. Dans le premier cas, findle nom de fichier s'échappe. Dans le -print0cas, findimprime sa sortie avec un séparateur nul, puis xargsse divise sur cela. Puisqu'aucun nom de fichier ne peut contenir ce caractère (ce que je sais), c'est toujours aussi sûr. Cela est surtout utile dans les cas simples; et n'est généralement pas un excellent substitut pour une forboucle complète .
L'utilisation find -print0combinée avec xargs -0est complètement robuste contre les noms de fichiers légaux et est l'une des méthodes les plus extensibles disponibles. Par exemple, supposons que vous vouliez une liste de tous les fichiers PDF du répertoire actuel. Tu pourrais écrire
Ceci trouvera chaque PDF (via -iname '*.pdf') dans le répertoire courant ( .) et n'importe quel sous-répertoire, et passera chacun d'eux en argument à la echocommande. Parce que nous avons spécifié l' -n 1option, xargsne passera qu'un seul argument à la fois à echo. Si nous avions omis cette option, nous en xargsaurions transmis autant que possible echo. (Vous pouvez echo short input | xargs --show-limitsvoir combien d'octets sont autorisés dans une ligne de commande.)
Que fait xargsexactement?
Nous pouvons clairement voir l'effet xargsa sur son entrée - et l'effet de -nen particulier - en utilisant un script qui fait écho à ses arguments d'une manière plus précise que echo.
Je ne suis pas d'accord avec les bashbashers, car bash, avec l'ensemble d'outils * nix, est tout à fait apte à gérer les fichiers (y compris ceux dont les noms ont des espaces intégrés).
En fait, findvous donne un contrôle fin sur le choix des fichiers à traiter ... Du côté bash, vous n'avez vraiment besoin de réaliser que vous devez faire de vos chaînes bash words; généralement en utilisant des "guillemets doubles", ou un autre mécanisme comme l'utilisation d'IFS, ou de trouver{}
Notez que dans la plupart des situations, vous n'avez pas besoin de définir et de réinitialiser IFS; utilisez simplement IFS localement comme indiqué dans les exemples ci-dessous. Les trois poignées permettent de bien espacer les espaces. De plus, vous n'avez pas besoin d'une structure de boucle "standard", car la recherche \;est en fait une boucle; mettez simplement votre logique de boucle dans une fonction bash (si vous n'appelez pas un outil standard).
Les deux perspectives sont valables. Lorsque je ne travaillais que sur mes propres fichiers, j'utilisais simplement find et je ne m'en fais pas, car mes fichiers n'ont pas d'espaces (ou de retours chariot!) Dans leurs noms. Mais lorsque vous commencez à travailler avec des fichiers d'autres personnes, vous devez utiliser des techniques plus robustes.
find -print0
etxargs -0
.Réponses:
Idéalement, vous ne le faites pas du tout, car analyser correctement les noms de fichiers dans un script shell est toujours difficile (corrigez-le pour les espaces, vous aurez toujours des problèmes avec d'autres caractères incorporés, en particulier les sauts de ligne). Ceci est même répertorié comme la première entrée de la page BashPitfalls.
Cela dit, il existe un moyen de faire presque ce que vous voulez:
N'oubliez pas de citer également
$i
lors de son utilisation, afin d'éviter d'autres choses d'interprétation ultérieure des espaces. N'oubliez pas également de$IFS
reculer après l'avoir utilisé, car ne pas le faire entraînera des erreurs déroutantes plus tard.Cela a une autre mise en garde attachée: ce qui se passe à l'intérieur de la
while
boucle peut avoir lieu dans un sous-shell, selon le shell exact que vous utilisez, donc les paramètres variables peuvent ne pas persister. Lafor
version en boucle évite cela, mais au prix que, même si vous appliquez la$IFS
solution pour éviter les problèmes d'espace, vous aurez alors des ennuis si lefind
retourne trop de fichiers.À un moment donné, le correctif correct pour tout cela devient de le faire dans un langage tel que Perl ou Python au lieu de shell.
la source
Utilisez-le
find -print0
et dirigez-le versxargs -0
, ou écrivez votre propre petit programme C et dirigez-le vers votre petit programme C. C'est pour cela-print0
et ils-0
ont été inventés.Les scripts shell ne sont pas le meilleur moyen de gérer les noms de fichiers avec des espaces: vous pouvez le faire, mais cela devient maladroit.
la source
Vous pouvez définir le "séparateur de champ interne" (
IFS
) sur autre chose que de l'espace pour la division des arguments de boucle, par exempleJe réinitialise
IFS
après son utilisation dans la recherche, principalement parce qu'il a l'air sympa, je pense. Je n'ai vu aucun problème à le régler sur newline, mais je pense que c'est "plus propre".Une autre méthode, en fonction de ce que vous voulez faire avec la sortie
find
, consiste soit à l'utiliser directement-exec
avec lafind
commande, soit à l'utiliser-print0
et à la diriger versxargs -0
. Dans le premier cas,find
le nom de fichier s'échappe. Dans le-print0
cas,find
imprime sa sortie avec un séparateur nul, puisxargs
se divise sur cela. Puisqu'aucun nom de fichier ne peut contenir ce caractère (ce que je sais), c'est toujours aussi sûr. Cela est surtout utile dans les cas simples; et n'est généralement pas un excellent substitut pour unefor
boucle complète .la source
Utilisation
find -print0
avecxargs -0
L'utilisation
find -print0
combinée avecxargs -0
est complètement robuste contre les noms de fichiers légaux et est l'une des méthodes les plus extensibles disponibles. Par exemple, supposons que vous vouliez une liste de tous les fichiers PDF du répertoire actuel. Tu pourrais écrireCeci trouvera chaque PDF (via
-iname '*.pdf'
) dans le répertoire courant (.
) et n'importe quel sous-répertoire, et passera chacun d'eux en argument à laecho
commande. Parce que nous avons spécifié l'-n 1
option,xargs
ne passera qu'un seul argument à la fois àecho
. Si nous avions omis cette option, nous enxargs
aurions transmis autant que possibleecho
. (Vous pouvezecho short input | xargs --show-limits
voir combien d'octets sont autorisés dans une ligne de commande.)Que fait
xargs
exactement?Nous pouvons clairement voir l'effet
xargs
a sur son entrée - et l'effet de-n
en particulier - en utilisant un script qui fait écho à ses arguments d'une manière plus précise queecho
.Notez qu'il gère parfaitement les espaces et les nouvelles lignes,
ce qui serait particulièrement gênant avec la solution commune suivante:
Remarquesla source
Je ne suis pas d'accord avec les
bash
bashers, carbash
, avec l'ensemble d'outils * nix, est tout à fait apte à gérer les fichiers (y compris ceux dont les noms ont des espaces intégrés).En fait,
find
vous donne un contrôle fin sur le choix des fichiers à traiter ... Du côté bash, vous n'avez vraiment besoin de réaliser que vous devez faire de vos chaînesbash words
; généralement en utilisant des "guillemets doubles", ou un autre mécanisme comme l'utilisation d'IFS, ou de trouver{}
Notez que dans la plupart des situations, vous n'avez pas besoin de définir et de réinitialiser IFS; utilisez simplement IFS localement comme indiqué dans les exemples ci-dessous. Les trois poignées permettent de bien espacer les espaces. De plus, vous n'avez pas besoin d'une structure de boucle "standard", car la recherche
\;
est en fait une boucle; mettez simplement votre logique de boucle dans une fonction bash (si vous n'appelez pas un outil standard).Et, deux autres exemples
'trouver
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)la source