Biorąc pod uwagę te nazwy plików:
$ ls -1
file
file name
otherfile
bash
sam doskonale sobie radzi z osadzonymi białymi znakami:
$ for file in *; do echo "$file"; done
file
file name
otherfile
$ select file in *; do echo "$file"; done
1) file
2) file name
3) otherfile
#?
Czasami jednak nie chcę pracować z każdym plikiem, a nawet ściśle w tym $PWD
, co tam find
jest. Który obsługuje również białe znaki nominalnie:
$ find -type f -name file\*
./file
./file name
./directory/file
./directory/file name
Próbuję wymyślić bezpieczną dla whispace wersję tego skryptu, który weźmie dane wyjściowe find
i zaprezentuje je w select
:
$ select file in $(find -type f -name file); do echo $file; break; done
1) ./file
2) ./directory/file
Jednak wybucha to spacjami w nazwach plików:
$ select file in $(find -type f -name file\*); do echo $file; break; done
1) ./file 3) name 5) ./directory/file
2) ./file 4) ./directory/file 6) name
Zwykle poradziłbym sobie z tym, bawiąc się IFS
. Jednak:
$ IFS=$'\n' select file in $(find -type f -name file\*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
$ IFS='\n' select file in $(find -type f -name file\*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
Jakie jest na to rozwiązanie?
bash
text-processing
whitespace
select
DopeGhoti
źródło
źródło
find
swojej zdolności do dopasować dany nazwę pliku, możesz po prostu użyćselect file in **/file*
(po ustaleniushopt -s globstar
) wbash
4 lub nowszy.Odpowiedzi:
Jeśli potrzebujesz tylko obsługiwać spacje i tabulatory (nie osadzone znaki nowej linii), możesz użyć
mapfile
(lub jego synonimureadarray
), aby odczytać tablicę, np.następnie
Jeśli zrobić potrzebę nowej linii uchwytów, a
bash
wersja zapewnia zerowy rozdzielanymapfile
1 , a następnie można modyfikować, że doIFS= mapfile -t -d '' files < <(find . -type f -print0)
. W przeciwnym razie złóż równoważną tablicę zfind
danych wyjściowych rozdzielonych znakiem null za pomocąread
pętli:1
-d
opcji dodanomapfile
wbash
wersji 4.4 IIRCźródło
mapfile
jest także dla mnie nowy. Sława.while IFS= read
Wersja działa z powrotem w v3 bash (co jest ważne dla tych z nas, używając MacOS).find -print0
wariant; narzekam za umieszczenie go po znanej niepoprawnej wersji i opisanie go tylko do użytku, jeśli wiadomo , że trzeba obsługiwać nowe wiersze. Jeśli ktoś poradzi sobie z nieoczekiwanym w miejscach, w których jest to oczekiwane, nigdy nie poradzi sobie z nieoczekiwanym.Ta odpowiedź zawiera rozwiązania dla każdego rodzaju plików. Z nowymi liniami lub spacjami.
Istnieją rozwiązania dla ostatniego basha, a także starożytnego basha, a nawet starych powłok posix.
Do testów użyto drzewa wymienionego poniżej w tej odpowiedzi [1] .
Wybierz
select
Praca z tablicą jest łatwa :Lub z parametrami pozycyjnymi:
Tak więc jedynym prawdziwym problemem jest umieszczenie „listy plików” (poprawnie rozdzielonej) w tablicy lub w parametrach pozycyjnych. Czytaj dalej.
grzmotnąć
Nie widzę problemu, który zgłaszasz za pomocą bash. Bash może wyszukiwać w danym katalogu:
Lub, jeśli lubisz pętlę:
Zauważ, że powyższa składnia będzie działać poprawnie z dowolną (rozsądną) powłoką (przynajmniej csh).
Jedynym ograniczeniem powyższej składni jest zejście do innych katalogów.
Ale bash może to zrobić:
Aby wybrać tylko niektóre pliki (takie jak te, które kończą się plikiem), po prostu zamień *:
krzepki
Kiedy w tytule umieścisz słowo „ bezpieczny dla przestrzeni”, założę , że to, co miałeś na myśli, było „ solidne ”.
Najprostszym sposobem na solidne podejście do spacji (lub znaków nowej linii) jest odrzucenie przetwarzania danych wejściowych zawierających spacje (lub znaki nowej linii). Bardzo prostym sposobem na wykonanie tego w powłoce jest wyjście z błędem, jeśli dowolna nazwa pliku rozwija się spacją. Można to zrobić na kilka sposobów, ale najbardziej kompaktowy (i posiks) (ale ograniczony do jednej zawartości katalogu, w tym nazw suddirectories i unikania plików kropkowych):
Jeśli zastosowane rozwiązanie jest solidne w którymkolwiek z tych elementów, usuń test.
W bash podkatalogi mogą być testowane jednocześnie z ** wyjaśnionym powyżej.
Istnieje kilka sposobów dołączania plików kropek, rozwiązaniem Posix jest:
odnaleźć
Jeśli z jakiegoś powodu należy użyć find, zamień separator na NUL (0x00).
bash 4.4+
bash 2.05+
POSIXLY
Aby stworzyć prawidłowe rozwiązanie POSIX, w którym find nie ma separatora NUL i nie ma
-d
(ani-a
) do odczytu, potrzebujemy zupełnie innego podejścia.Musimy użyć kompleksu
-exec
z find z wywołaniem powłoki:Lub, jeśli potrzebna jest opcja select (select jest częścią bash, a nie sh):
[1] To drzewo (\ 012 to nowe linie):
Można go zbudować za pomocą tych dwóch poleceń:
źródło
Nie można ustawić zmiennej przed konstrukcją zapętloną, ale można ustawić ją przed warunkiem. Oto segment ze strony podręcznika:
(Pętla nie jest prostym poleceniem .)
Oto często używany konstrukt pokazujący scenariusze awarii i sukcesu:
Niestety nie widzę sposobu na osadzenie zmiany
IFS
wselect
konstrukcie, gdy ma to wpływ na przetwarzanie powiązanego$(...)
. Jednak nic nie stoi na przeszkodzie,IFS
aby ustawić ją poza pętlą:i widzę, że ten konstrukt działa z
select
:Podczas pisania kodu obronną, polecam, że klauzula albo być uruchamiane w podpowłoce, albo
IFS
iSHELLOPTS
zapisane i przywrócone wokół bloku:źródło
IFS=$'\n'
jest bezpieczny, jest bezpodstawne. Nazwy plików mogą doskonale zawierać literały nowej linii.[0-9a-f]{24}
. TB kopii zapasowych danych służących do obsługi fakturowania klientów zostało utraconych.select
sama konstrukcja jest przeznaczona dla rozwiązań skryptowych , dlatego zawsze powinna być zaprojektowana do obsługi przypadków skrajnych.select
ze powłoki, w której wpisujesz polecenia do uruchomienia, ale tylko w skrypcie, w którym odpowiadasz na monit dostarczony przez ten skrypt i gdzie ten skrypt jest wykonywanie predefiniowanej logiki (zbudowanej bez wiedzy o obsługiwanych nazwach plików) na podstawie tych danych wejściowych.Mogę być poza moją jurysdykcją tutaj, ale może możesz zacząć od czegoś takiego, przynajmniej nie ma żadnych problemów z białymi znakami:
Aby uniknąć potencjalnych fałszywych założeń, jak zauważono w komentarzach, należy pamiętać, że powyższy kod jest równoważny z:
źródło
read -d
jest sprytnym rozwiązaniem; dzięki za to.read -d $'\000'
jest dokładnie identyczny zread -d ''
, ale dla wprowadzających w błąd ludzi o możliwościach basha (niepoprawnie sugerując, że jest w stanie reprezentować dosłowne wartości NUL w łańcuchach). Uruchoms1=$'foo\000bar'; s2='foo'
, a następnie spróbuj znaleźć sposób na rozróżnienie tych dwóch wartości. (Przyszła wersja może się znormalizować z zachowaniem zastępowania poleceń, czyniąc przechowywaną wartość równoważnąfoobar
, ale dzisiaj tak nie jest).