Konsekwentnie widzę odpowiedzi cytujące ten link stwierdzające definitywnie: „Nie analizuj ls
!” Niepokoi mnie to z kilku powodów:
Wygląda na to, że informacje w tym linku zostały zaakceptowane hurtowo, bez drobnych pytań, chociaż potrafię wybrać przynajmniej kilka błędów w swobodnym czytaniu.
Wydaje się również, że problemy wskazane w tym łączu nie wywołały chęci znalezienia rozwiązania.
Z pierwszego akapitu:
... kiedy pytasz
[ls]
o listę plików, pojawia się ogromny problem: Unix dopuszcza prawie dowolny znak w nazwie pliku, w tym białe znaki, znaki nowej linii, przecinki, symbole potoku i prawie wszystko, czego kiedykolwiek chciałbyś użyć jako ogranicznik oprócz NUL. ...ls
oddziela nazwy plików znakami nowej linii. Jest to w porządku, dopóki nie masz pliku z nową linią w nazwie. A ponieważ nie znam żadnej implementacji,ls
która pozwala na zakończenie nazw plików ze znakami NUL zamiast znaków nowej linii, nie możemy bezpiecznie uzyskać listy nazw plikówls
.
Bummer, prawda? Jak zawsze możemy obsługiwać nowa linia zakończona notowanej zestaw danych dla danych, które mogą zawierać znaki nowej linii? Cóż, jeśli ludzie odpowiadający na pytania na tej stronie nie robią tego rodzaju rzeczy na co dzień, myślę, że mieliśmy kłopoty.
Prawda jest jednak taka, że większość ls
implementacji zapewnia bardzo prosty interfejs API do analizowania wyników i wszyscy robiliśmy to od samego początku, nawet nie zdając sobie z tego sprawy. Nie tylko możesz zakończyć nazwę pliku na null, ale możesz także zacząć od null lub dowolnego innego dowolnego ciągu, jaki możesz chcieć. Co więcej, możesz przypisać te dowolne ciągi według typu pliku . Proszę wziąć pod uwagę:
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
Zobacz to po więcej.
Teraz jednak kolejna część tego artykułu mnie naprawdę przyciąga:
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
Problem polega na tym, że z wyjścia
ls
ani użytkownik, ani komputer nie mogą stwierdzić, które części stanowią nazwę pliku. Czy to każde słowo? Nie. Czy to każda linia? Nie. Nie ma poprawnej odpowiedzi na to pytanie poza: nie możesz powiedzieć.Zauważ też, jak
ls
czasami garbles dane pliku (w naszym przypadku okazało się\n
postać w między słowami „a” i „nowej linii” w ? Znakiem zapytania ......
Jeśli chcesz po prostu iterować wszystkie pliki w bieżącym katalogu, użyj
for
pętli i globu:
for f in *; do
[[ -e $f ]] || continue
...
done
Autor nazywa to mylącymi nazwami plików, gdy ls
zwraca listę nazw plików zawierających globusy powłoki, a następnie zaleca użycie globu powłoki do pobrania listy plików!
Rozważ następujące:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX definiuje-1
i -q
ls
Operandy tak:
-q
- Wymusza<tab>
zapisywanie każdego wystąpienia znaków niedrukowalnych nazw plików jako znaków znaku zapytania ('?'
). Implementacje mogą domyślnie zapewniać tę opcję, jeśli dane wyjściowe dotyczą urządzenia końcowego.
-1
- (cyfra jeden.) Wymusza na wyjściu jeden wpis na linię.
Globowanie nie jest pozbawione własnych problemów - ?
dopasowuje dowolny znak, więc wiele pasujących ?
wyników na liście będzie pasowało do tego samego pliku wiele razy. Łatwo sobie z tym poradzić.
Chociaż nie chodzi o to, jak to zrobić - w końcu nie zajmuje wiele i pokazano to poniżej - byłem zainteresowany, dlaczego nie . Uważam, że najlepsza odpowiedź na to pytanie została zaakceptowana. Sugerowałbym, abyś częściej skupiał się na mówieniu ludziom, co mogą zrobić, niż na tym, czego nie mogą. Myślę, że co najmniej mniej prawdopodobne jest udowodnienie, że się mylisz.
Ale po co w ogóle próbować? Wprawdzie moją główną motywacją było to, że inni mówili mi, że nie mogę. Wiem bardzo dobrze, że ls
wyniki są tak regularne i przewidywalne, jak tylko można sobie życzyć, o ile wiesz, czego szukać. Dezinformacja niepokoi mnie bardziej niż większość rzeczy.
Prawda jest jednak taka, że z godnym uwagi wyjątkiem zarówno Patricka, jak i Wumpusa Q. Odpowiedzi Wumbley (pomimo niesamowitego uchwytu tego ostatniego) uważam większość informacji w odpowiedziach tutaj za w większości poprawnych - glob powłoki jest łatwiejszy w użyciu i ogólnie bardziej skuteczne, jeśli chodzi o wyszukiwanie w bieżącym katalogu, niż parsowanie ls
. Nie są one jednak, przynajmniej w moim zakresie, to wystarczający powód, aby uzasadnić szerzenia dezinformacji albo cytowany w artykule powyżej nie są one akceptowalne uzasadnienie „ nie analizować ls
. ”
Należy pamiętać, że niespójne wyniki Patryka odpowiedź brzmi to głównie wynikiem niego używając zsh
potem bash
. zsh
- domyślnie - nie przenosi $(
polecenia podziału tekstu )
na przenośne. Więc kiedy pyta, gdzie poszła reszta plików? odpowiedź na to pytanie jest taka, że zjadła je twoja skorupa. Dlatego musisz ustawić SH_WORD_SPLIT
zmienną podczas używania zsh
i obsługi przenośnego kodu powłoki. Uważam, że jego brak odnotowania tego w odpowiedzi jest strasznie mylący.
Odpowiedź Wumpusa nie jest dla mnie obliczalna - w kontekście listy ?
postać jest globem powłoki. Nie wiem, jak inaczej to powiedzieć.
Aby poradzić sobie z przypadkiem wielu wyników, musisz ograniczyć chciwość globu. Poniższe elementy utworzą bazę testową okropnych nazw plików i wyświetlą ją dla Ciebie:
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
WYNIK
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
Teraz będę bezpieczny każdy znak, który nie jest /slash
, -dash
, :colon
, lub alfanumeryczny znak w glob powłoki następnie sort -u
liście dla wyjątkowych rezultatów. Jest to bezpieczne, ponieważ ls
ma już za sobą wszelkie znaki, które nie mogą zostać wydrukowane. Zegarek:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
WYNIK:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
Poniżej ponownie podchodzę do problemu, ale używam innej metodologii. Pamiętaj, że - oprócz \0
null - /
znak ASCII jest jedynym bajtem zabronionym w nazwie ścieżki. Odkładam globusy na bok i zamiast tego łączę -d
opcję określoną dla ls
POSIX i -exec $cmd {} +
konstrukcję określoną również dla POSIX find
. Ponieważ w find
naturalny sposób będzie emitować tylko jeden /
po kolei, poniższe elementy łatwo zdobywają rekurencyjną i rzetelnie ograniczoną listę plików, w tym wszystkie informacje o dentystyce dla każdego wpisu. Wyobraź sobie, co możesz zrobić z czymś takim:
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
może być bardzo przydatny - zwłaszcza gdy kwestionowana jest wyjątkowość wyniku.
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
To tylko najbardziej przenośne środki, jakie mogę wymyślić. Z GNU ls
możesz:
ls --quoting-style=WORD
I na koniec, oto o wiele prostsza metoda parsowanials
, z której zdarza się dość często, gdy potrzebuję numerów i-węzłów:
ls -1iq | grep -o '^ *[0-9]*'
To tylko zwraca numery i-węzłów - co jest kolejną przydatną opcją określoną przez POSIX.
stat
w mojej odpowiedzi, ponieważ faktycznie sprawdza, czy każdy plik istnieje. Twój kawałek na dole sed
rzeczy nie działa.
ls
? To, co opisujesz, jest bardzo trudne. Będę musiał go zdekonstruować, aby wszystko zrozumieć, a ja jestem stosunkowo kompetentnym użytkownikiem. Nie możesz oczekiwać, że przeciętny Joe będzie w stanie poradzić sobie z czymś takim.
ls
danych wyjściowych jest nieprawidłowe, zostały dobrze omówione w oryginalnym łączu (oraz w wielu innych miejscach). To pytanie byłoby rozsądne, gdyby OP poprosił o pomoc w zrozumieniu go, ale zamiast tego OP próbuje po prostu udowodnić, że jego nieprawidłowe użycie jest w porządku.
parsing ls is bad
. Robi for something in $(command)
i opierając się na słowo rozszczepienia, aby uzyskać dokładne wyniki są złe dla większości z command's
których nie mają proste wyjście.
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s vstime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28s