Mam problem ze skryptem powłoki, w którym dostaję katalog pełen plików wejściowych (każdy plik zawiera wiele wierszy wejściowych) i muszę przetwarzać je osobno, przekierowując każde z ich wyników do unikalnego pliku (aka, plik_1.input potrzebuje do przechwycenia w pliku_1.output itd.).
Przed równolegle , po prostu iterowałbym każdy plik w katalogu i wykonywał moje polecenie, wykonując jakąś technikę licznika / liczenia, aby nie przytłoczyć procesorów (zakładając, że każdy proces miał stały czas działania). Wiem jednak, że nie zawsze tak będzie, więc użycie rozwiązania „równoległego” wydaje się najlepszym sposobem na uzyskanie wielowątkowości skryptu powłoki bez pisania niestandardowego kodu.
Chociaż zastanawiałem się nad niektórymi sposobami równoległego przetwarzania w celu przetworzenia każdego z tych plików (i umożliwiania wydajnego zarządzania rdzeniami), wszystkie wydają się hackerskie. Mam, jak sądzę, dość łatwą w użyciu skrzynkę, więc wolałbym zachować ją tak czystą, jak to możliwe (i nic w równoległych przykładach nie wydaje się być moim problemem.
Każda pomoc będzie mile widziana!
przykład katalogu wejściowego:
> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt
Scenariusz:
> cat proces_script.sh
#!/bin/sh
customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]
Aktualizacja : po przeczytaniu odpowiedzi Ole poniżej udało mi się zebrać brakujące elementy do mojej równoległej implementacji. Chociaż jego odpowiedź jest świetna, oto moje dodatkowe badania i notatki, które wziąłem:
Zamiast uruchomić cały proces, pomyślałem, że zacznę od dowodu koncepcji, aby sprawdzić jego rozwiązanie w moim środowisku. Zobacz moje dwie różne implementacje (i uwagi):
find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out
Używa find (nie ls, co może powodować problemy), aby znaleźć wszystkie odpowiednie pliki w moim katalogu plików wejściowych, a następnie przekierowuje ich zawartość do osobnego katalogu i pliku. Mój problem z góry polegał na czytaniu i przekierowywaniu (sam skrypt był prosty), więc zastąpienie go kotem było dobrym dowodem koncepcji.
parallel cat '>' /home/me/output_files/{.}.out ::: /home/me/input_files/*
To drugie rozwiązanie wykorzystuje paradygmat zmiennych wejściowych równoległych do odczytu plików, jednak dla nowicjuszy było to znacznie bardziej mylące. Dla mnie użycie find a pipe potwierdziło moje potrzeby.
źródło
Standardowym sposobem na to jest ustawienie kolejki i odrodzenie dowolnej liczby pracowników, którzy wiedzą, jak wyciągnąć coś z kolejki i przetworzyć. Do komunikacji między tymi procesami można użyć fifo (aka o nazwie potok).
Poniżej znajduje się naiwny przykład pokazujący tę koncepcję.
Prosty skrypt kolejki:
I pracownik:
process_file
może być zdefiniowane gdzieś u twojego pracownika i może zrobić wszystko, czego potrzebujesz.Po uzyskaniu tych dwóch elementów możesz mieć prosty monitor, który uruchamia proces kolejki i dowolną liczbę procesów roboczych.
Skrypt monitorowania:
Masz to. Jeśli faktycznie to zrobisz, lepiej ustawić fifo na monitorze i przekazać ścieżkę zarówno do kolejki, jak i pracowników, aby nie były one połączone i nie utknęły w określonej lokalizacji dla fifo. Ustawiłem to w odpowiedzi, aby było jasne, że używasz tego, co czytasz.
źródło
monitor_workers
jest jakprocess_file
- to funkcja, która robi, co chcesz. O monitorze - miałeś rację; powinien zapisać stawki swoich pracowników (aby mógł wysłać sygnał zabicia), a licznik należy zwiększyć, gdy uruchomi pracownika. Zredagowałem odpowiedź, aby to uwzględnić.parallel
. Myślę, że to Twój pomysł, w pełni zrealizowany.Inny przykład:
Znalazłem inne przykłady niepotrzebnie złożone, gdy w większości przypadków powyższe jest tym, czego mogłeś szukać.
źródło
Jest powszechnie dostępnym narzędziem, które może wykonywać paralelizację. Marka GNU i kilka innych ma
-j
opcję wykonywania kompilacji równoległych.Uruchom w
make
ten sposób (zakładam, że twoje nazwy plików nie zawierają żadnych znaków specjalnych,make
nie jest z nimi dobra):źródło
Aby wykonać tę samą komendę na dużym zestawie plików w bieżącym katalogu:
Spowoduje to uruchomienie
customScript
każdegotxt
pliku, umieszczając dane wyjściowe wouttxt
plikach. Zmień według potrzeb. Kluczem do tego, aby to zadziałało, jest przetwarzanie sygnału za pomocą SIGUSR1, aby proces potomny mógł poinformować proces nadrzędny, że jest on wykonywany. Użycie SIGCHLD nie będzie działać, ponieważ większość instrukcji w skrypcie generuje sygnały SIGCHLD do skryptu powłoki. Próbowałem tego, zastępując twoje poleceniesleep 1
, program wykorzystał 0,28s procesora użytkownika i 0,14s procesora systemowego; dotyczyło to tylko około 400 plików.źródło
wait
jest wystarczająco „inteligentne”; ale wróci po otrzymaniuSIGUSR1
sygnału. Dziecko / pracownik wysyła aSIGUSR1
do rodzica, który jest przechwytywany (trap
), i dekrementuje$worker
(trap
klauzula) i wraca nienormalnie zwait
, umożliwiając wykonanieif [ $worker -lt $num_workers ]
klauzuli.Lub po prostu użyj
xargs -P
, bez potrzeby instalowania dodatkowego oprogramowania:Trochę wyjaśnienia dla opcji:
-I'XXX'
ustawia ciąg, który zostanie zastąpiony w szablonie poleceń nazwą pliku-P4
uruchomi 4 procesy równolegle-n1
umieści tylko jeden plik na wykonanie, mimo że znaleziono dwa XXX-print0
i-0
współpracują ze sobą, pozwalając na stosowanie znaków specjalnych (takich jak białe znaki) w nazwach plikówźródło