Confus par la sortie en utilisant xargs

J’ai ces fichiers dans un répertoire: y y1 y2 y3

Exécuter cette commande:
ls y* | xargs -i basename {}|xargs -i sed "s/{}//g"

produit ceci:
1
2
3

Quelqu’un peut-il expliquer pourquoi? Je m’attendais à ce qu’il ne produise rien – exécutez sed quatre fois, une fois pour chaque fichier et supprimez le nom du fichier à chaque fois. Mais en réalité, il semble qu’il applique sed avec {} défini sur le premier fichier, sur une liste de y1 y2 y3

Ceci est Solaris 10

Lorsque j’essaie ceci sur ma boîte Linux, les résultats sont incohérents. Parfois 123, parfois (la plupart du temps) 23, parfois 12. Il s’agit d’une condition de course subtile entre les xargs les plus à droite et l’un des sed créés.

Dissection de la ligne de commande:

  • ls y* affichera 4 lignes, y, y1, y2 et y3; mise en mémoire tampon non pertinente
  • xargs -i basename {} les lira et lancera, dans une séquence, le nom de basename y , le nom de basename y1 , le nom de basename y2 , le nom de basename y3 ; La sortie, identique à celle utilisée dans notre cas, est mise en mémoire tampon car chaque ligne provient d’un processus différent.
  • xargs -i sed "s/{}//g" , pour chaque ligne X qu’il lit (plus tard plus tard), lance sed "s/X//g"
  • chaque sed "s/X//g" filtre chaque X qu’il voit dans les lignes qu’il lit

Là où ça devient difficile: les deux dernières commandes lisent les entrées du même stream. Ce stream est produit par plusieurs processus différents dans une séquence. Selon une multitude de facteurs (charge du système, ordonnancement), la sortie peut être produite selon des schémas de synchronisation très différents.

Supposons qu’ils soient tous très rapides. Ensuite, les quatre lignes peuvent être disponibles pour que les xargs puissent être lus dans un seul bloc. Dans ce cas, il n’y aurait plus d’entrée pour aucun des sed à lire, donc pas de sortie du tout .

D’un autre côté, si elles étaient très lentes, il ne pourrait y avoir qu’une seule ligne disponible pour le bon xargs lors de sa première tentative de lecture. Cette ligne serait “y”. xargs engendrerait le premier sed comme sed "s/y//g" , qui consumrait toutes les entrées restantes (y1, y2, y3), la bande y et les sorties 1, 2, 3 . Voici la même explication à nouveau, avec un séquençage plus explicite.

  1. le premier nom de basename écrit “y”.
  2. xargs droit lit “y”, crée sed s/y//g . xargs attend maintenant que sed se termine.
  3. deuxième nom de basename écrit “y1”; sed lit “y1”, écrit “1”
  4. le troisième nom de basename écrit “y2”; sed lit “y2”, écrit “2”
  5. le quasortingème nom de basename écrit “y3”; sed lit “y3”, écrit “3”
  6. xargs gauche est terminé; sed lit EOF et s’arrête
  7. xargs droite essaie de continuer, lit EOF et arrête

Pas sûr de mon cas. Il est possible que GNU xargs n’attende pas que ses enfants se terminent avant de lire les entrées disponibles subséquentes et arrache la ligne “y3” du premier sed .

Dans tous les cas, vous venez de configurer un pipeline avec plusieurs lecteurs simultanés sur le même graveur , ce qui donne des résultats essentiellement indéterminés. À éviter.

Si vous vouliez une opération sur chacun des fichiers, cela serait évité en spécifiant un nom de fichier à utiliser par sed (notez le final {}):

 ls y* | xargs -i basename {} | xargs -i sed "s/{}//g" {} 

Si vous vouliez un résultat de type produit croisé (enlevez chaque nom de fichier de chaque fichier), vous devez vous arranger pour que la liste des fichiers soit produite autant de fois qu’il y a de fichiers. Plus un pour xargs , si vous avez encore utilisé ça.

J’espère que cela t’aides.

L’entrée de la commande xargs -i sed … est:

 y y1 y2 y3 

La commande lira la ligne y et exécutera sed s/y//g , qui lit depuis l’entrée standard. L’entrée standard est héritée, elle aura donc le même canal que son entrée standard et pourra lire les entrées restantes:

 y1 y2 y3 

La commande sed s/y//g va supprimer le y de chaque ligne:

 1 2 3 

Cependant, si xargs consum toute l’entrée avant l’exécution de la première commande sed commande sed n’aura plus aucune entrée à lire et ne fera rien.