Utilisation de xargs en combinaison avec bash -c pour créer des commandes complexes –

xargs est un feu d’artifice pour vos commandes shell. Toute sortie, générée à partir de n’importe quelle commande shell, peut être envoyée à xargs pour qu’elle soit traitée ultérieurement dans une autre ligne de commande. Apprenez à exploiter cette grande puissance des xargs dès aujourd’hui!
Sommaire
xargs Introduction
Mon collègue Dave McKay a écrit un article intéressant Comment utiliser la commande xargs sous Linux, que vous aimerez peut-être lire en premier pour une introduction détaillée et une exploration de xargs en général.
Cet article se concentrera sur un problème spécifique: que faire lorsque vous rencontrez des limitations de l’empilement traditionnel basé sur des tuyaux, ou normal (;
délimité) l’empilement de commandes Linux, et même en utilisant xargs ne semble pas immédiatement fournir une réponse?
C’est régulièrement, sinon souvent, le cas lors de l’écriture de scripts à une seule ligne sur la ligne de commande et / ou lors du traitement de données ou de structures de données complexes. Les informations présentées ici sont basées sur des recherches et de nombreuses années d’expérience dans l’utilisation de cette méthode et approche particulières.
Tuyaux et xargs
Quiconque apprend Bash développera ses compétences en script en ligne de commande au fil du temps. La grande chose avec Bash est que toutes les compétences acquises en ligne de commande se traduisent facilement en scripts Bash (qui sont généralement marqués d’un .sh
suffixe). La syntaxe est aussi bonne qu’identique.
Et, à mesure que les compétences s’améliorent, les nouveaux ingénieurs découvriront généralement l’idiome Pipe en premier. L’utilisation d’un tuyau est simple et directe: la sortie de la commande précédente est « canalisée » vers l’entrée pour la commande suivante, pensez-y comme une conduite d’eau apportant de l’eau d’une source de sortie à une source d’entrée ailleurs, par exemple l’eau d’un barrage raccordé à une turbine à eau.
Regardons un exemple simple:
echo 'a' | sed 's/a/b/'
Ici, nous avons simplement fait écho à ‘a’ et avons ensuite changé la même chose en utilisant l’éditeur de flux de texte sed. La sortie est naturellement ‘b’: la commande sed a substitué (s) ‘a’ à ‘b’.
Quelque temps plus tard, l’ingénieur se rendra compte que les canalisations n’ont encore que des capacités limitées, en particulier lorsque l’on souhaite pré-traiter les données dans un format prêt pour le prochain outil. Par exemple, considérez cette situation:
Ici, nous commençons un sommeil en arrière-plan. Ensuite, nous utilisons pidof
pour récupérer le PID de la commande de sommeil en cours d’exécution et tenter de le tuer avec kill -9
(Pensez à -9 comme un mode destructeur pour tuer un processus). Il échoue. Nous essayons ensuite d’utiliser le PID fourni par le shell lorsque nous avons démarré le processus d’arrière-plan, mais cela échoue de la même manière.
Le problème est que kill
n’accepte pas directement l’entrée, qu’elle provienne ps
ou même d’un simple echo
. Pour résoudre ce problème, nous pouvons utiliser xargs pour prendre la sortie soit du ps
ou la echo
commande) et les fournir comme entrée pour kill
, en leur faisant des arguments à la commande kill. C’est donc comme si on exécutait kill -9 some_pid
directement. Voyons comment cela fonctionne:
sleep 300 & pidof sleep | xargs kill -9
Cela fonctionne parfaitement et réalise ce que nous avons décidé de faire: tuer le processus de sommeil. Un petit changement au code (c’est-à-dire ajouter simplement xargs devant la commande), mais un grand changement sur l’utilité de Bash pour l’ingénieur en développement!
Nous pouvons également utiliser le -I
option (définissant l’argument replace string) à kill
pour rendre un peu plus clair comment nous passons les arguments à tuer: i12b Ici, nous définissons {}
comme notre chaîne de remplacement. En d’autres termes, chaque fois que xargs verra {}
, il se substituera {}
à n’importe quelle entrée reçue de la dernière commande.
Pourtant, même cela a ses limites. Et si nous voulions fournir de belles informations de débogage imprimées en ligne et à partir de l’instruction? Cela semble impossible jusqu’à présent.
Oui, nous pourrions post-traiter la sortie avec un sed
regex, ou insérez un sous-shell ($()
) quelque part, mais tout cela semble encore avoir des limitations, et particulièrement quand il est temps de créer des flux de données complexes, sans utiliser une nouvelle commande, et sans utiliser de fichiers temporaires intermédiaires.
Et si nous pouvions – une fois pour toutes – laisser ces limites derrière nous et être 100% libres de créer tout La ligne de commande Bash que nous aimons, n’utilise que des tubes, des xargs et le shell Bash, sans fichiers intermédiaires temporaires et sans démarrer une nouvelle commande? C’est possible.
Et ce n’est pas du tout complexe si quelqu’un vous montre, mais la première fois, cela a pris du temps et des discussions pour comprendre. Je tiens tout particulièrement à remercier mon ancien mentor Linux et mon ancien collègue, Andrew Dalgleish – ensemble, nous avons trouvé la meilleure façon de le faire il y a un peu moins de 10 ans.
Bienvenue chez xargs avec bash -c
Comme nous l’avons vu, même lorsque les tubes sont utilisés en combinaison avec xargs, il y aura toujours des limitations pour les scripts de niveau ingénieur un peu plus expérimenté. Prenons notre exemple précédent et injectons des informations de débogage sans post-traitement du résultat. Normalement, cela serait difficile à réaliser, mais pas avec xargs
combiné avec bash -c
:
sleep 300 & pidof sleep | xargs -I{} echo "echo 'The PID of your sleep process was: {}'; kill -9 {}; echo 'PID {} has now been terminated'" | xargs -I{} bash -c "{}"
Ici, nous avons utilisé deux xargs
commandes. Le premier construit une ligne de commande personnalisée, en utilisant comme entrée la sortie de la commande précédente dans le tube (étant pidof sleep
) et la deuxième commande xargs exécute cette commande générée, personnalisée par entrée (important!).
Pourquoi personnaliser par entrée? La raison en est que xargs par défaut traitera ligne par ligne via son entrée (la sortie de la commande précédente dans le tube) et exécutera tout ce qu’il a été chargé d’exécuter pour chacune de ces lignes d’entrée.
Il y a beaucoup de pouvoir ici. Cela signifie que vous pouvez créer et construire n’importe quelle commande personnalisée et l’exécuter par la suite, totalement libre de tout format dans lequel se trouvent les données d’entrée et sans avoir à vous soucier de la façon de l’exécuter. La seule syntaxe dont vous devez vous souvenir est la suivante:
some_command | xargs -I{} echo "echo '...{}...'; more_commands; more_commands_with_or_without{}" | xargs -I{} bash -c "{}"
Notez que les echo
(le deuxième écho) n’est vraiment nécessaire que si vous souhaitez réémettre le texte réel. Sinon, si le second echo
n’était pas là, le premier echo
commencerait à afficher «Le PID…» etc. et le bash -c
Le sous-shell ne pourrait pas analyser cela comme une commande (IOW, «Le PID…» n’est pas une commande, et ne peut pas être exécuté en tant que tel, d’où l’écho secondaire / imbriqué).
Une fois que vous vous souvenez du bash -c
, le -I{}
et la façon de faire un écho à partir d’un autre écho (et on pourrait également utiliser des séquences d’échappement si nécessaire), vous vous retrouverez à utiliser cette syntaxe encore et encore.
Disons que vous devez exécuter trois choses par fichier dans le répertoire: 1) afficher le contenu du fichier, 2) le déplacer dans un sous-répertoire, 3) le supprimer. Normalement, cela nécessiterait un certain nombre d’étapes avec différentes commandes par étapes, et si cela devient plus complexe, vous pourriez même avoir besoin de fichiers temporaires. Mais cela se fait très facilement en utilisant bash -c
et xargs
:
echo '1' > a echo '2' > b echo '3' > c mkdir subdir ls --color=never | grep -v subdir | xargs -I{} echo "cat {}; mv {} subdir; rm subdir/{}" | xargs -I{} bash -c "{}"
Tout d’abord en notant rapidement que c’est toujours une bonne idée à utiliser --color=never
pour ls
, pour éviter les problèmes sur le système qui utilise un code couleur pour les sorties de liste de répertoires (activé par défaut dans Ubuntu), car cela provoque souvent de forts problèmes d’analyse en raison des codes de couleur réellement envoyés au terminal et préfixés aux entrées de liste de répertoires.
Nous créons d’abord trois fichiers, a, b et c, et un sous-répertoire nommé subdir. Nous excluons ce sous-répertoire de la liste du répertoire avec grep -v
après avoir remarqué qu’un essai rapide (sans exécuter les commandes, ou en d’autres termes sans le deuxième xargs), le sous-répertoire s’affiche toujours.
Il est toujours important de tester d’abord vos commandes complexes en observant la sortie avant de les passer réellement à un sous-shell bash avec bash -c
pour exécution.
Enfin, nous utilisons exactement la même méthode que celle vue précédemment pour construire ou commander; cat
(afficher) le fichier, déplacer le fichier (indiqué par {}
) dans le sous-répertoire, puis supprimez finalement le fichier à l’intérieur du sous-répertoire. Nous voyons que le contenu des 3 fichiers (1, 2, 3) est affiché à l’écran, et si nous vérifions le répertoire actuel, nos fichiers ont disparu. Nous pouvons également voir comment il n’y a plus de fichiers dans le sous-répertoire. Tout a bien fonctionné.
Emballer
En utilisant xargs
étant une grande puissance pour un utilisateur avancé de Linux (et dans ce cas) Bash. En utilisant xargs
en combinaison avec bash -c
apporte encore une puissance bien plus grande; la capacité unique de créer des lignes de commande personnalisées et complexes à 100% gratuites, sans avoir besoin de fichiers intermédiaires ou de constructions, et sans avoir besoin d’avoir des commandes empilées / séquencées.
Prendre plaisir!