désolé si c'est une question noobie mais je ne trouve pas de bonne réponse.
Pour trouver puis supprimer quelque chose que je peux utiliser
find . -name ".txt" -exec rm "{}" \;
Mais pourquoi ne puis-je pas simplement diriger les résultats vers rm comme
find . -name ".txt" | rm
comme je le ferais pour grep
find . -name ".txt" | grep a
J'ai lu quelque part que rm ne prend pas d'entrée de stdin et donc je ne peux pas le diriger mais qu'est-ce que cela signifie? Lorsque je tape rm a.txt, il lit à partir de l'entrée standard comme je peux le faire, non? Ou y a-t-il une différence entre stdin et la ligne de commande. Aidez-moi!
Pour développer la réponse de @Alex Gitelman: oui, il y a une différence entre "l'entrée standard" et la ligne de commande.
Lorsque vous tapez rm a.txt b.txt c.txt
, les fichiers que vous listez après rm
sont appelés arguments et sont mis à la disposition de rm via une variable spéciale (appelée argv
en interne). L'entrée standard, d'autre part, ressemble à un programme Unix comme un fichier nommé stdin
. Un programme peut lire les données de ce "fichier" comme s'il le faisait s'il ouvrait un fichier normal sur le disque et le lire.
rm
, comme beaucoup d'autres programmes, prend ses arguments depuis la ligne de commande mais ignore l'entrée standard. Vous pouvez y ajouter tout ce que vous voulez; il va juste jeter ces données. C'est là que xargs
est utile. Il lit les lignes sur l'entrée standard et les transforme en arguments de ligne de commande, afin que vous puissiez efficacement diriger les données vers la ligne de commande d'un autre programme. C'est une astuce intéressante.
Par exemple:
find . -name ".txt" | xargs rm
find . -name ".txt" | grep "foo" | xargs rm
Notez que cela ne fonctionnera pas correctement s'il existe des noms de fichiers contenant des sauts de ligne ou des espaces. Pour traiter les noms de fichiers contenant des sauts de ligne ou des espaces, vous devez utiliser à la place:
find . -name ".txt" -print0 | xargs -0 rm
Cela indiquera à find
de terminer les résultats avec un caractère nul au lieu d'une nouvelle ligne. Cependant, grep
ne fonctionnera pas comme avant. Utilisez plutôt ceci:
find . -name ".txt" | grep "foo" | tr "\n" "\0" | xargs -0 rm
Cette fois, tr
est utilisé pour convertir toutes les nouvelles lignes en caractères nuls.
"pourquoi ne puis-je pas trouver le résultat vers rm?"
Lorsque vous dirigez quelque chose vers un programme, le canal remplace l'entrée clavier. Gardez cela à l'esprit et posez-vous la question suivante: Que ferait rm
avec un clavier? Supprimer vos frappes? (un peu idiot en effet) Accepter le contrôle interactif? (rm
n'est pas interactif sauf parfois quand il a besoin d'une confirmation, qui peut en effet être donnée par un pipe.)
En fait, lorsque rm
est déjà en cours d'exécution, vous ne pouvez pas taper de commandes pour lui permettre de supprimer des fichiers .... vous ne pouvez donc pas le faire avec un canal non plus.
Si vous gardez à l'esprit qu'un tuyau remplace la combinaison clavier/écran, les choses apparaîtront immédiatement plus logiques.
Maintenant l'inverse. Vous pouvez diriger un flux de données dans grep
. Cela signifie-t-il que vous pouvez laisser grep lire les séquences de touches de votre clavier en tant que données d'entrée?
OUI! C'est en fait ce qu'il fait nativement (sans tuyauterie).
(b.t.w. notez que vous ne pouvez pas diriger ni taper l'argument de recherche dans grep
)
Alors maintenant, vous savez pourquoi vous ne pouvez pas diriger vers rm et vous attendre pour fonctionner comme un argument de ligne de commande.
tl; dr:
Anatomie d'un programme selon la philosophie UNIX:
entrée de fichier, sortie de fichier, entrée de clavier, sortie d'écran. -> Le tuyau ne remplace que le clavier et l'écran.
Le tuyau envoie la sortie de la première commande à l'entrée standard de la seconde. rm
n'accepte pas l'entrée standard, vous ne pouvez donc pas y accéder. Vous pouvez utiliser xargs
pour obtenir le même effet. Vous pouvez trouver un exemple pour xargs
spécifiquement pour votre cas dans page de manuel pour xargs .
Une alternative sans utiliser de tuyaux:
xargs rm -f <<< $(find . -name ".txt")