Comment puis-je faire en sorte que xargs gère les noms de fichiers contenant des espaces?

$ 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 qu’il s’agit de deux fichiers. Puis-je faire fonctionner find + xargs avec des noms de fichiers comme celui-ci?

    La commande xargs prend les espaces en blanc (tabs, espaces, nouvelles lignes) en tant que délimiteurs. Vous pouvez le réduire uniquement pour les nouveaux caractères de ligne (‘\ n’) avec l’option -d comme ceci:

     ls *.mp3 | xargs -d '\n' mplayer 

    Cela fonctionne seulement 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 les GNU xargs.

    L’utilitaire xargs lit les chaînes d’espace, de tabulation, de nouvelle ligne et de fin 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 un délimiteur. Cela peut être fait en changeant 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 de jouer tous les septièmes mp3, il est plus simple de courir

      mplayer "$(ls | grep 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, donc cette solution utilise -0 à la place.

    Obtenez ls pour sortir un fichier par ligne, puis traduisez les newlines en null et dites à xargs d’utiliser les valeurs 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 dans un nom de fichier est une alternative intéressante aux autres solutions suggérées ici (comme l’utilisation d’un caractère nul comme séparateur plutôt que comme espace). Mais cela pourrait être plus simple – vous n’avez pas vraiment besoin d’un personnage unique. Vous pouvez juste avoir sed append directement les espaces échappés:

     ls | grep ' ' | sed 's| |\\ |g' | xargs ... 

    De plus, le grep n’est nécessaire que si vous voulez uniquement des fichiers avec des espaces dans les noms. Plus génériquement (par exemple, lors du traitement d’un lot de fichiers dont certains ont des espaces, d’autres non), ignorez simplement le grep:

     ls | sed 's| |\\ |g' | xargs ... 

    Ensuite, bien sûr, le nom de fichier peut avoir d’autres espaces que les blancs (par exemple, un onglet):

     ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ... 

    Cela suppose que vous ayez un sed qui supporte -r (regex étendue) tel que GNU sed ou les versions récentes de bsd sed (par exemple, FreeBSD qui a initialement orthographié l’option “-E” avant FreeBSD 8 et supporte à la fois -r & -E pour la compatibilité via FreeBSD 11 au moins). Sinon, vous pouvez utiliser une expression de base de classe de caractères regex de base et entrer manuellement les espaces et les tabulations dans les délimiteurs [] .

    [1] C’est peut-être plus approprié comme commentaire ou édition de 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 cette dernière forme ci-dessus (sans le grep) modifie 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é sur Linux Xubuntu 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 être acceptable pour d’autres cibles.

    Cela dépend de (a) votre attachement au nombre 7, par opposition à, par exemple, Citrons, et (b) si l’un de vos noms de fichiers contient des lignes nouvelles (et si vous êtes prêt à les renommer).

    Il y a plusieurs façons de le gérer, mais certaines 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 de 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 doubles autour du nom de fichier est la clé pour que les boucles fonctionnent correctement.

    Si vous avez GNU find et GNU xargs (ou FreeBSD (* BSD?) Ou Mac OS X), vous pouvez également utiliser les options -print0 et -0 , 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 le slash ne pose aucun problème dans un chemin de fichier, donc utiliser NUL comme délimiteur de nom couvre tout). Cependant, si vous avez besoin de 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 la nouvelle ligne … et je ne suis pas sûr qu’il y en ait.

    Le premier est de loin le plus simple pour le cas particulier dont nous disposons; Cependant, il ne peut pas être généralisé pour couvrir vos autres scénarios que vous n’avez pas encore répertoriés.

    Je sais que je ne réponds pas directement à la question de xargs mais il vaut la peine de mentionner l’option -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 Theater et King’s X. Donc, pour trouver les batteurs de chaque groupe en utilisant 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 l’option -exec , {} représente le nom de fichier incluant le chemin. Notez que vous n’avez pas à y échapper ou à le mettre entre guillemets.

    La différence entre les -exec de -exec ( + et \; ) est que + regroupe autant de noms de fichiers que possible sur une seule ligne de commande. Considérant que \; exécutera la commande pour chaque nom de fichier.

    Donc, find bands/ -type f -exec grep Drums {} + donnera:

     grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth" 

    et find bands/ -type f -exec grep Drums {} \; aura pour résultat:

     grep Drums "bands/Dream Theater" grep Drums "bands/Rush" grep Drums "bands/King's X" grep Drums "bands/Megadeth" 

    Dans le cas de grep cela a pour effet secondaire 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ôleront si le nom du fichier est imprimé, quelle que soit la manière dont grep est appelé.


    xargs

    xargs peut également contrôler la manière dont les fichiers man sont sur la ligne de commande.

    xargs regroupe par défaut tous les arguments sur une seule ligne. Pour 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 

    Voir que l’option -l demande à xargs d’exécuter grep pour chaque nom de fichier.

    Par rapport à la valeur par défaut (c.-à-d. Option non -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 a un meilleur contrôle sur le nombre de fichiers pouvant être sur la 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 été exécuté avec deux noms de fichiers à cause de -l2 .

    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 

    Étant donné le titre spécifique de cet article, 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 le changer en '\', une barre oblique suivie d'un blanc. Vous pouvez ensuite transférer cela dans n'importe quelle commande, par exemple:

     ls | grep ' ' | tr ' ' '< ' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo 

    La clé réside dans les commandes 'tr' et 'sed'; et vous pouvez utiliser n'importe quel caractère en dehors de '< ', tel que '?' ou même un caractère de tabulation.

    Des solutions alternatives peuvent être utiles …

    Vous pouvez également append un caractère nul à la fin de vos lignes à l’aide de Perl, puis utiliser l’option -0 dans xargs. Contrairement aux 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, j’utiliserais:

     find . | grep \.jpg | perl -ne 'chop; print "$_\0"' | xargs -0 ls 

    (Note: Pour le filtrage, je préfère la syntaxe “| grep” plus facile à retenir pour “trouver” les arguments –name.)