Я послідовно бачу відповіді, цитуючи це посилання, вказуючи остаточно: "Не розбирайте ls
!" Це мене турбує з кількох причин:
Здається, інформація в цьому посиланні була прийнята оптом з невеликим питанням, хоча я можу виділити принаймні кілька помилок у випадковому читанні.
Також здається, що проблеми, зазначені в цьому посиланні, не викликали бажання знайти рішення.
З першого пункту:
... коли ви запитуєте
[ls]
список файлів, виникає величезна проблема: Unix дозволяє практично будь-який символ у назві файлу, включаючи пробіл, нові рядки, коми, символи труби та багато іншого, що ви коли-небудь намагалися використовувати як роздільник, крім NUL. ...ls
відокремлює назви файлів новими рядками. Це добре, поки у вас не буде файлу з новим рядком у його імені. Оскільки я не знаю жодної реалізації,ls
яка дозволяє вам скасувати назви файлів із символами NUL замість нових рядків, це не дозволяє нам безпечно отримати список іменls
.
Бампер, правда? Як коли-небудь ми можемо обробляти перелічений набір даних, що закінчується новим рядком для даних, які можуть містити нові рядки? Ну, якщо люди, відповідаючи на запитання на цьому веб-сайті, не робили подібних дій щодня, я можу подумати, що ми потрапили у певну проблему.
Правда, однак, більшість ls
реалізацій насправді надають дуже простий api для розбору їх результатів, і ми все це робимо весь час, навіть не усвідомлюючи цього. Ви не тільки можете закінчити ім'я файлу з null, ви можете почати його також з null або з будь-якої іншої довільної рядки, яка може бути бажана. Більше того, ви можете призначити ці довільні рядки для кожного типу файлів . Зверніть увагу:
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^@^@^@$
^@
Про це див. Докладніше.
Тепер це вже наступна частина цієї статті, яка мене дійсно отримує:
$ 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
Проблема в тому, що з виходу
ls
ви ні комп’ютер не можете сказати, які його частини складають назву файлу. Це кожне слово? Ні. Це кожен рядок? Ні. На це питання немає правильної відповіді, крім: ви не можете сказати.Також зауважте, як
ls
іноді вбрано ваші дані файлу (у нашому випадку він перетворив\n
символ між словами "a" та "newline" у знак питання ......
Якщо ви просто хочете повторити всі файли в поточному каталозі, використовуйте
for
цикл і глобус:
for f in *; do
[[ -e $f ]] || continue
...
done
Автор називає це шаленими іменами, коли ls
повертає список імен, що містять кульки оболонки, а потім рекомендує використовувати глобул оболонки для отримання списку файлів!
Розглянемо наступне:
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 визначає-1
і -q
ls
операнди так:
-q
- Примушуйте кожен екземпляр символів та<tab>
s, що не друкуються, записуватися як'?'
символ запитання ( ). Реалізації можуть надавати цю опцію за замовчуванням, якщо вихід на термінальний пристрій.
-1
- (Цифрова цифра.) Примушуйте вивести один запис на рядок.
Глоббінг не без власних проблем - ?
відповідає будь-якому символу, тож декілька ?
результатів відповідності у списку будуть збігатися з одним і тим же файлом кілька разів. З цим легко впоратися.
Хоча як це зробити, справа не в цьому - це все-таки не займе багато, адже це показано нижче - мене цікавило, чому ні . Як я вважаю, найкраща відповідь на це питання була прийнята. Я б запропонував вам спробувати зосередитися на тому, щоб розповісти людям, що вони можуть зробити, ніж на тому, що вони не можуть. Ви набагато рідше, як я вважаю, принаймні виявитесь неправильними.
Але навіщо навіть намагатися? Справді, моя основна мотивація полягала в тому, що інші продовжували мені говорити, що я не можу. Я дуже добре знаю, що ls
вихід такий самий регулярний і передбачуваний, як ви цього хотіли б, доки ви знаєте, що шукати. Дезінформація мене більше турбує, ніж більшість справ.
Правда, однак, за винятком винятків як Патріка, так і Вуппуса. Відповіді Вемблі (незважаючи на дивовижну реакцію останнього) , я вважаю більшість інформації у відповідях тут, як правило, правильними - глобус оболонки як простіший у використанні і, як правило, ефективніше шукати поточний каталог, ніж розбирати ls
. Вони, однак, принаймні , в моєму ставленні, достатньо підстав , щоб виправдати або поширюючи дезінформацію цитований в статті вище , ні вони поважна не " ніколи не розібрати ls
. »
Зверніть увагу, що непослідовні результати відповіді Патріка - це в основному результат його використання zsh
тоді bash
. zsh
- за замовчуванням - не розділяє слова, $(
замінені )
результатами на портативний спосіб. Тож коли він запитує, куди пішли інші файли? відповідь на це питання - ваша шкаралупа їх з'їла. Ось чому вам потрібно встановити SH_WORD_SPLIT
змінну при використанні zsh
та роботі з портативним кодом оболонки. Я вважаю його неспроможність зазначити це у своїй відповіді як жахливо оманливе.
Відповідь Wumpus не розраховує для мене - в контексті списку ?
персонаж є глобусом оболонки. Я не знаю, як ще сказати це.
Для вирішення кількох випадків результатів потрібно обмежити жадібність глобуса. Далі просто створимо тестову базу жахливих імен файлів і відобразимо їх для вас:
{ 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" $# )
}
ВИХІД
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
Тепер я буду в безпеці кожен символ , який не є /slash
, -dash
, :colon
або буквено-цифрового символу в Glob оболонки потім sort -u
список для унікальних результатів. Це безпечно, оскільки ls
вже видалило для нас будь-які символи, що не друкуються. Дивитися:
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
ВИХІД:
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'
Нижче я знову підходжу до проблеми, але використовую іншу методологію. Пам'ятайте, що - крім \0
нуля - /
символ ASCII - єдиний байт, заборонений у імені шляху. Я відкладаю глобуси тут і замість цього поєдную вказаний -d
для POSIX параметр ls
і також задану POSIX -exec $cmd {} +
конструкцію find
. Оскільки find
в природному режимі випускається лише один /
послідовно, наступне легко створює рекурсивний і надійно розмежений список файлів, включаючи всю інформацію про стоматологію для кожного запису. Уявіть собі, що ви можете зробити з чимось подібним:
#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
може бути дуже корисним - особливо, коли питання про унікальність результату.
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
Це лише найбільш портативний засіб, про який я можу придумати. З GNU ls
ви можете:
ls --quoting-style=WORD
І останнє, ось набагато простіший метод розбору,ls
який я буваю використовувати досить часто, коли потребують чисел inode:
ls -1iq | grep -o '^ *[0-9]*'
Це просто повертає числа inode - що є ще однією зручною опцією POSIX.
stat
у своїй відповіді, оскільки він фактично перевіряє наявність кожного файлу. Ваш шматочок внизу з sed
річчю не працює.
ls
в першу чергу? Те, що ви описуєте, дуже важко. Мені потрібно деконструювати це, щоб зрозуміти все це, і я відносно компетентний користувач. Ви не можете очікувати, що ваш середній Джо зможе впоратися з чимось подібним.
ls
неправильного розбору результатів були добре висвітлені у вихідному посиланні (і в багатьох інших місцях). Це питання було б розумним, якби ОП просили допомогти зрозуміти це, але натомість ОП просто намагається довести його неправильне використання - це нормально.
parsing ls is bad
. Робити for something in $(command)
і покладатися на розбиття слів, щоб отримати точні результати, погано для більшості людей, command's
які не мають простого результату.
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