Come eseguire i comandi come in una coda


42

Devo fare molte copie di vari file in varie cartelle. Posso aggiungere tutti i miei comandi di copia a uno script bash e quindi eseguirlo, ma poi devo aspettare fino al termine se voglio aggiungere altri comandi a quella "coda" di copiatura.

C'è un modo in cui posso eseguire i comandi come coda e una sorta di aggiungere più comandi a quella coda mentre le cose sono in esecuzione?

Spiegato in modo diverso, voglio iniziare un'attività di lunga durata. Mentre è in esecuzione, voglio avviarne un altro che in realtà non si avvia fino a quando non viene eseguito il primo. E poi aggiungine un altro dopo l'ultimo e così via. È possibile in qualche modo?


3
Posso suggerirti di accettare una risposta che risolva il tuo problema. Sembra che ce ne siano un paio che funzionerebbero per te.
holmb,

2
Sì, puoi, e ho voluto. Il problema è che l'ho chiesto quando lavoravo in un'azienda che utilizzava Mac. Non sto più lavorando lì, né ho un Mac da solo, quindi non ho modo di testare nessuno di questi. Non mi sento a mio agio nel segnarne una come risposta quando non sono in grado di testare che funzioni davvero, quindi per ora ho sperato che le persone votassero quelle risposte che trovavano bene per loro in modo che la "risposta" potesse emergere in quel modo anziché.
Svish,

Risposte:


25

L' atutilità, nota per l'esecuzione di comandi in un determinato momento, ha anche una funzione di coda e può essere richiesta di avviare subito l'esecuzione dei comandi. Legge il comando per l'esecuzione dall'input standard.

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

Il batchcomando è equivalente a at -q b -m now( -msignifica che l'output del comando, se presente, ti verrà inviato per posta, come cron fa). Non tutte le varianti di unix supportano i nomi delle code ( -q myqueue); potresti essere limitato a una singola coda chiamata b. Linux atè limitato ai nomi di coda a lettera singola.


2
Almeno su Linux questo non sembra attendere il completamento del comando precedente.
wds,

@wds Funziona per me su Debian wheezy. Dove e come si è rotto per te? Nota che aspetta solo che il comando finisca; se il comando avvia sottoprocessi che sopravvivono ai loro genitori, at non attende i sottoprocessi.
Gilles 'SO- smetti di essere malvagio' il

Ho provato su CentOS 6. Ho provato con un semplice "sleep x". Non sono sicuro di cosa faccia per eseguirlo, ma presumo che avvii una subshell e che la subshell dovrebbe attendere il risultato (una chiamata sleep non restituisce immediatamente).
wds,

Oltre ai nomi delle code, "ora" non appare neanche standard: bash su Ubuntu se ne lamenta ...
Winwa

@winwaed Oh, nownon è -t now, scusa. Non avevo notato di aver sbagliato nel codice di esempio.
Gilles 'SO- smetti di essere malvagio' il

23

Aggiungi &alla fine del comando per inviarlo in background, quindi waitsu di esso prima di eseguire il successivo. Per esempio:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...

2
Sìì! questa sintassi è fantastica quando hai dato il via a qualcosa e poi vai a cercare StackOverflow per un modo per mettere in coda qualcosa dopo di esso
Erik Aronesty,

2
Se nel frattempo cambi idea su uno di questi comandi, cosa si può fare per interrompere wait? Sembra impervio a tutti i miei omicidi.
Ken Williams,

1
Devi solo uccidere il comando. Il waitsemplicemente interrompe il lavoro fino al completamento di quelli precedenti.
Gert Sønderby,

Funzionerà con nohup?
Michael,

11

Entrambe le soluzioni di Brad e Mankoff sono buoni suggerimenti. Un altro che è simile a una combinazione di entrambi sarebbe usare GNU Screen per implementare la tua coda. Questo ha il vantaggio di poter essere eseguito in background, è possibile verificarlo ogni volta e accodare i nuovi comandi li incolla nel buffer per essere eseguiti dopo l'uscita dei comandi precedenti.

Prima corsa:

$ screen -d -m -S queue

(per inciso, ora è un buon momento per giocare con dei fantastici file .rackrc )

Ciò genererà una sessione di schermata di sfondo per te chiamata coda.

Ora, fai la coda di tutti i comandi che vuoi:

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

Sto facendo più comandi in quanto sopra solo per i test. Il tuo caso d'uso dovrebbe probabilmente apparire più simile a:

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

Nota che la "^ M" nella mia riga sopra è un modo per ottenere una nuova riga incorporata che verrà interpretata in seguito dopo che lo schermo lo inserisce nella tua shell bash esistente. Usa "CTL-V" per ottenere quella sequenza.

Sarebbe abbastanza facile creare alcuni semplici script di shell per automatizzare ciò e mettere in coda i comandi. Quindi, ogni volta che si desidera verificare lo stato della coda in background, si ricollega tramite:

screen -S queue -r

Tecnicamente, non hai nemmeno bisogno di nominare la tua sessione schermo e funzionerà bene, ma una volta che ti sarai agganciato, vorrai comunque lasciarne uno in esecuzione tutto il tempo. ;-)

Naturalmente, se lo fai, un altro buon modo per farlo sarebbe quello di nominare una delle "code" attuali di Windows e usare:

screen -S queue -p queue -X stuff "command"

Sembra che sostanzialmente "digiti" il comando nella sessione dello schermo in esecuzione, in modo che quando la shell acquisisce nuovamente il controllo al termine del primo comando, questo comando verrà avviato. In altre parole, equivale alla soluzione di @ mankoff, ma utilizza uno strumento più elaborato per eseguire la digitazione.
Ken Williams,

Praticamente - la differenza principale è che la soluzione dello schermo supporta una migliore automazione. Puoi fare alcune cose a mano, altre cose con gli script, il tutto con una semplice interfaccia.
Giordania,

@Jordan Molto bene, funziona per me. Ma cos'è "roba" per dopo -X?
Konstantin,

@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - riempi tutto ciò che segue nel terminale come se lo scrivessi)
Giordania,

@Jordan Oh, vedo ora. È un comando Ho pensato che fosse una stringa arbitraria.
Konstantin

8

C'è un'utilità che ho usato con grande successo esattamente per il caso d'uso che stai descrivendo. Di recente ho spostato il mio laptop principale su un nuovo hardware, il che ha comportato lo spostamento di alcuni file su un NAS e il resto sulla nuova macchina.

È così che l'ho fatto.

  1. Configurare tutte le macchine coinvolte in una connessione di rete in modo che possano raggiungersi.

  2. Sul computer da cui si stanno spostando i file (di seguito chiamato computer di origine), installare rsync con apt-get install rsynce il Task Spooler secret-sauce (sito Web http://vicerveza.homeunix.net/~viric/soft/ts/ ). Se sei su Debian il nome del pacchetto tsè task-spoolere il file eseguibile viene rinominato tspper evitare lo scontro di nomi con l' tseseguibile dal moreutilspacchetto. Il pacchetto Debian collegato dal sito Web è perfettamente installabile su Ubuntu con dpkg -i task-spooler_0.7.3-1_amd64.debo simili.

  3. Assicurarsi inoltre che tutte le macchine abbiano SSH installato apt-get install openssh-server. Sul computer di origine è necessario impostare SSH per consentire l'accesso senza password ai computer di destinazione. Il metodo più utilizzato sarà l'autenticazione con chiave pubblica con ssh-agent(vedi https://www.google.se/search?q=ssh+public+key+authentication per esempi di questo), ma a volte uso un metodo più semplice che funziona solo anche con autenticazione password. Aggiungi quanto segue alla configurazione del tuo client SSH (o ~/.ssh/configo /etc/ssh/ssh_config):

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

    Quindi aprire un terminale sul computer di origine, accedere con ssh target1, autenticarsi come al solito, quindi lasciare aperto questo terminale. Si noti che esiste un file socket denominato ~/.ssh/master-user@target1:22, questo è il file che manterrà aperta una sessione master autenticata e consentirà le successive connessioni senza password user(a condizione che la connessione utilizzi lo stesso nome host e porta di destinazione).

    A questo punto è necessario verificare che sia possibile accedere senza che sia richiesta l'autenticazione per i computer di destinazione.

  4. Ora esegui ts rsync -ave ssh bigfile user@target1:per un singolo file o ts rsync -ave ssh bigdir user@target1:per una directory. Con rsync è importante non includere una barra finale nella directory ( bigdirrispetto a bigdir/) o rsync supporrà che intendevi l'equivalente di bigdir/*nella maggior parte degli altri strumenti.

    Task Spooler restituirà il prompt e consente di mettere in coda molti di questi comandi in successione. Ispeziona la coda di esecuzione con tssenza argomenti.

Task Spooler ha molte funzionalità come riorganizzare la coda di esecuzione, eseguire un lavoro specifico solo se un altro lavoro è stato eseguito correttamente, ecc. Consultare la guida con ts -h. A volte ispeziono l'output del comando mentre è in esecuzione ts -c.

Esistono altri metodi per farlo, ma nel tuo caso d'uso includono tutti Task Spooler. Ho scelto di utilizzare rsync su SSH per preservare i timestamp dei file, che la copia su SMB non avrebbe.


grazie per la tua risposta di cui non ero a conoscenza ts, sembra molto potente e facile da usare, appena biforcato su github autotoolized e debianized.
Alex,

@Alex Ho dato un'occhiata al tuo fork ed ero particolarmente interessato a ciò che è stato fatto per quanto riguarda l'autotooling e la debianizzazione della fonte originale, e guardando i tuoi commit non era prontamente disponibile come diff rispetto alla fonte originale. Ti dispiacerebbe forzare a spingere nuovi commit che nel passaggio 1. importa la fonte originale e 2. impegna con le tue aggiunte? Aiuterà qualcuno a navigare nella tua fonte per fidarti (più facilmente) delle tue aggiunte alla fonte originale. Questo è uno dei pochi pacchetti che non installo da un PPA o simile, quindi sarebbe bello costruirlo da solo con il tuo fork.
holmb,

sai che devo cambiarmi gli occhiali, ho perso la parte della tua risposta sull'esistente task-spooler:-) ... comunque, questo nel tuo commento è un ottimo suggerimento, lo farò molto presto.
Alex,

fatto, cancellato e ricreato il repository con commit progressivi, solo un paio di errori (dovrebbe metabolizzare più commit locali prima del push)
Alex

4

Ho bisogno di cose del genere anche abbastanza frequentemente. Ho scritto una piccola utility chiamata afterche esegue un comando ogni volta che è terminato qualche altro processo. Sembra così:

#!/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;

Quindi eseguirlo in questo modo:

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

Il grande vantaggio di questo rispetto ad alcuni altri approcci è che il primo lavoro non deve essere eseguito in alcun modo speciale, è possibile seguire qualsiasi processo con il nuovo lavoro.


Bel copione @ken. Anche se consiglierei di provare Task Spooler poiché sembra che questo richieda di tanto in tanto. Vedi la mia risposta
holmb,

Sembra pulito, grazie. L'unica cosa che manca a TS sembra essere la capacità di seguire lavori che non erano stati avviati in TS. Anche se immagino che potresti iniziare un nuovo lavoro TS il cui scopo è solo quello di attendere il completamento di un processo esistente.
Ken Williams,

Infatti. Questo è un aspetto utile della tua soluzione.
holmb,

2

Command1 && Command2

  • "&&" indica che command2 viene eseguito solo dopo che command1 termina con un codice di ritorno 0
  • Nota: a || significa che command2 viene eseguito solo dopo che command1 termina con un codice di ritorno diverso da 0

1

È possibile semplicemente aggiungere comandi alla riga di comando della shell che sta già eseguendo un comando.

Digita con attenzione o digita altrove, verifica e taglia e incolla. Anche se il comando corrente sta emettendo righe di output, è comunque possibile digitare qualcosa di nuovo, premere invio e verrà eseguito quando tutti i comandi in esecuzione o immessi prima del completamento.

Segue l'esempio. Puoi tagliare e incollare il tutto o inserire i comandi successivi mentre il 1 ° è in esecuzione (se digiti davvero molto velocemente), o più probabilmente mentre il 2 ° è in esecuzione .:

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

0

il modo più hacker che mi viene in mente è di mantenere lo "stato" della coda con semplici file di blocco in / tmp. quando file1.sh sta eseguendo i suoi comandi cp, manterrà un file di blocco (o hardlink) in / tmp. al termine, cancellare il file. ogni altro script dovrà cercare in / tmp i file di blocco con lo schema di denominazione scelto. naturalmente questo è soggetto a tutti i tipi di fallimenti, ma è banale da installare ed è fattibile completamente all'interno di bash.


Difficile da usare in pratica, poiché richiede che ogni lavoro che si desidera eseguire debba essere modificato per mantenere i file di blocco e che debba conoscere (o accettare come parametri) quali file di blocco utilizzare. Quando possibile, è meglio avere la coda esterna al lavoro.
Ken Williams,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.