Podczas gdy używam BASH od kilku lat, moje doświadczenie ze skryptowaniem BASH jest stosunkowo ograniczone.
Mój kod jest jak poniżej. Powinien pobrać całą strukturę katalogów z bieżącego katalogu i zreplikować ją $OUTDIR
.
for DIR in `find . -type d -printf "\"%P\"\040"`
do
echo mkdir -p \"${OUTPATH}${DIR}\" # Using echo for debug; working script will simply execute mkdir
echo Created $DIR
done
Problem polega na tym, że oto przykład mojej struktury plików:
$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K
Zwróć uwagę na spacje: -S I for
pobiera parametry słowo po słowie, więc wynik mojego skryptu wygląda mniej więcej tak:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot
Ale potrzebuję go, aby pobrać całe nazwy plików (jedna linia na raz) z wyjścia find
. Próbowałem też robić find
umieszczaj podwójne cudzysłowy wokół każdej nazwy pliku. Ale to nie pomaga.
for DIR in `find . -type d -printf "\"%P\"\040"`
A wyjście z tą zmienioną linią:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"
Teraz potrzebuję jakiegoś sposobu, aby przejść przez to w ten sposób, ponieważ chcę też uruchomić bardziej skomplikowane polecenie gstreamer
na każdym pliku w następującej podobnej strukturze. Jak mam to robić?
Edytować: Potrzebuję struktury kodu, która pozwoli mi uruchomić wiele linii kodu dla każdego katalogu / pliku / pętli. Przepraszam, jeśli byłam niejasna.
Rozwiązanie: Początkowo próbowałem:
find . -type d | while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done
W większości działało to dobrze. Jednak później odkryłem, że ponieważ potok powoduje pętlę while działającą w podpowłoce, później wszystkie zmienne ustawione w pętli były niedostępne, co utrudniało implementację licznika błędów. Moje ostateczne rozwiązanie (od ta odpowiedź na SO ):
while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done < <(find . -type d)
To później pozwoliło mi warunkowo zwiększyć zmienne w pętli, które pozostałyby dostępne później w skrypcie.
/
i niedrukowalne znaki. Ale wszystko jest dozwolone z wyjątkiem/
i\0
więc musisz im pozwolić.Odpowiedzi:
Musisz potokować
find
w awhile
pętla.Ponadto nie musisz używać
-printf
w tym przypadku.Możesz zrobić ten dowód przeciwko plikom z nowymi znakami w nazwach, jeśli chcesz, używając separatora nullbyte (który jest jedynym znakiem, który nie może pojawić się w ścieżce pliku * nix):
Znajdziesz również użycie
$()
zamiast backticks być bardziej wszechstronnym i łatwiejszym. Można je znacznie łatwiej zagnieżdżać, a cytowanie można zrobić znacznie łatwiej. Ten wymyślony przykład ilustruje te punkty:Spróbuj to zrobić za pomocą backticks.
źródło
"$dir"
, lepiej jest użyć"${dir}"
- łatwo jest odróżnić nazwę $ {dir} od $ {dirname}, ale $ dirname można interpretować w dowolny sposób.read
czyta całą linię do${dir}
, więc IFS nie ma znaczenia.find … -print0 | xargs -0 …
ponieważ separator, którego używa, odpowiada dokładnie jedynemu znakowi, który nie jest dozwolony w ścieżkach POSIX: NUL (U + 0000).while
. @Chris Johnsen: Prawda, ale nawet programy do zgrywania muzyki nie mają tendencji do umieszczania kanałów w nazwach plików. A jeśli tak, chcę wiedzieć (tzn. Coś pójdzie nie tak) i natychmiast się ich pozbyć ...Widzieć ta odpowiedź Napisałem kilka dni temu przykład skryptu, który obsługuje nazwy plików ze spacjami.
Istnieje jednak nieco bardziej skomplikowany (ale bardziej zwięzły) sposób na osiągnięcie tego, co próbujesz zrobić:
-print0
mówi find, aby oddzielił argumenty o wartości null; -0 do xargs mówi mu, by oczekiwał argumentów oddzielonych przez null. Oznacza to, że dobrze obsługuje przestrzenie.-I {}
mówi xargs, aby zastąpił ciąg{}
z nazwą pliku. Oznacza to również, że w wierszu poleceń powinna być używana tylko jedna nazwa pliku (xargs będzie zwykle wypychać tyle, ile zmieści się w linii)Reszta powinna być oczywista.
źródło
Problem, który napotykasz, to instrukcja for odpowiadająca na znalezione jako osobne argumenty. Ogranicznik przestrzeni. Musisz użyć zmiennej IFS bash, aby nie rozdzielać przestrzeni.
Tutaj jest połączyć to wyjaśnia, jak to zrobić.
Ustaw swoje znalezisko na wyjście ogranicznika pola po% P i odpowiednio ustaw IFS. Wybrałem średnik, ponieważ jest mało prawdopodobne, aby go znaleźć w twoich nazwach plików.
Inną alternatywą jest wywołanie mkdir ze znaleziska bezpośrednio przez
-exec
czy możesz całkowicie pominąć pętlę for. Jeśli nie musisz wykonywać żadnych dodatkowych analiz.źródło
/
w POSIX i:
na systemach plików DOS. Istnieją nielegalne znaki dla różnych systemów plików, które można wybrać dla IFS. Wszystko bardziej skomplikowane i lepiej jest używać perla.find
zwraca nazwy plików ze ścieżkami zawierającymi ukośnik. Spróbuj zmienić średnik w skrypcie na ukośnik, a echo wydrukuje katalog i nazwę pliku w osobnych wierszach.while
opcja, ale wygląda to również całkiem dobrze. Tak, w mojej podobnej strukturze później musiałem wykonać dalsze parsowanie. (Nazwa pliku wejściowego to .ogg, który zostanie przekazany jakofilesrc
w potoku gst, ale zostanie wygenerowany odpowiednik kończący się na .mp3 w katalogu wyjściowym, a także przekazany do potoku jakofilesink
, i oczywiście trzeba to zrobić dla każdego pliku, razem z niektórymiecho
do użytkownika.)Jeśli treść twojej pętli jest więcej niż jednym poleceniem, można z niej skorzystać xargs sterować skryptem powłoki:
Pamiętaj, aby dołączyć kreskę końcową (lub inne „słowo”), jeśli powłoka jest odmiany Bourne / POSIX (jest używana do ustawienia 0 $ w skrypcie powłoki). Należy również zachować ostrożność przy cytowaniu, ponieważ skrypt powłoki jest zapisywany w cudzysłowionym łańcuchu zamiast bezpośrednio w monicie.
źródło
w zaktualizowanym pytaniu
to powinno być
źródło
źródło
lub aby uczynić całość mniej skomplikowaną:
to replikuje strukturę katalogów SRC na DST.
źródło
while
...do
...done
struktura później, aby wykonać podobne przetwarzanie ze find, co wymagałoby uruchomienia kilku wierszy kodu na każdym pliku (zmodyfikuj ciąg, echo, gst-launch itp.) irsync
nie osiągnie tego. Dlatego określiłem, że muszę być w stanie uruchomić bardziej skomplikowany zestaw poleceń w podobnej strukturze. Mój skrypt używa tej struktury pętli dwa razy, więc na pytanie zamieściłem ten z mniejszą ilością crud na środku.Jeśli masz GNU Parallel http: // www.gnu.org/software/parallel/ zainstalowany, możesz to zrobić:
Obejrzyj film wprowadzający do GNU Parallel, aby dowiedzieć się więcej: http://www.youtube.com/watch?v=OpaiGYxkSuQ
źródło