$ ls *mp3 | xargs mplayer
Playing Lemon.
File not found: 'Lemon'
Playing Tree.mp3.
File not found: 'Tree.mp3'
Exiting... (End of file)
Ma commande échoue car le fichier "Lemon Tree.mp3" contient des espaces et xargs pense donc qu'il s'agit de deux fichiers. Puis-je faire en sorte que find + xargs fonctionne avec des noms de fichiers comme celui-ci?
La commande xargs
prend des espaces (caractères de tabulation, espaces, nouvelles lignes) comme séparateurs. Vous ne pouvez le restreindre qu'aux caractères de la nouvelle ligne ('\ n') avec l'option -d
comme ceci:
ls *.mp3 | xargs -d '\n' mplayer
Cela ne fonctionne qu'avec GNU xargs. Pour les systèmes BSD, utilisez l’option -0
comme ceci:
ls *.mp3 | xargs -0 mplayer
Cette méthode est plus simple et fonctionne également avec le GNU xargs.
L'utilitaire xargs lit les chaînes délimitées par des espaces, des tabulations, des nouvelles lignes et des fins de fichier à partir de l'entrée standard et exécute l'utilitaire avec les chaînes comme arguments.
Vous voulez éviter d'utiliser l'espace comme délimiteur. Cela peut être fait en modifiant le délimiteur pour xargs. Selon le manuel:
-0 Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines. This is expected to be used in concert with the -print0 function in find(1).
Tel que:
find . -name "*.mp3" -print0 | xargs -0 mplayer
Pour répondre à la question sur la lecture du septième mp3; c'est plus simple de courir
mplayer "$(ls *.mp3 | sed -n 7p)"
Essayer
find . -name \*.mp3 -print0 | xargs -0 mplayer
au lieu de
ls | grep mp3
xargs sur MacOS n'a pas l'option -d, cette solution utilise donc -0 à la place.
Obtenez ls pour générer un fichier par ligne, puis traduisez les nouvelles lignes en NULL et indiquez à xargs d'utiliser NULL comme délimiteur:
ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer
La réponse de Dick.Guertin [1] a suggéré que l'on pourrait échapper aux espaces d'un nom de fichier est une alternative valable aux autres solutions suggérées ici (telle que l'utilisation d'un caractère nul comme séparateur plutôt que comme un espace). Mais cela pourrait être plus simple: vous n'avez pas vraiment besoin d'un personnage unique. Vous pouvez simplement avoir sed ajouter les espaces échappés directement:
ls | grep ' ' | sed 's| |\\ |g' | xargs ...
De plus, le grep n’est nécessaire que si vous seulement voulez des fichiers avec des espaces dans les noms. De manière plus générique (par exemple, lors du traitement d'un lot de fichiers dont certains comportent des espaces, d'autres non), il suffit de sauter le grep:
ls | sed 's| |\\ |g' | xargs ...
Ensuite, bien sûr, le nom de fichier peut avoir d'autres espaces que des espaces (par exemple, un onglet):
ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...
Cela suppose que vous avez un sed qui supporte -r (regex étendu) tel que GNU sed ou des versions récentes de bsd sed (par exemple, FreeBSD qui épelait à l'origine l'option "-E" avant FreeBSD 8 et supporte les deux - r & -E pour la compatibilité via FreeBSD 11 au moins). Sinon, vous pouvez utiliser une expression de crochet de classe de caractères regex de base et saisir manuellement les espaces et les caractères de tabulation dans les délimiteurs []
.
[1] C'est peut-être plus approprié en tant que commentaire ou modification à cette réponse, mais pour le moment je n'ai pas assez de réputation pour commenter et ne peux que suggérer des modifications. Puisque les dernières formes ci-dessus (sans le grep) modifient le comportement de la réponse originale de Dick.Guertin, une modification directe n'est peut-être pas appropriée de toute façon.
find . -name 'Lemon*.mp3' -print0 | xargs -0 -i mplayer '{}'
Cela a aidé dans mon cas à supprimer différents fichiers avec des espaces. Cela devrait fonctionner aussi avec mplayer. L'astuce nécessaire est les citations. (Testé sous Linux Xubunt 14.04.)
ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}
Notez que dans la commande ci-dessus, xargs
appellera mplayer
à nouveau pour chaque fichier. Cela peut être indésirable pour mplayer
, mais peut convenir pour d'autres cibles.
Cela dépend (a) de votre degré d'attachement au numéro 7 par opposition à, par exemple, Lemons, et (b) si l'un de vos noms de fichier contient des nouvelles lignes (et si vous êtes prêt à les renommer s'ils le font).
Il y a beaucoup de façons de gérer cela, mais certaines d'entre elles sont:
mplayer Lemon*.mp3
find . -name 'Lemon*.mp3' -exec mplayer {} ';'
i=0
for mp3 in *.mp3
do
i=$((i+1))
[ $i = 7 ] && mplayer "$mp3"
done
for mp3 in *.mp3
do
case "$mp3" in
(Lemon*) mplayer "$mp3";;
esac
done
i=0
find . -name *.mp3 |
while read mp3
do
i=$((i+1))
[ $i = 7 ] && mplayer "$mp3"
done
La boucle read
ne fonctionne pas si les noms de fichiers contiennent des nouvelles lignes; les autres fonctionnent correctement même avec des nouvelles lignes dans les noms (sans parler des espaces). Pour mon argent, si vous avez des noms de fichiers contenant une nouvelle ligne, vous devez renommer le fichier sans la nouvelle ligne. L'utilisation des guillemets autour du nom de fichier est essentielle au bon fonctionnement des boucles.
Si vous avez GNU find
et GNU xargs
(ou FreeBSD (* BSD?) Ou Mac OS X), vous pouvez également utiliser le fichier -print0
et -0
options, comme dans:
find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer
Cela fonctionne quel que soit le contenu du nom (les deux seuls caractères qui ne peuvent pas apparaître dans un nom de fichier sont slash et NUL, et la slash ne pose aucun problème dans le chemin du fichier, utilisez donc NUL comme délimiteur de nom). Cependant, si vous devez filtrer les 6 premières entrées, vous avez besoin d'un programme qui gère les lignes terminées par NUL au lieu de newline ... et je ne suis pas sûr qu'il y en ait.
Le premier est de loin le plus simple pour un cas spécifique; Cependant, il se peut que la généralisation ne couvre pas les autres scénarios que vous n'avez pas encore répertoriés.
Sur macOS 10.12.x (Sierra), si vous avez des espaces dans les noms de fichiers ou les sous-répertoires, vous pouvez utiliser les éléments suivants:
find . -name '*.Swift' -exec echo '"{}"' \; |xargs wc -l
Je sais que je ne réponds pas directement à la question xargs
mais cela vaut la peine de mentionner l'option find
de -exec
.
Étant donné le système de fichiers suivant:
[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush
0 directories, 4 files
La commande find peut être utilisée pour gérer l'espace dans Dream Theatre et King's X. Pour trouver les batteurs de chaque groupe à l'aide de grep:
[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
Dans le -exec
l'option {}
signifie le nom du fichier, chemin compris. Notez que vous ne devez pas y échapper ou le mettre entre guillemets.
La différence entre les terminateurs de -exec
(+
et \;
) réside dans le fait que +
regroupe autant de noms de fichiers que possible sur une seule ligne de commande. Alors que \;
exécutera la commande pour chaque nom de fichier.
Donc, find bands/ -type f -exec grep Drums {} +
aura pour résultat:
grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"
et find bands/ -type f -exec grep Drums {} \;
entraînera:
grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"
Dans le cas de grep
, ceci a pour effet d’imprimer le nom de fichier ou non.
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren
[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren
Bien sûr, les options de grep
-h
et -H
contrôlent si le nom de fichier est imprimé ou non, quel que soit le mode d'appel de grep
.
xargs
xargs
peut également contrôler la manière dont les fichiers man sont en ligne de commande.
xargs
regroupe par défaut tous les arguments sur une seule ligne. Afin de faire la même chose que -exec \;
utilise xargs -l
. Notez que l'option -t
indique à xargs
d'imprimer la commande avant de l'exécuter.
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater
Drums:Mike Mangini
grep Drums ./bands/Rush
Drums: Neil Peart
grep Drums ./bands/King's X
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth
Drums:Dirk Verbeuren
Vérifiez que l'option -l
indique à xargs d'exécuter grep pour chaque nom de fichier.
Par rapport à la valeur par défaut (c.-à-d. Aucune option -l
]):
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
xargs
contrôle mieux le nombre de fichiers pouvant être en ligne de commande. Donnez à l'option -l
le nombre maximal de fichiers par commande.
[root@localhost bokeh]# find ./bands -type f | xargs -d '\n' -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]#
Voir que grep
a été exécuté avec deux noms de fichier à cause de -l2
.
Étant donné le titre spécifique de ce post, voici ma suggestion:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'
L'idée est de convertir les blancs en n'importe quel caractère unique, comme '<', puis de changer cela en '\', une barre oblique inverse suivie d'un blanc. Vous pouvez ensuite diriger cette commande vers la commande de votre choix, telle que:
ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo
La clé ici réside dans les commandes 'tr' et 'sed'; et vous pouvez utiliser n'importe quel caractère en plus de '<', tel que '?' ou même un caractère de tabulation.
Des solutions alternatives peuvent être utiles ...
Vous pouvez également ajouter un caractère null à la fin de vos lignes à l'aide de Perl, puis utiliser l'option -0
dans xargs. Contrairement à xargs -d '\ n' (dans la réponse approuvée) - cela fonctionne partout, y compris OS X.
Par exemple, pour lister de manière récursive (exécuter, déplacer, etc.) des images JPEG pouvant contenir des espaces ou d’autres personnages amusants, il faudrait utiliser:
find . | grep \.jpg | Perl -ne 'chop; print "$_\0"' | xargs -0 ls
(Remarque: pour le filtrage, je préfère la syntaxe "| grep" facile à mémoriser aux arguments de "find" --name.)