Я постоянно вижу ответы, цитирующие эту ссылку с указанием "Не разбирайся ls!" Это беспокоит меня по нескольким причинам:
Кажется, что информация в этой ссылке была принята оптом с небольшим вопросом, хотя я могу выделить по крайней мере несколько ошибок при случайном чтении.
Также кажется, что проблемы, указанные в этой ссылке, не вызвали желания найти решение.
Из первого абзаца:
... когда вы запрашиваете
[ls]список файлов, возникает огромная проблема: Unix позволяет использовать практически любой символ в имени файла, включая пробелы, символы новой строки, запятые, символы канала и почти все, что вы когда-либо пытались использовать в качестве разделитель, кроме NUL. ...lsразделяет имена файлов с помощью новых строк. Это нормально, пока у вас нет файла с новой строкой в названии. И поскольку я не знаю какой-либо реализацииls, позволяющей вам завершать имена файлов символами NUL вместо символов новой строки, мы не можем безопасно получить список имен файловls.
Облом, верно? Как всегда мы можем справиться с новой строки завершается перечисленный набор данных для данных , которые могут содержать символы новой строки? Ну, если бы люди, отвечающие на вопросы на этом сайте, не делали такого рода вещи ежедневно, я мог бы подумать, что у нас были некоторые проблемы.
Правда в том, что большинство lsреализаций на самом деле предоставляют очень простой API для анализа их вывода, и мы все делали это все время, даже не осознавая этого. Мало того, что вы можете завершить имя файла с нуля, вы также можете начать с нуля или с любой другой произвольной строки, которую вы можете пожелать. Более того, вы можете назначить эти произвольные строки для каждого типа файла . Пожалуйста примите к сведению:
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 - единственный байт, запрещенный в имени пути. Здесь я откладываю globs и вместо этого комбинирую указанную -dдля POSIX опцию для lsи указанную -exec $cmd {} +для POSIX конструкцию для 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 который я использую довольно часто, когда нужны номера инодов:
ls -1iq | grep -o '^ *[0-9]*'
Это просто возвращает номера инодов - это еще одна удобная опция, указанная в 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,18 сtime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'= 1,28 с