Comment 'find -exec' passe-t-il les noms de fichiers avec des espaces?

14

Si j'ai un répertoire contenant des fichiers dont les noms ont des espaces, par exemple

$ ls -1 dir1
file 1
file 2
file 3

Je peux tous les copier avec succès dans un autre répertoire comme celui-ci:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Cependant, la sortie de find dir1 -mindepth 1contient des espaces non échappés:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Si j'utilise à la print0place de print, la sortie contient toujours des espaces non échappés:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Pour copier ces fichiers manuellement en utilisant cp, j'aurais besoin d'échapper aux espaces; mais il semble que ce ne soit pas nécessaire quand cples aguments proviennent find, que j'utilise +ou \;à la fin de la commande.

Quelle en est la raison?

EmmaV
la source

Réponses:

8

La findcommande exécute la commande directement. La commande, y compris l'argument du nom de fichier, ne sera pas traitée par le shell ou tout autre élément susceptible de modifier le nom de fichier. C'est très sûr.

Vous avez raison, il n'est pas nécessaire d'échapper les noms de fichiers qui sont représentés par {}sur la findligne de commande.

findpasse le nom de fichier brut du disque directement dans la liste des arguments internes de la -execcommande, dans votre cas, la cpcommande.

RobertL
la source
1
En un mot, find..execpeut gérer des noms de fichiers étranges par lui-même ..
heemayl
2
La première règle du club Linux est que vous
n'analysez
5

La question est en deux parties:

  • comment ne find gérer les programmes d'appel à l' aide -execsans se heurter à des problèmes avec des espaces intégrés dans les noms de fichiers, et
  • à quoi sert l' -print0option?

Pour le premier, findeffectue un appel système, en fait l'un d'un groupe d'appels connexes appelé "exec" . Il transmet le nom de fichier en tant qu'argument directement à cet appel, qui est ensuite transmis directement (après la création d'un nouveau processus) sans perdre d'informations sur le nom de fichier.

La findfonction POSIX +est expliquée comme suit, dans la justification :

Une caractéristique de l' findutilitaire SVR4 était le -execterminateur + du primaire. Cela a permis aux noms de fichiers contenant des caractères spéciaux (en particulier les caractères de nouvelle ligne ) d'être regroupés sans les problèmes qui se produisent si ces noms de fichiers sont redirigés vers xargs. D'autres implémentations ont ajouté d'autres façons de contourner ce problème, notamment un -print0principal qui a écrit des noms de fichiers avec un terminateur d'octets nuls. Cela a été examiné ici, mais pas adopté. L'utilisation d'un terminateur nul signifiait que tout utilitaire qui allait traiter la -print0sortie de find devait ajouter une nouvelle option pour analyser les terminateurs nuls qu'il serait en train de lire.

Que " notamment un -print0primaire" se réfère à GNU findet xargsqui résout le problème d'une manière différente. Il est également supporté par FreeBSD findet xargs. Si vous avez ajouté une -0option (voir la page de manuel ) à l' xargsappel, alors ce programme accepte les lignes terminées par des caractères "null byte". À son tour, xargsappelle les fonctions exec pour faire son travail. La principale distinction entre la fonctionnalité -print0et -0et la +fonctionnalité est que la première transmet les noms de fichiers sur un tuyau, tandis que la seconde ne le fait pas. Les développeurs trouvent des utilisations pour presque toutes les fonctionnalités; les tuyaux ne font pas exception.

Revenons à l'exemple de OP, qui utilise une -toption pour cp: qui ne se trouve pas dans POSIX cp . Il s'agit plutôt d'une extension (alias "fonctionnalité non standard") fournie par GNU cp . L' -0extension de xargsn'améliorerait pas cet exemple, mais il y a d'autres cas où il peut être utilisé efficacement - en gardant à l'esprit qu'il existe une alternative portable +, que GNU findaccepte.

Thomas Dickey
la source
-1

( Cela devrait être un commentaire mais il est trop grand. )

Pour ceux qui aiment essayer les choses:

Créez un script répertoriant les paramètres positionnels transmis, appelez-le list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Exécutez- findle sur un répertoire $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Comme prévu, il n'y a qu'un seul paramètre dans tous les appels, le nom de fichier, qu'il y ait ou non des espaces dans son nom.

David Tonhofer
la source
1
Vous pouvez également utiliser printfcomme printf '"%s"\n' "$@"pour imprimer tous les arguments positionnels cités, pour une inspection visuelle.
Kusalananda