Je suis constamment à la recherche de la syntaxe de

find . -name "FILENAME" -exec rm {} \; 

principalement parce que je ne vois pas comment fonctionne exactement la partie -exec. Quelle est la signification des accolades, de la barre oblique inverse et du point-virgule? Existe-t-il dautres cas dutilisation pour cette syntaxe?

Commentaires

  • @Philippos: Je vois votre point. Veuillez garder à lesprit que les pages de manuel sont une référence, cest-à-dire utiles pour celles-ci avec une compréhension de la question pour rechercher la syntaxe. Pour quelquun qui découvre le sujet, ils sont souvent trop énigmatiques et formels pour être utiles. Vous constaterez que la réponse acceptée est environ 10 fois plus longue que lentrée de la page de manuel, et ' pour une raison.
  • Même lancienne page POSIX man lit Un nom_utilitaire ou un argument contenant uniquement les deux caractères " {} " doivent être remplacés d par le chemin daccès actuel , ce qui me semble suffisant. De plus, il a un exemple avec -exec rm {} \;, comme dans votre question. À mon époque, il n’y avait guère d’autres ressources que le " grand mur gris ", des livres de pages (le papier était plus agréable que le stockage). Je sais donc que cela suffit pour quelquun de nouveau sur le sujet. Votre dernière question est cependant juste à poser ici. Malheureusement, ni @Kusalananda ni moi-même navons de réponse à cela.
  • @Philippos 🙂 Oh, ça serait une chose excitante à essayer, pour un niveau de " excitant " qui est hors de léchelle de mon graphique.
  • Comeon @Philippos. Êtes-vous vraiment en train de dire à Kusalananda quil na pas amélioré la page de manuel? 🙂
  • @ZsoltSzilagy Je nai ni dit cela ni voulu dire cela. Il vous a très bien nourri, je pense juste que vous êtes assez vieux pour manger seul. (-;

Réponse

Cette réponse se décline dans les parties suivantes:

  • Utilisation de base de -exec
  • Utilisation de -exec en combinaison avec sh -c
  • Utilisation de -exec ... {} +
  • Utilisation de -execdir

Utilisation basique de -exec

Loption -exec prend un utilitaire externe avec des arguments facultatifs comme son argument et lexécute.

Si la chaîne {} est présente nimporte où dans la commande donnée, chaque instance de celle-ci sera remplacée par le chemin en cours de traitement ( par exemple ./some/path/FILENAME). Dans la plupart des shells, les deux caractères {} nont pas besoin dêtre entre guillemets.

La commande doit être terminé par un ; pour find pour savoir où il se termine (car il peut y avoir dautres options par la suite s). Pour protéger le ; du shell, il doit être cité comme \; ou ";" , sinon le shell le verra comme la fin de la commande find.

Exemple (le \ au la fin des deux premières lignes est juste pour les continuations de ligne):

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";" 

Ceci trouvera tous les fichiers normaux (-type f) dont les noms correspondent au modèle *.txt dans ou sous le répertoire courant. Il testera ensuite si la chaîne hello apparaît dans lun des fichiers trouvés en utilisant grep -q (qui ne produit aucune sortie, juste une sortie statut). Pour les fichiers contenant la chaîne, cat sera exécuté pour afficher le contenu du fichier vers le terminal.

Chaque -exec agit également comme un « test » sur les chemins trouvés par find, tout comme -type et -name fait. Si la commande renvoie un statut de sortie nul (signifiant « succès »), la partie suivante de la commande find est prise en compte, sinon la find La commande continue avec le chemin suivant. Ceci est utilisé dans lexemple ci-dessus pour rechercher les fichiers contenant la chaîne hello, mais pour ignorer tous les autres fichiers.

Lexemple ci-dessus illustre les deux utilisations les plus courantes cas de -exec:

  1. Comme test pour restreindre davantage la recherche.
  2. Pour effectuer une sorte daction sur le trouvé chemin daccès (généralement, mais pas nécessairement, à la fin de la commande find).

Utilisation de -exec en combinaison avec sh -c

La commande que -exec peut exécuter est limitée à un utilitaire externe avec des arguments optionnels.Utiliser directement les fonctions intégrées du shell, les fonctions, les conditions, les pipelines, les redirections, etc. avec -exec, à moins dêtre enveloppé dans quelque chose comme un sh -c shell enfant.

Si des fonctionnalités bash sont requises, utilisez bash -c à la place de sh -c.

sh -c exécute /bin/sh avec un script donné sur la ligne de commande, suivi darguments de ligne de commande facultatifs pour ce script.

Un exemple simple dutilisation de sh -c seul, sans find :

sh -c "echo "You gave me $1, thanks!"" sh "apples" 

Ceci passe deux arguments au script shell enfant. Ceux-ci seront placés dans $0 et $1 pour le script à utiliser.

  1. Le chaîne sh. Ce sera disponible sous la forme $0 dans le script, et si le shell interne génère un message derreur, il le préfixera avec cette chaîne.

  2. Largument apples est disponible en tant que $1 dans le script, et sil y avait eu plus darguments, ils auraient été disponibles comme $2, $3 etc. Ils seraient également disponibles dans la liste "$@" (sauf pour $0 qui ne ferait pas partie de "$@").

Ceci est utile en combinaison avec -exec car il nous permet de créer des scripts arbitrairement complexes qui agissent sur les chemins trouvés par find.

Exemple: Trouvez tous les fichiers normaux qui ont un certain suffixe de nom de fichier, et changez ce suffixe de nom de fichier en un autre suffixe, où les suffixes sont conservés dans des variables:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c "mv "$3" "${3%.$1}.$2"" sh "$from" "$to" {} ";" 

À lintérieur de lentier rnal script, $1 serait la chaîne text, $2 serait la chaîne txt et $3 serait le chemin que find a trouvé pour nous. Lexpansion du paramètre ${3%.$1} prendrait le chemin et en supprimerait le suffixe .text.

Ou, en utilisant dirname / basename:

find . -type f -name "*.$from" -exec sh -c " mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"" sh "$from" "$to" {} ";" 

ou, avec des variables ajoutées dans le script interne:

find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2; pathname=$3 mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"" sh "$from" "$to" {} ";" 

Notez que dans cette dernière variante, les variables from et to dans le shell enfant sont distincts des variables portant les mêmes noms dans le script externe.

Ce qui précède est la manière correcte dappeler un script complexe arbitraire à partir de -exec avec find. Utiliser find dans une boucle comme

for pathname in $( find ... ); do 

est sujet aux erreurs et inélégant (opinion personnelle). Il divise les noms de fichiers sur des espaces, invoque le globbing des noms de fichiers et force également le shell à étendre le résultat complet de find avant même dexécuter la première itération de la boucle.

Voir aussi:


Utiliser -exec ... {} +

Le ; à la fin peut être remplacé par +. Cela amène find à exécuter la commande donnée avec autant darguments (chemins trouvés) que possible plutôt quune fois pour chaque chemin trouvé. La chaîne {} doit apparaître juste avant le + pour que cela fonctionne .

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} + 

Ici, find collectera les chemins daccès résultants et exécutera cat sur autant dentre eux que possible à la fois.

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec mv -t /tmp/files_with_hello/ {} + 

De même ici, mv sera exécuté comme quelques fois que possible. Ce dernier exemple nécessite GNU mv de coreutils (qui prend en charge loption -t).

Utilisation de -exec sh -c ... {} + est également un moyen efficace de boucler sur un ensemble de chemins avec un script arbitrairement complexe.

Les bases sont les mêmes que lors de lutilisation de -exec sh -c ... {} ";", mais le script prend maintenant une liste darguments beaucoup plus longue. Ceux-ci peuvent être bouclés en boucle sur "$@" à lintérieur du script.

Notre exemple de la dernière section qui change les suffixes de nom de fichier:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2 shift 2 # remove the first two arguments from the list # because in this case these are *not* pathnames # given to us by find for pathname do # or: for pathname in "$@"; do mv "$pathname" "${pathname%.$from}.$to" done" sh "$from" "$to" {} + 

Utilisation de -execdir

Il y a aussi -execdir ( implémentée par la plupart des variantes find, mais pas une option standard).

Cela fonctionne comme -exec à la différence que la commande shell donnée est exécutée avec le répertoire du chemin trouvé comme répertoire de travail actuel et que {} contiendra le nom de base du chemin trouvé sans son chemin (mais GNU find préfixera toujours le nom de base avec ./, alors que BSD find ne le fera pas).

Exemple:

find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \; 

Cela déplacera chaque *.txt -fichier trouvé vers un sous-répertoire done-texts dans le même répertoire que celui où se trouvait le fichier trouvé . Le fichier sera également renommé en y ajoutant le suffixe .done.

Ce serait un peu plus délicat à faire avec -exec car nous devrions obtenir le nom de base du fichier trouvé à partir de {} pour former le nouveau nom du fichier. Nous avons également besoin du nom de répertoire de {} pour localiser correctement le répertoire done-texts.

Avec -execdir, certaines choses comme celles-ci deviennent plus faciles.

Lopération correspondante en utilisant -exec au lieu de -execdir devrait utiliser un shell enfant:

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done" done" sh {} + 

ou,

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "${name%/*}/done-texts/${name##*/}.done" done" sh {} + 

Commentaires

  • -exec prend un programme et des arguments et lexécute; certaines commandes shell consistent uniquement en un programme et des arguments, mais beaucoup ne le sont pas. Une commande shell peut inclure la redirection et la tuyauterie; -exec ne peut pas (bien que lensemble de find puisse être redirigé). Une commande shell peut utiliser ; && if etc; -exec ne peut pas, bien que -a -o puisse en faire. Une commande shell peut être un alias ou une fonction shell, ou intégrée; -exec ne peut pas. Une commande shell peut développer vars; -exec ne peut pas (bien que le shell externe qui exécute le find le puisse). Une commande shell peut remplacer $(command) différemment à chaque fois; -exec ne peut pas. …
  • Le dire ' sa commande shell est incorrect ici, find -exec cmd arg \; ne ' t invoquer un shell pour interpréter une ligne de commande shell, il exécute execlp("cmd", "arg") directement, pas execlp("sh", "-c", "cmd arg") (pour lequel le shell finirait par faire léquivalent de execlp("cmd", "arg") si cmd nétait pas intégré).
  • Vous pourriez clarifier que tout les arguments find après -exec et jusquà ; ou + composent la commande à exécuter avec ses arguments, chaque instance dun argument {} remplacée par le fichier courant (par ;), et {} comme dernier argument avant + remplacé par une liste de fichiers comme arguments séparés (dans le {} + cas). IOW -exec prend plusieurs arguments, terminés par un ; ou {} +.
  • @Kusalananda wouldn ' t votre dernier exemple fonctionne également avec cette commande plus simple: find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'?
  • @Atralb Oui, cela aurait également fonctionné et aurait eu le même effet que le dernier morceau de code, mais au lieu dexécuter mv dans une boucle, une fois par fichier trouvé, vous exécutez à la fois sh et mv pour chaque fichier trouvé, qui sera sensiblement plus lent pour de grandes quantités de fichiers.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *