Jak uruchamiać polecenia jak w kolejce

42

Muszę dużo kopiować różne pliki do różnych folderów. Mogę dodać wszystkie moje polecenia kopiowania do skryptu bash, a następnie uruchomić je, ale muszę poczekać, aż zakończy się, jeśli chcę dodać więcej poleceń do tej „kolejki” kopiowania.

Czy istnieje sposób, w jaki mogę uruchamiać polecenia jako kolejkę i dodawać kolejne polecenia do tej kolejki podczas działania?

Wyjaśnione w inny sposób, chcę rozpocząć długotrwałe zadanie. Podczas działania chcę uruchomić inny, który nie uruchomi się, dopóki nie zostanie ukończony pierwszy. A potem dodaj kolejne po tym ostatnim i tak dalej. Czy to jest jakoś możliwe?

Svish
źródło
3
Czy mogę zasugerować odpowiedź, która rozwiązuje problem? Wygląda na to, że istnieje kilka z nich, które będą dla ciebie odpowiednie.
holmb
2
Tak, możesz i chciałem. Problem polega na tym, że zapytałem o to, pracując w firmie używającej komputerów Mac. Już tam nie pracuję, nie mam też komputerów Mac, więc nie mam możliwości przetestowania żadnego z nich. Nie czuję się dobrze, zaznaczając jedną jako odpowiedź, gdy nie jestem w stanie sprawdzić, czy to rzeczywiście działa, więc mam nadzieję, że ludzie głosują na te odpowiedzi, które uznają za dobre dla nich, aby „odpowiedź” mogła się w ten sposób pojawić zamiast.
Svish

Odpowiedzi:

25

atNarzędzie, najlepiej znany z systemem poleceń w określonym czasie, posiada również funkcję kolejki i może zostać poproszony, aby rozpocząć poleceń uruchomionych teraz. Odczytuje polecenie uruchomienia ze standardowego wejścia.

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

batchKomenda jest równoważna at -q b -m now( -mczyli wyjście polecenia, jeśli w ogóle, zostanie wysłany do Ciebie, jak cron robi). Nie wszystkie warianty unixowe obsługują nazwy kolejek ( -q myqueue); możesz być ograniczony do jednej kolejki o nazwie b. Linux atjest ograniczony do jednoliterowych nazw kolejek.

Gilles „SO- przestań być zły”
źródło
2
Na Linuksie przynajmniej nie wydaje się czekać na zakończenie poprzedniego polecenia.
wds
@wds Działa dla mnie przy wheezy Debiana. Gdzie i jak to się stało? Zauważ, że at tylko czeka na zakończenie polecenia; jeśli polecenie uruchomi podprocesy, które przeżyją ich rodzica, at nie czeka na podprocesy.
Gilles „SO- przestań być zły”
Próbowałem na CentOS 6. Testowałem z prostym „sleep x”. Nie jestem pewien, co to robi, aby to uruchomić, ale zakładam, że uruchamia podpowłokę i że podpowłoka powinna poczekać na wynik (wywołanie uśpienia nie wraca natychmiast).
wds
Oprócz nazw kolejek „teraz” również nie jest standardem: bash na ubuntu narzeka na to ...
wygrał
@winwaed O nownie -t now, przepraszam. Nie zauważyłem, że popełniłem błąd w przykładowym kodzie.
Gilles „SO- przestań być zły”
23

Dołącz &na końcu polecenia, aby wysłać go w tle, a następnie waitna nim przed uruchomieniem następnego. Na przykład:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...
Vortico
źródło
2
tak! ta składnia jest niesamowita, gdy coś wyrzuciłeś, a potem spojrzałeś na stackoverflow, aby znaleźć sposób na umieszczenie czegoś w kolejce po nim
Erik Aronesty,
2
Jeśli w międzyczasie zmienisz zdanie na temat któregoś z tych poleceń, co możesz zrobić, aby je przerwać wait? Wydaje się to niewrażliwe na wszystkie moje zabójcze strzały.
Ken Williams
1
Po prostu zabij rozkaz. Po waitprostu zatrzymuje pracę, dopóki poprzednie nie zakończą.
Gert Sønderby,
Czy to zadziała nohup?
Michael
11

Zarówno rozwiązania Brada, jak i Mankoffa są dobrymi sugestiami. Innym, który jest podobny do kombinacji obu z nich, byłoby użycie GNU Screen do implementacji twojej kolejki. Ma to tę zaletę, że może działać w tle, możesz to sprawdzić za każdym razem, a kolejkowanie nowych poleceń po prostu wkleja je do bufora, który ma zostać wykonany po wyjściu poprzednich poleceń.

Pierwszy bieg:

$ screen -d -m -S queue

(nawiasem mówiąc, teraz jest dobry moment na zabawę z niesamowitymi plikami . screenrc )

Spowoduje to utworzenie sesji ekranu w tle dla Ciebie o nazwie kolejka.

Teraz ustaw w kolejce tyle poleceń, ile chcesz:

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

Wykonuję wiele powyższych poleceń tylko w celu testowania. Twój przypadek użycia prawdopodobnie wyglądałby bardziej jak:

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

Zauważ, że „^ M” w moim wierszu powyżej to sposób na osadzenie nowej linii, która zostanie zinterpretowana później, gdy screen włoży ją do istniejącej powłoki bash. Użyj „CTL-V”, aby uzyskać tę sekwencję.

Stworzenie prostych skryptów powłoki do automatyzacji i ustawiania w kolejce poleceń byłoby dość łatwe. Następnie za każdym razem, gdy chcesz sprawdzić stan swojej kolejki w tle, ponownie dołączasz za pomocą:

screen -S queue -r

Technicznie rzecz biorąc, nie musisz nawet nazywać swojej sesji ekranowej i będzie ona działała dobrze, ale kiedy już ją wciągniesz, i tak będziesz chciał pozostawić ją uruchomioną przez cały czas. ;-)

Oczywiście, jeśli to zrobisz, innym dobrym sposobem na to byłoby nazwanie jednego z obecnych okien „kolejką” i użycie:

screen -S queue -p queue -X stuff "command"
Jordania
źródło
Wygląda na to, że to po prostu „wpisuje” polecenie w sesji uruchomionego ekranu, więc gdy powłoka ponownie przejmie kontrolę po zakończeniu pierwszego polecenia, to polecenie się uruchomi. Innymi słowy, jest to odpowiednik rozwiązania @ mankoff, ale używa bardziej zaawansowanego narzędzia do pisania.
Ken Williams
Prawie - główna różnica polega na tym, że rozwiązanie ekranowe obsługuje lepszą automatyzację. Możesz robić niektóre rzeczy ręcznie, inne za pomocą skryptów, a wszystko to za pomocą prostego interfejsu.
Jordan
@Jordan Bardzo dobrze, działa dla mnie. Ale czym są „rzeczy” po -X?
Konstantin
@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste (TL; DR - rzeczy, które pojawiają się w terminalu, jakbyś je wpisał)
Jordan
@Jordan Och, teraz rozumiem. To jest rozkaz. Myślałem, że to dowolny ciąg.
Konstantin
8

Istnieje narzędzie, które z dużym powodzeniem wykorzystałem do dokładnie opisywanego przypadku użycia. Niedawno przeniosłem mój główny laptop na nowy sprzęt, co wymagało przeniesienia niektórych plików na serwer NAS, a resztę na nowy komputer.

Tak to zrobiłem.

  1. Skonfiguruj wszystkie komputery związane z połączeniem sieciowym, aby mogły się nawzajem kontaktować.

  2. Na komputerze, z którego przenosisz pliki (zwanym dalej maszyną źródłową), zainstaluj program rsync zi narzędzie do buforowania zadańapt-get install rsync tajnego sosu (strona internetowa http://vicerveza.homeunix.net/~viric/soft/ts/ ). Jeśli korzystasz z Debiana, nazwa pakietu to i nazwa pliku wykonywalnego zostaje zmieniona, aby uniknąć kolizji nazwy z plikiem wykonywalnym z pakietu. Pakiet Debian, do którego odsyłacz znajduje się na stronie internetowej, można doskonale zainstalować na Ubuntu z podobnym oprogramowaniem.tstask-spoolertsptsmoreutilsdpkg -i task-spooler_0.7.3-1_amd64.deb

  3. Upewnij się również, że wszystkie maszyny mają zainstalowany SSH apt-get install openssh-server. Na komputerze źródłowym musisz skonfigurować SSH, aby umożliwić logowanie bez hasła do komputerów docelowych. Metodą najczęściej używaną jest uwierzytelnianie za pomocą klucza publicznego ssh-agent( przykłady: https://www.google.se/search?q=ssh+public+key+authentication ), ale czasami używam łatwiejszej metody, która działa tylko a także z uwierzytelnianiem za pomocą hasła. Dodaj następujące elementy do konfiguracji klienta SSH (albo ~/.ssh/configlub /etc/ssh/ssh_config):

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

    Następnie otwórz jeden terminal na maszynie źródłowej, zaloguj się ssh target1, uwierzytelnij jak zwykle, a następnie pozostaw ten terminal otwarty. Zauważ, że istnieje plik o nazwie o nazwie ~/.ssh/master-user@target1:22, jest to plik, który utrzyma uwierzytelnioną sesję główną otwartą i umożliwi kolejne połączenia bez hasła user(o ile połączenie używa tej samej docelowej nazwy hosta i portu).

    W tym momencie musisz sprawdzić, czy możesz się zalogować bez pytania o uwierzytelnienie na komputerach docelowych.

  4. Teraz uruchom ts rsync -ave ssh bigfile user@target1:dla pojedynczego pliku lub ts rsync -ave ssh bigdir user@target1:katalogu. W przypadku rsync ważne jest, aby nie dołączać ukośnika końcowego w katalogu (w bigdirporównaniu do bigdir/), ponieważ rsync zakłada, że ​​miałeś na myśli odpowiednik bigdir/*większości innych narzędzi.

    Bufor zadań zwróci monit i umożliwi kolejkowanie wielu z tych poleceń kolejno. Sprawdź kolejkę uruchamiania tsbez argumentów.

Bufor zadań ma wiele funkcji, takich jak zmiana kolejności kolejki uruchamiania, uruchamianie określonego zadania tylko wtedy, gdy inne zadanie przebiegło pomyślnie itp. Przejrzyj pomoc za pomocą ts -h. Czasami sprawdzam dane wyjściowe polecenia, gdy jest ono uruchomione ts -c.

Istnieją inne metody wykonania tej czynności, ale w twoim przypadku użycia wszystkie one zawierają bufor zadań. Wybieram użycie rsync przez SSH, aby zachować znaczniki czasu plików, których kopiowanie przez SMB nie miałoby.

holmb
źródło
dziękuję za twoją odpowiedź, o której nie wiedziałem ts, wydaje się bardzo potężny i łatwy w użyciu, po prostu rozwidlony na github z autoolowaniem i debianizacją.
Alex
@Alex Spojrzałem na twój widelec i byłem szczególnie zainteresowany tym, co zostało zrobione w odniesieniu do autouzupełniania i debianizacji oryginalnego źródła, a patrząc na twoje commity, nie było łatwo dostępne jako różnicę do oryginalnego źródła. Czy miałbyś coś przeciwko forsowaniu nowych zatwierdzeń, które w kroku 1. importują oryginalne źródło i 2. zatwierdzają z twoimi dodatkami? Pomoże to komuś przeglądając twoje źródło, aby (łatwiej) zaufać twoim dodatkom do oryginalnego źródła. Jest to jeden z niewielu pakietów, których nie instaluję z PPA lub podobnego, więc fajnie byłoby zbudować go samodzielnie za pomocą widelca.
holmb
wiesz, że muszę zmienić okulary, straciłem część twojej odpowiedzi na temat istniejącej task-spooler:-) ... tak czy inaczej, to w twoim komentarzu jest bardzo dobra sugestia, zrobię to wkrótce.
Alex
zrobione, usunięte i ponownie utworzone repozytorium z progresywnymi zatwierdzeniami, tylko kilka błędów (powinno przetworzyć więcej lokalnych zatwierdzeń przed wypchnięciem)
Alex
4

Potrzebuję też takich rzeczy dość często. Napisałem małe narzędzie o nazwie, afterktóre wykonuje polecenie za każdym razem, gdy zakończy się jakiś inny proces. To wygląda tak:

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

Następnie uruchom go tak:

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

Dużą zaletą tego w porównaniu z innymi podejściami jest to, że pierwsze zadanie nie musi być uruchamiane w żaden specjalny sposób, możesz śledzić każdy proces z nowym zleceniem.

Ken Williams
źródło
Niezły skrypt @ken. Chociaż polecam wypróbowanie bufora zadań, ponieważ wydaje się, że od czasu do czasu tego potrzebujesz. Zobacz moją odpowiedź.
holmb
Wygląda schludnie, dzięki. Jedyną rzeczą, której brakuje w TS, wydaje się być zdolność do wykonywania zadań, które nie zostały rozpoczęte w TS. Myślę jednak, że możesz rozpocząć nowe zadanie TS, którego celem jest po prostu poczekanie na zakończenie istniejącego procesu.
Ken Williams
W rzeczy samej. To przydatny aspekt twojego rozwiązania.
holmb
2

Command1 i& Command2

  • „&&” oznacza, że ​​polecenie 2 działa tylko po zakończeniu polecenia 1 z kodem powrotu 0
  • Uwaga: a || oznacza, że ​​polecenie 2 działa tylko po zakończeniu polecenia 1 z kodem powrotu innym niż 0
naftuli
źródło
1

Możesz po prostu dodać polecenia do wiersza poleceń powłoki, która już uruchamia polecenie.

Albo pisz ostrożnie, albo pisz w innym miejscu, sprawdź i wytnij i wklej. Nawet jeśli bieżące polecenie wyrzuca linie wyjściowe, nadal możesz wpisać coś nowego, nacisnąć klawisz Enter, a uruchomi się ono, gdy wszystkie polecenia zostaną uruchomione lub wprowadzone przed zakończeniem.

Przykład poniżej. Możesz wyciąć i wkleić całość lub wprowadzić kolejne polecenia, gdy pierwsza jest uruchomiona (jeśli wpisujesz naprawdę bardzo szybko), lub bardziej prawdopodobne, gdy druga jest uruchomiona:

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

źródło
0

najgorszym sposobem, jaki mogę wymyślić, jest utrzymanie „stanu” kolejki za pomocą prostych plików blokujących w / tmp. gdy plik1.sh wykonuje polecenia cp, zachowa plik blokady (lub hardlink) w / tmp. po zakończeniu usuń plik. każdy inny skrypt będzie musiał szukać w / tmp plików blokujących z wybranym schematem nazewnictwa. naturalnie jest to podatne na wszelkiego rodzaju awarie, ale konfiguracja jest banalna i jest wykonalna całkowicie w ramach bash.

Brad Clawsie
źródło
Trudne w użyciu, ponieważ wymaga, aby każde zadanie, które chcesz uruchomić, musi zostać zmodyfikowane, aby zachować zablokowane pliki, i musi wiedzieć (lub zaakceptować jako parametry), które pliki blokowania mają być używane. O ile to możliwe, najlepiej ustawić kolejkowanie na zewnątrz zadania.
Ken Williams