Comment exécuter des commandes comme dans une file d'attente

43

Je dois beaucoup copier de divers fichiers dans différents dossiers. Je peux ajouter toutes mes commandes de copie à un script bash, puis l'exécuter, mais je dois attendre jusqu'à ce que l'opération se termine si je veux ajouter d'autres commandes à cette "file" de copie.

Est-il possible d'exécuter des commandes en tant que file d'attente et d'ajouter d'autres commandes à cette file d'attente pendant le fonctionnement?

Expliqué d'une manière différente, je veux démarrer une tâche longue. Pendant que cela fonctionne, je veux en lancer un autre qui ne démarre réellement que lorsque le premier est terminé. Et puis ajoutez un autre après ce dernier et ainsi de suite. Est-ce possible d'une certaine manière?

Svish
la source
3
Puis-je vous suggérer d’accepter une réponse qui résout votre problème? Il semble que quelques-uns d’entre eux fonctionneraient pour vous.
Holmb
2
Oui vous pouvez, et j'ai voulu. Le problème, c’est que j’ai posé cette question lorsque je travaillais dans une entreprise qui utilisait des Mac. Je ne travaille plus là-bas et je n'ai pas non plus de Mac moi-même, je n'ai donc aucun moyen de les tester. Ne vous sentez pas à l'aise de marquer cette réponse comme une réponse lorsque je ne suis pas en mesure de vérifier si cela fonctionne réellement. J'ai donc pour l'instant espéré que les gens votent pour ces réponses qu'ils trouvent bien pour eux, afin que la "réponse" puisse émerger de cette manière. au lieu.
Svish

Réponses:

26

L' atutilitaire, mieux connu pour l'exécution de commandes à une heure précise, possède également une fonction de file d'attente et peut être invité à démarrer l'exécution de commandes maintenant. Il lit la commande à exécuter à partir de l'entrée standard.

echo 'command1 --option arg1 arg2' | at -q myqueue now
echo 'command2 ...' | at -q myqueue now

La batchcommande est équivalente à at -q b -m now( -mce qui signifie que le résultat de la commande, le cas échéant, vous sera envoyé, comme le fait cron). Toutes les variantes Unix ne prennent pas en charge les noms de file d'attente ( -q myqueue); vous pouvez être limité à une seule file d'attente appelée b. Linux atest limité aux noms de file d'attente d'une seule lettre.

Gilles, arrête de faire le mal
la source
2
Sur linux au moins, cela ne semble pas attendre la fin de la commande précédente.
wds
@wds fonctionne pour moi sur Debian Wheezy. Où et comment cela s'est-il cassé pour vous? Notez qu'attend seulement que la commande se termine; si la commande lance des sous-processus qui survivent à leur parent, at n'attend pas les sous-processus.
Gilles 'SO- arrête d'être méchant'
J'ai essayé sur CentOS 6. J'ai testé avec un simple "sommeil x". Je ne sais pas trop ce qu'il faut faire pour l'exécuter, mais je suppose que cela démarre un sous-shell et ce sous-shell doit attendre le résultat (un appel en veille ne revient pas immédiatement).
wds
En plus des noms de file d'attente, 'maintenant' n'apparaît pas non plus comme standard: bash sur Ubuntu s'en plaint…
winwaed
@winwaed Oh, c'est now, non -t now, désolé. Je n'avais pas remarqué que j'avais tort dans l'exemple de code.
Gilles 'SO- arrête d'être méchant'
24

Ajoutez &à la fin de votre commande pour l'envoyer en arrière-plan, puis waitsur avant de lancer la suivante. Par exemple:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...
Vortico
la source
2
Yay! cette syntaxe est géniale quand on a lancé quelque chose et qu'on a ensuite regardé stackoverflow pour un moyen de faire la queue après quelque chose
Erik Aronesty
2
Si vous changez d'avis sur l'une de ces commandes entre-temps, que peut-on faire pour abandonner la wait? Il semble insensible à tous mes coups de feu.
Ken Williams
1
Vous venez de tuer la commande. Le waitsimplement arrête le travail jusqu'à la fin des précédents
Gert Sønderby le
Cela fonctionnera-t-il nohup?
Michael
11

Les solutions de Brad et Mankoff sont toutes deux de bonnes suggestions. Une autre combinaison semblable à une combinaison des deux consisterait à utiliser GNU Screen pour implémenter votre file d'attente. Cela présente l’avantage de pouvoir s’exécuter en arrière-plan. Vous pouvez le vérifier à tout moment et les mettre en file d’attente de nouvelles commandes ne fait que les coller dans la mémoire tampon à exécuter après la fermeture des commandes précédentes.

Première exécution:

$ screen -d -m -S queue

(accessoirement, le moment est venu de jouer avec des fichiers géniaux. screenrc )

Cela créera une session d'écran en arrière-plan pour votre file d'attente nommée.

Maintenant, mettez en file d'attente autant de commandes que vous le souhaitez:

screen -S queue -X stuff "echo first; sleep 4; echo second^M"

Je fais plusieurs commandes dans ce qui précède juste pour les tests. Votre cas d'utilisation ressemblerait probablement davantage à:

screen -S queue -X stuff "echo first^M"
screen -S queue -X stuff "echo second^M"

Notez que le "^ M" dans ma ligne ci-dessus est un moyen d’obtenir une nouvelle ligne incorporée qui sera interprétée plus tard après que l’écran l’a insérée dans votre shell bash existant. Utilisez "CTL-V" pour obtenir cette séquence.

Il serait assez facile de créer de simples scripts shell pour automatiser cela et mettre les commandes en file d'attente. Ensuite, chaque fois que vous souhaitez vérifier l'état de votre file d'attente en arrière-plan, vous vous reconnectez via:

screen -S queue -r

Techniquement, vous n'avez même pas besoin de nommer votre session écran et cela fonctionnera bien, mais une fois que vous y êtes accroché, vous voudrez toujours en laisser une en cours d'exécution tout le temps. ;-)

Bien sûr, si vous faites cela, un autre bon moyen de le faire serait de nommer l’une des "files" de windows actuelles et d’utiliser:

screen -S queue -p queue -X stuff "command"
Jordan
la source
Il semble qu’il s’agisse en fait simplement de "taper" la commande dans la session d’écran d’exécution, de sorte que lorsque le shell reprend le contrôle à la fin de la première commande, cette commande est lancée. En d'autres termes, cela équivaut à la solution de @ mankoff, mais utilise un outil plus sophistiqué pour la frappe.
Ken Williams
En gros, la principale différence est que la solution d'écran prend en charge une meilleure automatisation. Vous pouvez faire certaines choses à la main, d'autres avec des scripts, le tout avec une interface simple.
Jordanie
@ Jordan Très bien, cela fonctionne pour moi. Mais à quoi servent les "trucs" après -X?
Konstantin
@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - tout ce qui suit dans le terminal comme si vous l'aviez tapé)
Jordanie
@ Jordan Oh, je vois maintenant. C'est une commande. Je pensais que c'est une chaîne arbitraire.
Konstantin
8

Il existe un utilitaire que j'ai utilisé avec un grand succès pour le cas d'utilisation que vous décrivez. Récemment, j'ai transféré mon ordinateur portable principal sur un nouveau matériel, ce qui impliquait le transfert de certains fichiers sur un NAS et le reste sur la nouvelle machine.

Voici comment je l'ai fait.

  1. Configurez toutes les machines impliquées avec une connexion réseau afin qu'elles puissent se joindre.

  2. Sur la machine à partir de laquelle vous déplacez des fichiers (ci-après appelée machine source), installez rsync avec apt-get install rsyncet le spool de tâches secret-sauce (site Web http://vicerveza.homeunix.net/~viric/soft/ts/ ). Si vous êtes sur Debian, le nom du paquet tsest is task-spooleret l'exécutable est renommé afin tspd'éviter tout conflit de nom avec l' tsexécutable du moreutilspaquet. Le paquet Debian lié au site Web est parfaitement installable sur Ubuntu avec dpkg -i task-spooler_0.7.3-1_amd64.debou similaire.

  3. Assurez-vous également que SSH est installé sur toutes les machines apt-get install openssh-server. Sur la machine source, vous devez configurer SSH pour permettre la connexion sans mot de passe aux machines cibles. La méthode la plus utilisée est l’authentification par clé publique ssh-agent(voir https://www.google.se/search?q=ssh+public+key+authentication pour des exemples), mais j’utilise parfois une méthode plus simple qui fonctionne simplement. aussi bien avec authentification par mot de passe. Ajoutez les éléments suivants à la configuration de votre client SSH ( ~/.ssh/configou bien /etc/ssh/ssh_config):

    Host *
         ControlMaster auto
         ControlPath ~/.ssh/master-%r@%h:%p
    

    Ouvrez ensuite un terminal sur la machine source, connectez-vous avec ssh target1, authentifiez-vous comme d'habitude, puis laissez ce terminal ouvert. Notez qu'il existe un fichier de socket nommé ~/.ssh/master-user@target1:22. Il s'agit du fichier qui gardera une session maître authentifiée ouverte et autorisera les connexions sans mot de passe ultérieures user(tant que la connexion utilise les mêmes nom d'hôte et port cibles).

    À ce stade, vous devez vérifier que vous pouvez vous connecter sans être invité à vous authentifier auprès des ordinateurs cibles.

  4. Maintenant, exécutez ts rsync -ave ssh bigfile user@target1:pour un seul fichier ou ts rsync -ave ssh bigdir user@target1:pour un répertoire. Avec rsync, il est important de ne pas inclure de barre oblique finale dans le répertoire ( bigdirpar rapport à bigdir/), sinon rsync supposera que vous entendiez l'équivalent de la bigdir/*plupart des autres outils.

    Le spouleur de tâches renverra l'invite et vous permettra de mettre en file d'attente plusieurs de ces commandes. Inspectez la file d'attente avec tssans arguments.

Le spouleur de tâches possède de nombreuses fonctionnalités telles que la réorganisation de la file d'attente, l'exécution d'un travail spécifique uniquement si un autre travail a été exécuté avec succès, etc. Consultez l'aide avec ts -h. J'inspecte parfois la sortie de la commande pendant son exécution ts -c.

Il existe d'autres méthodes pour ce faire, mais pour votre cas d'utilisation, elles incluent toutes le spouleur de tâches. J'ai choisi d'utiliser rsync sur SSH pour préserver les horodatages des fichiers, ce que la copie sur SMB n'aurait pas.

holmb
la source
merci pour votre réponse que je ne connaissais pas ts, qui semble très puissant et facile à utiliser, mais que je viens de bifurquer sur github autotoolized et debianized.
Alex
@ Alex, j’ai jeté un coup d’œil à votre fork et j’ai été particulièrement intéressé par ce qui a été fait en ce qui concerne l’autotooling et la debianisation de la source originale. En regardant vos commits, il n’était pas facilement disponible en tant que diff de la source originale. Est-ce que cela vous dérangerait de forcer de nouveaux commits qui à l'étape 1. importe la source d'origine et 2. commettent vos ajouts? Cela aidera quelqu'un qui navigue dans votre source à (plus facilement) faire confiance à vos ajouts à la source d'origine. C’est l’un des rares paquets que je n’installe pas à partir d’un PPA ou similaire, il serait donc agréable de le construire moi-même avec votre fork.
Holmb
vous savez que je dois changer mes lunettes, j'ai perdu la partie de votre réponse concernant l'existant task-spooler:-) ... de toute façon, c'est dans votre commentaire, c'est une très bonne suggestion, je le ferai très bientôt.
Alex
done, supprimé et recréé le référentiel avec des commits progressifs, seulement quelques erreurs (il faudrait métaboliser plus de commits locaux avant de pousser)
Alex
4

J'ai besoin de trucs comme ça assez souvent aussi. J'ai écrit un petit utilitaire appelé afterqui exécute une commande lorsqu'un autre processus est terminé. Cela ressemble à ceci:

#!/usr/bin/perl

my $pid = shift;
die "Usage: $0 <pid> <command...>" unless $pid =~ /^\d+$/ && @ARGV;

print STDERR "Queueing process $$ after process $pid\n";
sleep 1 while -e "/proc/$pid";
exec @ARGV;

Vous le lancez alors comme ceci:

% command1 arg1 arg2 ...  # creates pid=2853
% after 2853 command2 arg1 arg2 ... # creates pid=9564
% after 9564 command3 arg1 arg2 ...

Le gros avantage de cette approche par rapport à d’autres approches est que le premier travail n’a pas besoin d’être exécuté de manière particulière, vous pouvez suivre n’importe quel processus avec votre nouvel emploi.

Ken Williams
la source
Beau script @ken. Bien que je vous recommande d'essayer Task Spooler car vous semblez en avoir besoin de temps en temps. Voir ma réponse.
Holmb
Semble soigné, merci. La seule chose qui manque à TS semble être la capacité de suivre des travaux qui n’ont pas été commencés sous TS. Bien que je suppose que vous puissiez commencer un nouveau travail TS dont le but est simplement d’attendre la fin d’un processus existant.
Ken Williams
Effectivement. C'est un aspect utile de votre solution.
Holmb
2

Command1 && Command2

  • "&&" signifie que commande2 ne s'exécute qu'après que commande1 se termine avec un code retour égal à 0
  • Note: a || signifie que commande2 ne s'exécute qu'après que commande1 se termine avec un code retour autre que 0
Naftuli
la source
1

Vous pouvez simplement ajouter des commandes à la ligne de commande du shell qui exécute déjà une commande.

Tapez avec soin ou tapez-le ailleurs, vérifiez et copiez-collez. Même si la commande en cours crache des lignes de sortie, vous pouvez toujours taper quelque chose de nouveau, appuyer sur entrée et elle sera exécutée lorsque toutes les commandes seront exécutées ou entrées avant la fin.

L'exemple suit. Vous pouvez couper et coller le tout, ou entrer les commandes suivantes pendant que la 1re est en cours d'exécution (si vous tapez très vite), ou plus probablement pendant que la 2e est en cours d'exécution .:

echo "foo"
sleep 10; echo "bar"
sleep 3
echo "baz"

la source
0

La façon la plus commode de penser est de maintenir "l'état" de la file d'attente avec de simples fichiers de verrouillage dans / tmp. Lorsque file1.sh exécute ses commandes cp, il conservera un fichier de verrouillage (ou lien dur) dans / tmp. quand c'est fait, effacez le fichier. Tous les autres scripts devront rechercher dans le fichier / tmp les fichiers verrouillés avec le schéma de nommage choisi. Naturellement, cela est sujet à toutes sortes d'échecs, mais son installation est triviale et tout à fait réalisable au sein de Bash.

Brad Clawsie
la source
Difficile à utiliser dans la pratique, car elle exige que chaque emploi que vous voulez exécuter doit être modifié pour conserver les fichiers de verrouillage, et a besoin de savoir (ou accepter en tant que paramètres) qui verrouiller les fichiers à utiliser. Dans la mesure du possible, il est préférable que la file d'attente soit externe au travail.
Ken Williams