web-dev-qa-db-fra.com

Exécuter plusieurs commandes sur une ligne dans un shell

Dis que j'ai un fichier /templates/Apple et que je veux

  1. mettez-le à deux endroits différents, puis
  2. enlever l'original.

Ainsi, /templates/Apple sera copié dans /templates/used ET /templates/inuse, puis je souhaite supprimer l’original.

Est-ce que cp est le meilleur moyen de le faire, suivi de rm? Ou y a-t-il un meilleur moyen?

Je veux tout faire en une seule ligne, je pense donc que cela ressemblerait à quelque chose comme:

cp /templates/Apple /templates/used | cp /templates/Apple /templates/inuse | rm /templates/Apple

Est-ce la syntaxe correcte?

328
John Redyns

Vous utilisez | (pipe) pour diriger la sortie d'une commande dans une autre commande. Ce que vous recherchez, c'est l'opérateur && pour exécuter la commande suivante uniquement si la commande précédente a réussi:

cp /templates/Apple /templates/used && cp /templates/Apple /templates/inuse && rm /templates/Apple

Ou

cp /templates/Apple /templates/used && mv /templates/Apple /templates/inuse

Pour résumer (de manière non exhaustive) les opérateurs/séparateurs de commande de bash:

  • | conduit (pipelines) la sortie standard (stdout) d'une commande à l'entrée standard d'une autre. Notez que stderr continue à utiliser sa destination par défaut, quel qu’il soit.
  • |&pipe à la fois stdout et stderr d'une commande dans l'entrée standard d'une autre. Très utile, disponible dans les versions 4 et supérieures de bash.
  • && exécute la commande de droite de && uniquement si la précédente a réussi.
  • || exécute la commande de droite de || seulement la précédente a échoué.
  • ; exécute la commande de droite de ; toujours, que la commande précédente ait abouti ou non. Sauf si set -e a déjà été appelé, ce qui entraîne l'échec de bash en cas d'erreur.
668
Maxim Egorushkin

Pourquoi pas cp à l'emplacement 1, puis mv à l'emplacement 2. Cela permet de "supprimer" l'original.

Et non, ce n'est pas la syntaxe correcte. | est utilisé pour "diriger" la sortie d'un programme et la transformer en entrée pour le programme suivant. Ce que vous voulez, c'est ;, qui sépare plusieurs commandes.

cp file1 file2 ; cp file1 file3 ; rm file1

Si vous avez besoin que les commandes individuelles DOIVENT réussir avant que le prochain puisse être démarré, utilisez plutôt &&:

cp file1 file2 && cp file1 file3 && rm file1

De cette façon, si l'une des commandes cp échoue, le rm ne s'exécutera pas.

70
Marc B

Notez que cp A B; rm A est exactement mv A B. Ce sera plus rapide aussi, car vous n'avez pas à copier les octets (en supposant que la destination se trouve sur le même système de fichiers), il suffit de renommer le fichier. Donc, vous voulez cp A B; mv A C

8
glenn jackman

Une autre option est de taper Ctrl+VCtrl+J à la fin de chaque commande.

Exemple (remplacez # par Ctrl+VCtrl+J):

$ echo 1#
echo 2#
echo 3

Sortie:

1
2
3

Cela exécutera les commandes peu importe si les précédentes ont échoué.

Identique à: echo 1; echo 2; echo 3

Si vous souhaitez arrêter l'exécution sur des commandes ayant échoué, ajoutez && à la fin de chaque ligne, à l'exception de la dernière.

Exemple (remplacez # par Ctrl+VCtrl+J):

$ echo 1 &&#
failed-command &&#
echo 2

Sortie:

1
failed-command: command not found

Dans zsh, vous pouvez également utiliser Alt+Enter ou Esc+Enter au lieu de Ctrl+VCtrl+J

6
Eyal Levin

Essaye ça..

cp /templates/Apple /templates/used && cp /templates/Apple /templates/inuse && rm /templates/Apple

2
Peter

L'utilisation de tuyaux me semble bizarre. Quoi qu'il en soit, vous devez utiliser l'opérateur logique and Bash:

$ cp /templates/Apple /templates/used && cp /templates/Apple /templates/inuse && rm /templates/apples

Si les commandes cp échouent, la rm ne sera pas exécutée.

Vous pouvez également créer une ligne de commande plus élaborée à l’aide d’une boucle for et de cmp.

1
Renaud