Chcę coś robić wielokrotnie na liście plików. Pliki w pytaniach mają spacje w nazwach:
david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha
-rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy)
Teraz chcę statystyki każdego z tych plików:
david@david: echo '
for file in $(ls)
do
stat $file
done' | bash
(Używam echa i potoku do pisania poleceń wieloliniowych.)
Kiedy to robię, działa poprawnie na plikach, które nie mają spacji w nazwach. Ale inni ...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
Zmiana $(ls)
na "$(ls)"
lub $file
do "$file"
nie działa. Co mogę zrobić?
Edytować:
echo '
for files in *
do
stat "$files"
done' | bash
Zrób sztuczkę! Ponieważ jestem nowy w bash, chcę, aby wszystko było tak proste, jak to możliwe - więc nic z próbą ucieczki ze spacji, użycia xargs
lub rozwiązania read -r
, chociaż rozwiązują problem.
Jak niektórzy pytają: Tak, używanie tego zamiast stat *
jest dziwne. Ale chciałem tylko znaleźć ogólny sposób zastosowania tego samego polecenia na paczce nazw plików w bash, używając pętli for. Więc stat
może oznaczać gzip
, gpg
lub rm
.
źródło
stat *
? (;-)Odpowiedzi:
Wielokrotny cytat z tego
echo '
komplikuje sprawę.Możesz po prostu użyć:
Ale również
... a jeśli chcesz zebrać pliki, a następnie zastosować polecenie (dlaczego?) możesz przejść (ale uważaj na plik zawierający nowe linie ... (1))
... a jeśli chcesz też ukrytych plików, po prostu użyj
* .*
jako wzorca, ale pamiętaj o tym.
i..
będzie w zestawie .Poza tym nie powinieneś analizować
ls
danych wyjściowych .(1) ale jeśli masz nazwy plików z nowymi liniami, to w pewnym sensie zasługujesz na to ... ;-)
źródło
for f in *; do command "$f"; done
. Nigdy nie parsujls
, z pewnością nigdy nie rób tego wfor
pętli i po co używaćecho
?do
,|
,&&
etc, można kontynuować na nowej linii. Albo to, albo użyj heredoków. Bez powodu do używaniaecho
i może również powodować problemy.Na marginesie: możesz podzielić długie / skomplikowane polecenia na wiele wierszy, dodając spację, a następnie odwrotny ukośnik i uderzając Enterza każdym razem, gdy chcesz zacząć pisać do nowej linii, zamiast rozwiązywać wiele procesów za pomocą
echo [...] | bash
; należy również zawrzeć$file
w cudzysłowie, aby zapobiecstat
łamaniu w przypadku nazw plików zawierających spacje:Problem polega na tym, że
$(ls)
rozwija się do listy nazw plików zawierających spacje, i to samo stanie się z"$(ls)"
.Nawet rozwiązując ten problem, ta metoda będzie nadal działać na nazwach plików zawierających ukośniki odwrotne i na nazwach plików zawierających znaki nowej linii (jak wskazał terdon).
Rozwiązaniem dla obu problemów byłoby potokowanie wyjścia
find
dowhile
uruchomionej pętli,read -r
aby przy każdej iteracjiread -r
przechowywał jeden wierszfind
wyjścia w$file
:źródło
ls
. Zawsze.ls
. Istnieją lepsze i bardziej niezawodne sposoby. Możesz także przeczytać to: Dlaczego * nie * parsować `ls`? po więcej szczegółów.ls
, żefor
-for
iteruje (oddzielone IFS) słowa podane poin
słowie kluczowym`for
ils
.for f in *
na przykład byłoby dobrze.Użyj starego dobrego
find
, działa z ukrytymi plikami, znakami nowej linii i spacjami.lub jakikolwiek inny zamiast
stat
źródło
Jako facet R już znalazłem obejście w R:
Wiem, to szalone. Chciałbym, aby wynik
ls
był łatwiejszy do przeanalizowania ... R może zajmować się spacjami, ponieważ dir () zwraca wartość znaku cytowanego. Cokolwiek między cudzysłowami to poprawna nazwa pliku ze spacjami.źródło
for f in *
, naciśnij Enter i kontynuuj w nowej liniido
:, naciśnij Enter ponowniestat "$f"
, Enter ponownie,done
Enter. To polecenie podzielone ładnie na 4 linie i nie złamie żadnej nazwy pliku.Natrafiłem na inne przypadki problemów z białymi znakami w pętlach for, więc generalnie używam następującego (imo bardziej niezawodnego) polecenia. Ładnie pasuje również do rur.
Możesz połączyć to z
grep
lubfind
zamiast tego użyć :źródło
-E
opcjęgrep
, bez której nie zostanie rozpoznana+
w wyrażeniu regularnym. Nawet wtedy jest to huśtawka i brak w tym pytaniu, ponieważgrep
usuwasz nazwy plików zawierające spacje . Usuwa również nazwy plików zawierające cyfry (cyfry) i znaki interpunkcyjne (np. Nawiasy), tak jak w przykładach w pytaniu. I to nawet nie wspominając o nazwach plików, które zawierają-
znak nowej linii lub zaczynają się od (myślnik).Zamiast tego możesz zmienić nazwę swoich plików, zastępując spację inną postacią, taką jak podkreślenie, aby pozbyć się tego problemu:
Aby to zrobić, uruchom polecenie:
źródło
Ta odpowiedź rozwiąże problem parsowania
ls
i zajmie się backspaceami i nowymi liniamiSpróbuj tego, rozwiąże Twój problem za pomocą wewnętrznego separatora pól IFS.
Ale możesz też łatwo go rozwiązać bez potrzeby analizowania danych wyjściowych za pomocą
źródło
IFS
tutaj zmodyfikować : z pewnością cytowanie zmiennej powinno wystarczyć, aby zapobiec podziałowi słów?ls
.