Lista elementów ze spacjami w zsh

10

W tym momencie studiuję skrypty zsh przez wszystkie 2 godziny i uderzyłem w ścianę. Chcę przejrzeć listę plików, które mogą zawierać spacje. Jestem otwarty na zupełnie inne podejścia niż w poniższym przykładzie, o ile są one zsh, ponieważ zsh jest tym, czego się uczę, a nie zadaniem, które próbuję napisać.

files=(`ls`)
for f in $files; do
    print $f
done

Oczywiście nie odtwarzam tylko ls, chcę tylko $fprzechwycić całą nazwę pliku przy każdej iteracji pętli.

Felix
źródło

Odpowiedzi:

12

Po pierwsze, zakładam, że użycie lsjest tylko przykładem. Nie można przeanalizować wyniku lsw żadnej powłoce, ponieważ jest ona niejednoznaczna. Przeczytaj, dlaczego nie powinieneś analizować danych wyjściowych ls (1), jeśli jest to dla Ciebie wiadomość. W dowolnej powłoce, aby uzyskać listę plików, użyj symboli wieloznacznych, np files=(*).

W Zsh, podobnie jak w innych powłokach, wynik podstawienia polecenia jest dzielony na słowa w białych znakach (a dokładniej, zgodnie z wartością IFS). (W przeciwieństwie do innych powłok wynik podstawienia polecenia nie podlega globowaniu w zsh.) Więc jeśli wynikiem lspolecenia jest

hello world
wibble

następnie files=($(ls))ustawia filesukład zawiera elementy 3: hello, worldi wibble.

Jeśli podstawienie polecenia jest w podwójnym cudzysłowie, nie jest wykonywane dzielenie. Można wykonać podział niestandardowy za pomocą flag rozwijania parametrów . Użyj @flagi, aby wskazać, że wynikiem podziału ma być tablica (co dziwne, musisz zachować rozwinięcie w podwójnych cudzysłowach, tj. "${(@)…}"Nawet jeśli ciąg podwójnego cudzysłowu rozwinie się do wielu słów). Do podziału używaj sflagi, np. "${(@s:,:)…}"Do dzielenia przecinków; fflag dzieli się tylko znakami nowej linii.

files=("${(@f)$(ls)}")

Zauważ, że właściwym sposobem na iterację tablicy jest ogólnie for f in $files[@], ponieważ $filesusuwa ona puste elementy (tutaj nie ma to znaczenia, ponieważ elementy nie będą puste).

print $finterpretuje $fjako przełącznik, jeśli zaczyna się od a, -i rozszerza ukośniki odwrotne $f. Użyj print -r -- $flub, print -rn -- $fjeśli nie chcesz dodawać nowego wiersza po ciągu.

Gilles „SO- przestań być zły”
źródło
Dziękuję Ci. Tak, ls był tylko przykładem. W tej chwili nie miałam na myśli konkretnego celu, chcę tylko wiedzieć, jak cytować lub wymieniać elementy listy z dowolnego polecenia, które może mieć pojedyncze wyniki z białymi spacjami.
Felix
Jedną z rzeczy, które uważam za mylące, jest to, dlaczego wyrażenie zewnętrzne () w twoich plikach = („$ {(@ f) $ (ls)}”) jest konieczne w przypadku @f? I sama zabawa (f) wydaje się działać dobrze, dopóki na zewnątrz pojawia się ().
Koobz
@Koobz 1. Zewnętrzne (…)są konieczne, aby filesbyła tablicą, a nie ciągiem (który zawierałby konkatenację wierszy z polecenia, cofając podział wykonywany przez (f)). 2. Z foo=$'one\n\nthree', contrast print -rl $ {(f) foo} `i print "${(@f)foo}". Podwójne cudzysłowy są potrzebne, aby zachować puste wiersze, z których nie dostaniesz, lsale mogą się zdarzyć z innymi poleceniami.
Gilles 'SO - przestań być zły'
Bardzo mile widziane. Czy istnieje jakiś rym lub powód tego szaleństwa? Nawet tam, co jest mylące, dlaczego external () po prostu nie ponownie dzieli łańcucha na poszczególne słowa, jeśli wartość pośrednia jest moim połączonym łańcuchem. Na przykład wygląda to na interpolację łańcuchów znaków w języku ruby ​​lub php.
Koobz
@Koobz Powodem specjalnego traktowania pustych słów jest historyczna zgodność z dawno zapomnianymi starszymi wersjami Zsh. Powodem nieautomatycznego dzielenia jest to, że jest to zaskakujące i często niepożądane działanie. Automatyczne dzielenie jest zachowaniem powłoki Bourne'a, którego zsh nie emuluje, chyba że sam to zrobisz.
Gilles 'SO - przestań być zły'
2

W zsh możesz użyć rozszerzenia powłoki, które domyślnie nie wykonuje podziału słów. Próbować

for f in /path/to/files/*; do
    print ${f}
done
u-punkt
źródło