Czy ktoś mógłby podać kod do wykonania następujących czynności: Załóżmy, że istnieje katalog plików, z których wszystkie muszą być uruchomione przez program. Program wypisuje wyniki na standardowe wyjście. Potrzebuję skryptu, który przejdzie do katalogu, wykona polecenie dla każdego pliku i połączy dane wyjściowe w jeden duży plik wyjściowy.
Na przykład, aby uruchomić polecenie dla 1 pliku:
$ cmd [option] [filename] > results.out
ls <directory> | xargs cmd [options] {filenames put in here automatically by xargs} [more arguments] > results.out
ls
do prowadzenia pojazduxargs
. Jeślicmd
w ogóle jest napisany kompetentnie, być może możesz po prostu zrobićcmd <wildcard>
.Odpowiedzi:
Poniższy kod bash przekaże $ file do polecenia, gdzie $ file będzie reprezentować każdy plik w / dir
Przykład
źródło
/dir/
, wówczas pętla nadal działa raz z wartością „*” dla$file
, co może być niepożądane. Aby tego uniknąć, włącz nullglob na czas trwania pętli. Dodaj tę linię przed pętląshopt -s nullglob
i tę linię po pętlishopt -u nullglob #revert nullglob back to it's normal default state
.done >results.out
(i prawdopodobnie wtedy możesz zastąpić zamiast dołączać, tak jak tutaj założyłem).Co powiesz na to:
-maxdepth 1
argument zapobiega rekurencyjnemu zejściu find do jakichkolwiek podkatalogów. (Jeśli chcesz przetworzyć takie zagnieżdżone katalogi, możesz to pominąć).-type -f
określa, że będą przetwarzane tylko zwykłe pliki.-exec cmd option {}
każe mu działaćcmd
z podanymoption
dla każdego znalezionego pliku, z zastąpioną nazwą pliku{}
\;
oznacza koniec polecenia.cmd
wykonań są przekierowywane doresults.out
Jeśli jednak zależy Ci na kolejności przetwarzania plików, lepiej zapisz pętlę. Myślę, że
find
przetwarza pliki w kolejności i-węzłów (chociaż mogę się mylić), co może nie być tym, czego chcesz.źródło
stat
isort
, które oczywiście zależą od kryteriów sortowania.-exec
opcji? Czy muszę zawijać je w pojedyncze cudzysłowy czy coś?find
jest zawsze najlepszą opcją, ponieważ możesz filtrować według wzorca nazwy pliku z opcją-name
i możesz to zrobić za pomocą jednego polecenia.-exec
opcje:find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
Robię to na moim malinowym pi z wiersza poleceń, uruchamiając:
źródło
Przyjęte / wysoko głosowane odpowiedzi są świetne, ale brakuje im kilku drobiazgowych szczegółów. Ten post omawia przypadki, w których lepiej radzić sobie, gdy rozszerzenie nazwy ścieżki powłoki (glob) kończy się niepowodzeniem, gdy nazwy plików zawierają osadzone symbole nowego wiersza / myślnika i przeniesienie wyjścia polecenia z pętli for podczas zapisywania wyników do plik.
Podczas uruchamiania rozszerzenia globu powłoki za pomocą
*
istnieje możliwość niepowodzenia rozszerzenia, jeśli w katalogu nie ma żadnych plików, a nierozwinięty ciąg globu zostanie przekazany do polecenia do uruchomienia, co może mieć niepożądane skutki.bash
Powłoka zapewnia rozszerzoną opcję powłoki dla tego użyciemnullglob
. Pętla zasadniczo wygląda następująco w katalogu zawierającym plikiPozwala to bezpiecznie wyjść z pętli for, gdy wyrażenie
./*
nie zwraca żadnych plików (jeśli katalog jest pusty)lub w sposób zgodny z POSIX (
nullglob
jestbash
specyficzny)Pozwala to wejść do pętli, gdy wyrażenie nie powiedzie się raz i warunek
[ -f "$file" ]
sprawdzi, czy nierozwinięty ciąg./*
jest prawidłową nazwą pliku w tym katalogu, co nie byłoby. Tak więc w tym przypadku błąd, przy użyciucontinue
wznawiamy z powrotem dofor
pętli, która nie będzie działać później.Zwróć także uwagę na użycie
--
tuż przed przekazaniem argumentu nazwy pliku. Jest to konieczne, ponieważ, jak wspomniano wcześniej, nazwy plików powłoki mogą zawierać myślniki w dowolnym miejscu w nazwie pliku. Niektóre polecenia powłoki interpretują to i traktują je jako opcję polecenia, gdy nazwa nie jest poprawnie cytowana, i wykonują polecenie, zastanawiając się, czy flaga jest podana.W takim przypadku
--
sygnalizuje koniec opcji wiersza poleceń, co oznacza, że polecenie nie powinno analizować żadnych ciągów poza tym punktem jako flag poleceń, a jedynie jako nazwy plików.Podwójne cytowanie nazw plików prawidłowo rozwiązuje przypadki, gdy nazwy zawierają znaki globalne lub białe znaki. Ale nazwy plików * nix mogą również zawierać w nich znaki nowej linii. Dlatego ograniczamy nazwy plików za pomocą jedynego znaku, który nie może być częścią prawidłowej nazwy pliku - null byte (
\0
). Ponieważbash
wewnętrznie używaC
ciągów stylów, w których do wskazania końca łańcucha używane są bajty zerowe, jest to odpowiedni kandydat na to.Tak więc używając
printf
opcji powłoki do rozdzielenia plików tym bajtem NULL za pomocą-d
opcjiread
polecenia, możemy to zrobić poniżejnullglob
Iprintf
są owinięte wokół(..)
co oznacza, że są w zasadzie prowadzone w sub-shell (powłoka dziecko), ponieważ aby uniknąćnullglob
możliwości zastanowienia się na powłoce macierzystej, raz wyjść sterujących.-d ''
Opcjaread
polecenia jest nie POSIX zgodne, więc potrzebujebash
skorupę, aby to zrobić. Za pomocąfind
polecenia można to zrobić jakoW przypadku
find
implementacji, które nie obsługują-print0
(innych niż implementacje GNU i FreeBSD), można to emulować za pomocąprintf
Inną ważną poprawką jest przeniesienie zmiany kierunku poza pętlę for, aby zmniejszyć dużą liczbę operacji we / wy pliku. Gdy używana jest w pętli, powłoka musi wykonywać wywołania systemowe dwa razy dla każdej iteracji pętli for, raz dla otwarcia i raz dla zamknięcia deskryptora pliku skojarzonego z plikiem. Stanie się to wąskim gardłem w wydajności podczas wykonywania dużych iteracji. Zalecaną sugestią byłoby przeniesienie go poza pętlę.
Rozszerzając powyższy kod o te poprawki, możesz to zrobić
który po prostu umieści zawartość polecenia dla każdej iteracji wejścia pliku na standardowe wyjście, a gdy pętla się zakończy, otwórz plik docelowy jeden raz, aby zapisać zawartość standardowego wejścia i zapisać go. Równoważna
find
wersja tego samego byłabyźródło
Jednym szybkim i brudnym sposobem, który czasami wykonuje zadanie, jest:
Na przykład, aby znaleźć liczbę wierszy we wszystkich plikach w bieżącym katalogu, możesz:
źródło
~/.local/share/steam
. Ran steam. Skasował wszystko w systemie należącym do użytkownika”. Zgłoszenie błędu.Musiałem skopiować wszystkie pliki .md z jednego katalogu do drugiego, więc oto co zrobiłem.
for i in **/*.md;do mkdir -p ../docs/"$i" && rm -r ../docs/"$i" && cp "$i" "../docs/$i" && echo "$i -> ../docs/$i"; done
Co jest dość trudne do odczytania, więc podzielmy to.
najpierw cd do katalogu z plikami,
for i in **/*.md;
dla każdego pliku we wzorzemkdir -p ../docs/"$i"
umieść ten katalog w folderze dokumentów poza folderem zawierającym pliki. Który tworzy dodatkowy folder o takiej samej nazwie jak ten plik.rm -r ../docs/"$i"
usuń dodatkowy folder utworzony w wynikumkdir -p
cp "$i" "../docs/$i"
Skopiuj aktualny plikecho "$i -> ../docs/$i"
Echo tego, co zrobiłeś; done
Żyj długo i szczęśliwieźródło
**
aby działać,globstar
należy ustawić opcję powłoki:shopt -s globstar
Możesz użyć
xarg
ls | xargs -L 1 -d '\n' your-desired-command
-L 1
powoduje przejście 1 przedmiotu na raz-d '\n'
make wyjściels
jest podzielone na podstawie nowej linii.źródło
W oparciu o podejście @Jima Lewisa:
Oto szybkie rozwiązanie wykorzystujące,
find
a także sortujące pliki według daty modyfikacji:Do sortowania patrz:
http://www.commandlinefu.com/commands/view/5720/find-files-and-list-them-sorted-by-modification-time
źródło
-print0
na temat dlafind
i-0
dlaxargs
których należy używać znaku null zamiast jakichkolwiek białych znaków (w tym znaków nowej linii).-print0
jest czymś, co pomaga, ale cały rurociąg musi używać czegoś takiego, asort
nie jestmyślę, że proste rozwiązanie to:
źródło
Maksymalna głębokość
Przekonałem się, że działa to dobrze z odpowiedzią Jima Lewisa, wystarczy dodać coś takiego:
Porządek sortowania
Jeśli chcesz wykonać w kolejności sortowania, zmodyfikuj go w następujący sposób:
Na przykład będzie to wykonywane w następującej kolejności:
Nieograniczona głębokość
Jeśli chcesz wykonać na nieograniczonej głębokości pod pewnymi warunkami, możesz użyć tego:
następnie umieść na wierzchu każdego pliku w katalogach potomnych w następujący sposób:
i gdzieś w treści pliku nadrzędnego:
źródło