web-dev-qa-db-fra.com

Quels sont les opérateurs de contrôle et de redirection de Shell?

Je vois souvent des tutoriels en ligne qui connectent diverses commandes avec différents symboles. Par exemple:

command1 |  command2
command1 &  command2
command1 || command2    
command1 && command2

D'autres semblent connecter des commandes à des fichiers:

command1  > file1
command1  >> file1

Quelles sont ces choses? Comment s'appellent-ils? Que font-ils? Y en a-t-il plus?


Meta thread sur cette question. .

278
terdon

On les appelle les opérateurs Shell et oui, il y en a plus. Je vais donner un bref aperçu des plus courants parmi les deux classes principales, opérateurs de contrôle et opérateurs de redirection , et comment ils fonctionnent par rapport au shell bash.

A. Opérateurs de contrôle

Dans le langage de commande Shell, un jeton qui exécute une fonction de contrôle.
Il s'agit de l'un des symboles suivants:

&   &&   (   )   ;   ;;   <newline>   |   ||

Et |& En bash.

Un ! N'est pas pas un opérateur de contrôle mais un mot réservé . Il devient un NOT [opérateur de négation] logique à l'intérieur Expressions arithmétiques et à l'intérieur des constructions de test (tout en nécessitant toujours un délimiteur d'espace).

A.1 Liste des terminateurs

  • ;: Exécutera une commande après la fin d'une autre, quel que soit le résultat de la première.

    command1 ; command2
    

    command1 Est d'abord exécuté, au premier plan, et une fois terminé, command2 Sera exécuté.

    Une nouvelle ligne qui n'est pas dans un littéral de chaîne ou après certains mots clés est pas équivalent à l'opérateur point-virgule. Une liste de commandes simples délimitées par ; Est toujours une liste - comme dans l'analyseur Shell doit continuer à lire les commandes simples qui suivent une délimitation ; une simple commande avant de l'exécuter, tandis qu'une nouvelle ligne peut délimiter une liste de commandes entière - ou une liste de listes. La différence est subtile, mais compliquée: étant donné que le shell n'a aucun impératif préalable pour lire les données après une nouvelle ligne, la nouvelle ligne marque un point où le shell peut commencer à évaluer les commandes simples qu'il a déjà lues, tandis qu'un ; le point-virgule ne fonctionne pas.

  • &: Cela exécutera une commande en arrière-plan, vous permettant de continuer à travailler dans le même shell.

     command1 & command2
    

    Ici, command1 Est lancé en arrière-plan et command2 Démarre immédiatement au premier plan, sans attendre que command1 Se termine.

    Une nouvelle ligne après command1 Est facultative.

A.2 Opérateurs logiques

  • &&: Utilisé pour construire des listes ET, il vous permet d'exécuter une commande uniquement si une autre s'est terminée avec succès.

     command1 && command2
    

    Ici, command2 S'exécutera après la fin de command1 Et seulement si command1 A réussi (si son code de sortie était 0). Les deux commandes sont exécutées au premier plan.

    Cette commande peut également être écrite

    if command1
    then command2
    else false
    fi
    

    ou simplement if command1; then command2; fi si le statut de retour est ignoré.

  • ||: Utilisé pour créer des listes OR, il vous permet d'exécuter une commande uniquement si une autre s'est terminée sans succès.

     command1 || command2
    

    Ici, command2 Ne s'exécutera que si command1 A échoué (s'il a renvoyé un état de sortie autre que 0). Les deux commandes sont exécutées au premier plan.

    Cette commande peut également être écrite

    if command1
    then true
    else command2
    fi
    

    ou d'une manière plus courte if ! command1; then command2; fi.

    Notez que && Et || Sont associatifs à gauche; voir Priorité des opérateurs logiques Shell &&, pour plus d'informations.

  • !: Ceci est un mot réservé qui agit comme l'opérateur "non" (mais doit avoir un délimiteur), utilisé pour annuler le statut de retour d'une commande - retourne 0 si la commande retourne un statut différent de zéro, retourne 1 s'il renvoie l'état 0. Également un NOT logique pour l'utilitaire test.

    ! command1
    
    [ ! a = a ]
    

    Et un véritable opérateur NOT à l'intérieur des expressions arithmétiques:

    $ echo $((!0)) $((!23))
    1 0
    

A.3 Opérateur de tuyauterie

  • |: L'opérateur de pipe, il passe la sortie d'une commande en entrée à une autre. Une commande construite à partir de l'opérateur de tuyau est appelée pipeline .

     command1 | command2
    

    Toute sortie imprimée par command1 Est transmise en entrée à command2.

  • |&: Ceci est un raccourci pour 2>&1 | En bash et zsh. Il transmet à la fois la sortie standard et l'erreur standard d'une commande en entrée à une autre.

    command1 |& command2
    

A.4 Autre ponctuation de liste

;; Est utilisé uniquement pour marquer la fin d'un instruction case . Ksh, bash et zsh prennent également en charge ;& Pour passer au cas suivant et ;;& (Pas dans ATT ksh) pour continuer et tester les cas suivants.

( Et ) Sont utilisés pour commandes de groupe et les lancer dans un sous-shell. { Et } Regroupent également des commandes, mais ne les lancez pas en sous-shell. Voir cette réponse pour une discussion sur les différents types de parenthèses, crochets et accolades dans la syntaxe Shell.

Opérateurs de redirection

Opérateur de redirection

Dans le langage de commande Shell, un jeton qui exécute une fonction de redirection. Il s'agit de l'un des symboles suivants:

<     >     >|     <<     >>     <&     >&     <<-     <>

Ceux-ci vous permettent de contrôler l'entrée et la sortie de vos commandes. Ils peuvent apparaître n'importe où dans une simple commande ou peuvent suivre une commande. Les redirections sont traitées dans l'ordre dans lequel elles apparaissent, de gauche à droite.

  • <: Donne une entrée à une commande.

    command < file.txt
    

    Ce qui précède exécutera command sur le contenu de file.txt.

  • <>: Idem que ci-dessus, mais le fichier est ouvert en mode lecture + écriture au lieu de lecture seule:

    command <> file.txt
    

    Si le fichier n'existe pas, il sera créé.

    Cet opérateur est rarement utilisé parce que les commandes ne sont généralement que - lire à partir de leur stdin, bien que il peut être utile dans un certain nombre de situations spécifiques .

  • >: Dirige la sortie d'une commande dans un fichier.

    command > out.txt
    

    Ce qui précède enregistrera la sortie de command sous out.txt. Si le fichier existe, son contenu sera écrasé et s'il n'existe pas, il sera créé.

    Cet opérateur est également souvent utilisé pour choisir si quelque chose doit être imprimé sur erreur standard ou sortie standard :

    command >out.txt 2>error.txt
    

    Dans l'exemple ci-dessus, > Redirigera la sortie standard et 2> Redirigera l'erreur standard. La sortie peut également être redirigée à l'aide de 1> Mais, comme il s'agit de la valeur par défaut, le 1 Est généralement omis et il est simplement écrit comme >.

    Ainsi, pour exécuter command sur file.txt Et enregistrer sa sortie dans out.txt Et tous les messages d'erreur dans error.txt, Vous devez exécuter:

    command < file.txt > out.txt 2> error.txt
    
  • >|: Fait la même chose que >, Mais écrasera la cible, même si le shell a été configuré pour refuser l'écrasement (avec set -C Ou set -o noclobber) .

    command >| out.txt
    

    Si out.txt Existe, la sortie de command remplacera son contenu. S'il n'existe pas, il sera créé.

  • >>: Fait la même chose que >, Sauf que si le fichier cible existe, les nouvelles données sont ajoutées.

    command >> out.txt
    

    Si out.txt Existe, la sortie de command lui sera ajoutée, après tout ce qui s'y trouve déjà. S'il n'existe pas, il sera créé.

  • &>, >&, >>& Et &>>: (Non standard). Redirigez l'erreur standard et la sortie standard, en remplaçant ou en ajoutant respectivement.

    command &> out.txt
    

    L'erreur standard et la sortie standard de command seront enregistrées dans out.txt, Écrasant son contenu ou le créant s'il n'existe pas.

    command &>> out.txt
    

    Comme ci-dessus, sauf que si out.txt Existe, la sortie et l'erreur de command y seront ajoutées.

    La variante &> Provient de bash, tandis que la variante >& Vient de csh (des décennies plus tôt). Ils sont tous deux en conflit avec d'autres opérateurs POSIX Shell et ne doivent pas être utilisés dans des scripts portables sh.

  • <<: Un document ici. Il est souvent utilisé pour imprimer des chaînes multi-lignes.

     command << Word
         Text
     Word
    

    Ici, command prendra tout jusqu'à ce qu'il trouve la prochaine occurrence de Word, Text dans l'exemple ci-dessus, en entrée. Alors que Word est souvent EoF ou des variantes de celui-ci, il peut s'agir de n'importe quelle chaîne alphanumérique (et pas seulement) que vous aimez. Lorsque Word est cité, le texte du document ici est traité littéralement et aucune expansion n'est effectuée (sur des variables par exemple). S'il n'est pas cité, les variables seront développées. Pour plus de détails, consultez le manuel bash .

    Si vous voulez diriger la sortie de command << Word ... Word Directement dans une ou plusieurs commandes, vous devez placer le tuyau sur la même ligne que << Word, Vous ne pouvez pas le placer après le mot de fin ou sur la ligne suivante. Par exemple:

     command << Word | command2 | command3...
         Text
     Word
    
  • <<<: Voici des chaînes, similaires aux documents ici, mais destinées à une seule ligne. Ceux-ci n'existent que dans le port Unix ou rc (d'où il provient), zsh, certaines implémentations de ksh, yash et bash.

    command <<< Word
    

    Tout ce qui est donné comme Word est développé et sa valeur est passée en entrée à command. Ceci est souvent utilisé pour transmettre le contenu des variables en entrée à une commande. Par exemple:

     $ foo="bar"
     $ sed 's/a/A/' <<< "$foo"
     bAr
     # as a short-cut for the standard:
     $ printf '%s\n' "$foo" | sed 's/a/A/'
     bAr
     # or
     sed 's/a/A/' << EOF
     $foo
     EOF
    

Quelques autres opérateurs (>&-, x>&yx<&y) Peuvent être utilisés pour fermer ou dupliquer des descripteurs de fichiers. Pour plus de détails à leur sujet, veuillez consulter la section pertinente du manuel de votre Shell ( ici par exemple pour bash).

Cela ne couvre que les opérateurs les plus courants de coques de type Bourne. Certains shells ont leurs propres opérateurs de redirection supplémentaires.

Ksh, bash et zsh ont également des constructions <(…), >(…) et =(…) (cette dernière dans zsh uniquement). Ce ne sont pas des redirections, mais substitution de processus .

381
terdon

Avertissement concernant ‘>’

Les débutants Unix qui viennent d'apprendre la redirection d'E/S (< et >) essaie souvent des choses comme

commander … fichier_entrée > the_same_file

ou

commander … < fichier     > the_same_file

ou, presque de manière équivalente,

chat fichier | commander …> the_same_file

(grep, sed, cut, sort et spell sont des exemples de commandes que les gens sont tentés d'utiliser dans des constructions comme celles-ci .) Les utilisateurs sont surpris de découvrir que ces scénarios entraînent la vidange du fichier.

Une nuance qui ne semble pas être mentionnée dans l'autre réponse se trouve dans la première phrase de la section Redirection de bash (1) :

Avant qu'une commande ne soit exécutée, ses entrées et sorties peuvent être redirigées en utilisant une notation spéciale interprétée par le Shell.

Les cinq premiers mots doivent être en gras, en italique, soulignés, agrandis, clignotants, colorés en rouge et marqués d'un exclamation mark in red triangle , pour souligner le fait que le Shell effectue la ou les redirection (s) demandée (s) avant l'exécution de la commande. Et rappelez-vous aussi

La redirection de la sortie entraîne l'ouverture du fichier… pour l'écriture…. Si le fichier n'existe pas, il est créé; s'il existe, il est tronqué à zéro.

  1. Donc, dans cet exemple:

    sort roster > roster
    

    le Shell ouvre le fichier roster pour l'écriture, le tronque (c'est-à-dire en supprimant tout son contenu), avant que le programme sort ne démarre. Naturellement, rien ne peut être fait pour récupérer les données.

  2. On pourrait naïvement s’attendre à ce que

    tr "[:upper:]" "[:lower:]" < poem > poem
    

    pourrait être mieux. Étant donné que le shell gère les redirections de gauche à droite, il ouvre poem pour la lecture (pour l'entrée standard de tr) avant de l'ouvrir pour l'écriture (pour la sortie standard). Mais ça n'aide pas. Même si cette séquence d'opérations produit deux descripteurs de fichiers, ils pointent tous deux vers le même fichier. Lorsque le shell ouvre le fichier pour lecture, le contenu est toujours là, mais il est toujours encombré avant l'exécution du programme.

Alors, que faire?

Les solutions comprennent:

  • Vérifiez si le programme que vous exécutez a sa propre capacité interne pour spécifier où va la sortie. Ceci est souvent indiqué par un -o (ou --output=) jeton. En particulier,

    sort roster -o roster
    

    est à peu près équivalent à

    sort roster > roster
    

    sauf que, dans le premier cas, le programme sort ouvre le fichier de sortie. Et il est suffisamment intelligent pour ne pas ouvrir le fichier de sortie tant que après il n'a pas lu tous les fichiers d'entrée.

    De même, au moins certaines versions de sed ont un -i (Éditer jen place) option qui peut être utilisée pour réécrire la sortie dans le fichier d'entrée (encore une fois, après toutes les entrées ont été lues). Les éditeurs comme ed/ex, emacs, pico et vi/vim permettent à l'utilisateur de modifier un fichier texte et enregistrez le texte modifié dans le fichier d'origine. Notez que ed (au moins) peut être utilisé de manière non interactive.

    • vi a une fonction connexe. Si vous tapez :%!commandEnter, il écrira le contenu du tampon d'édition dans command, lira la sortie et l'insérera dans le tampon (en remplaçant le contenu d'origine).
  • Simple mais efficace:

    commander … fichier_entrée > fichier_temp  && mv fichier_tempfichier_entrée

    Cela présente l'inconvénient que, si input_file est un lien, il sera (probablement) remplacé par un fichier séparé. De plus, le nouveau fichier vous appartiendra, avec des protections par défaut. En particulier, cela comporte le risque que le fichier finisse par être lisible par tout le monde, même si l'original input_file ne l'était pas.

    Variations:

    • commandinput_file > temp_file && cp temp_fileinput_file && rm temp_file
      qui laissera (potentiellement) encore le temp_file lisible dans le monde entier. Encore mieux:
    • cp input_filetemp_file && commandtemp_file > input_file && rm temp_file
      Ceux-ci préservent l'état de la liaison, le propriétaire et le mode (protection) du fichier, potentiellement au prix de deux fois plus d'E/S. (Vous devrez peut-être utiliser une option comme -a ou -p on cp pour lui dire de conserver les attributs.)
    • commandinput_file > temp_file &&
      cp --attributes-only --preserve=all input_filetemp_file &&
      mv temp_fileinput_file
      (divisé en lignes distinctes uniquement pour des raisons de lisibilité) Cela préserve le mode du fichier (et, si vous êtes root, le propriétaire), mais le rend détenu par vous (si vous n'êtes pas root), et en fait un nouveau fichier séparé.
  • Ce blog (édition "sur place" des fichiers) suggère et explique

    {rm fichier_entrée  && commander …> fichier_entrée; } < fichier_entrée

    Cela nécessite que le command puisse traiter l'entrée standard (mais presque tous les filtres le peuvent). Le blog lui-même appelle cela un coup de tête risqué et décourage son utilisation. Et cela créera également un nouveau fichier séparé (non lié à quoi que ce soit), détenu par vous et avec des autorisations par défaut.

  • Le paquet moreutils a une commande appelée sponge:

    commander … fichier_entrée | éponge the_same_file

    Voir cette réponse pour plus d'informations.

Voici quelque chose qui m'a complètement surpris: erreur de syntaxe :

[La plupart de ces solutions] échouera sur un système de fichiers en lecture seule, où "lecture seule" signifie que votre $HOME le sera être accessible en écriture, mais /tmp sera en lecture seule (par défaut). Par exemple, si vous avez Ubuntu et que vous avez démarré dans la console de récupération, c'est généralement le cas. L'opérateur ici-document <<< ne fonctionnera pas non plus, car cela nécessite /tmp être en lecture/écriture car il y écrira également un fichier temporaire.
(cf.. cette question inclut une sortie strace ’d)

Les éléments suivants peuvent fonctionner dans ce cas:

  • Pour les utilisateurs avancés uniquement: Si votre commande est garantie de produire la même quantité de données de sortie qu'il y a d'entrée (par exemple, sort, ou tr sans le -d ou -s option), vous pouvez essayer
    commander … fichier_entrée | jj de =the_same_file conv = notrunc
    Voir cette réponse et cette réponse pour plus d'informations, y compris une explication de ce qui précède, et des alternatives qui fonctionnent si votre commande est garantie de produire la même quantité des données de sortie lorsqu'elles sont entrées ou moins (par exemple, grep, ou cut). Ces réponses ont l'avantage de ne pas nécessiter d'espace libre (ou très peu). Les réponses ci-dessus du formulaire commandinput_file > temp_file && … exige clairement qu'il y ait suffisamment d'espace libre pour que le système puisse contenir le fichier d'entrée (ancien) et le fichier de sortie (nouveau) simultanément; cela n'est pas évidemment vrai pour la plupart des autres solutions (par exemple, sed -i et sponge) également. Exception: sort … | dd … nécessitera probablement beaucoup d'espace libre, car sort doit lire toutes ses entrées avant de pouvoir écrire n'importe quelle sortie, et il met probablement en mémoire tampon la plupart sinon la totalité de ces données dans un fichier temporaire.
  • Pour les utilisateurs avancés uniquement:
    commander … fichier_entrée 1 <> the_same_file
    peut être équivalent à la réponse dd ci-dessus. Le n<>file la syntaxe ouvre le fichier nommé sur le descripteur de fichier n pour l'entrée et la sortie, sans le tronquer - une sorte de combinaison de n< et n>. Remarque: Certains programmes (par exemple, cat et grep) peuvent refuser de s'exécuter dans ce scénario car ils peuvent détecter que l'entrée et la sortie sont le même fichier. Voir cette réponse pour une discussion de ce qui précède, et un script qui fait fonctionner cette réponse si votre commande est garantie de produire la même quantité de données de sortie qu'il y a d'entrée ou moins.
    Avertissement: Je n'ai pas testé le script de Peter, donc je ne le garantis pas.

Alors, quelle était la question?

Cela a été un sujet populaire sur U&L; il est abordé dans les questions suivantes:

… Et cela ne compte pas le super utilisateur ou Ask Ubuntu. J'ai incorporé une grande partie des informations des réponses aux questions ci-dessus ici dans cette réponse, mais pas toutes. (C'est-à-dire, pour plus d'informations, lisez les questions ci-dessus et leurs réponses.)

P.S. Je n'ai aucune affiliation avec le blog que j'ai cité ci-dessus.

64
Scott

Plus d'observations sur ;, &, ( et )

  • Notez que certaines des commandes de la réponse de terdon peuvent être nulles. Par exemple, vous pouvez dire

    command1 ;
    

    (sans command2). Cela équivaut à

    command1
    

    (c'est-à-dire qu'il exécute simplement command1 au premier plan et attend qu'il se termine. En comparaison,

    command1 &
    

    (sans command2) Va lancer command1 en arrière-plan, puis émettez immédiatement une autre invite Shell.

  • Par contre, command1 &&, command1 ||, et command1 | ça n'a aucun sens. Si vous en saisissez une, le Shell supposera (probablement) que la commande se poursuit sur une autre ligne. Il affichera l'invite du shell secondaire (suite), qui est normalement définie sur >, et continuez à lire. Dans un script Shell, il va simplement lire la ligne suivante et l'ajouter à ce qu'il a déjà lu. (Attention: ce n'est peut-être pas ce que vous voulez.)

    Remarque: certaines versions de certains shells peuvent traiter ces commandes incomplètes comme des erreurs. Dans de tels cas (ou, en fait, dans tout cas où vous avez une commande longue), vous pouvez mettre une barre oblique inverse (\) à la fin d'une ligne pour indiquer au shell de continuer à lire la commande sur une autre ligne:

    command1  &&  \
    command2
    

    ou

    find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \
                            -newer some_existing_file -user fred -readable -print
    
  • Comme le dit terdon, ( et ) peut être utilisé pour regrouper des commandes. L'affirmation selon laquelle ils "ne sont pas vraiment pertinents" pour cette discussion est discutable. Certaines des commandes de la réponse de terdon peuvent être la commande groups. Par exemple,

    ( command1 ; command2 )  &&  ( command3; command4 )
    

    est ce que ca:

    • Courir command1 et attendez qu'il se termine.
    • Ensuite, quel que soit le résultat de l'exécution de cette première commande, exécutez command2 et attendez qu'il se termine.
    • Puis si command2 réussi,

      • Courir command3 et attendez qu'il se termine.
      • Ensuite, quel que soit le résultat de l'exécution de cette commande, exécutez command4 et attendez qu'il se termine.

      Si command2 a échoué, arrêtez de traiter la ligne de commande.

  • Parenthèses extérieures, | se lie très étroitement, donc

    command1 | command2 || command3
    

    est équivalent à

    ( command1 | command2 )  ||  command3
    

    et && et || lier plus fort que ;, donc

    command1 && command2 ; command3
    

    est équivalent à

    ( command1 && command2 ) ;  command3
    

    c'est à dire., command3 sera exécuté quel que soit l'état de sortie de command1 et/ou command2.