Почему * не * разбирать `ls`?

Я последовательно вижу ответы, цитируя эту ссылку, в которой говорится: «Не ls Это беспокоит меня по двум причинам:

  1. Кажется, информация в этой ссылке была принята оптом с небольшим вопросом, хотя я могу выбрать хотя бы несколько ошибок в случайном чтении.

  2. Кажется, что проблемы, указанные в этой ссылке, не вызвали никакого желания найти решение.

Из первого абзаца:

… когда вы спрашиваете [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 и glob:

 for f in *; do [[ -e $f ]] || continue ... done 

Автор называет это искажением имен файлов, когда ls возвращает список имен файлов, содержащих оболочки globs, а затем рекомендует использовать оболочку glob для извлечения списка файлов!

Рассмотрим следующее:

 printf 'touch ./"%b"\n' "file\nname" "filename" | . /dev/stdin ls -1q filename file?name IFS=" " ; printf "'%s'\n" $(ls -1q) 'filename' 'file name' 

POSIX определяет операнды -1 и -q ls так:

-q-q каждый экземпляр непечатаемых символов имени файла и <tab> s как символ вопросительного знака ( '?' ). Реализации могут предоставлять эту опцию по умолчанию, если вывод относится к терминальному устройству.

-1(цифра цифра одна.) Принудительная выгрузка – одна запись в строке.

Глобус не лишен собственных проблем – ? соответствует любому символу, поэтому несколько совпадений ? результаты в списке будут совпадать с одним и тем же файлом несколько раз. Это легко справиться.

Хотя, как это сделать, дело не в этом – это не займет много времени, и показано ниже – меня интересовало, почему нет . Как я считаю, наилучший ответ на этот вопрос был принят. Я бы посоветовал вам чаще сосредотачиваться на том, чтобы рассказывать людям, что они могут делать, чем от того, что они не могут. Вы, как мне кажется, гораздо менее вероятны, по крайней мере, ошибочно.

Но зачем даже пытаться? По общему признанию, моя главная мотивация заключалась в том, что другие продолжали говорить мне, что я не мог. Я очень хорошо знаю, что вывод ls такой же регулярный и предсказуемый, как вы могли бы пожелать, пока вы знаете, что искать. Дезинформация беспокоит меня больше, чем большинство вещей.

Правда, однако, за исключением замечаний Патрика и Уумпуса Q. Wumbley (несмотря на замечательный дескриптор последнего) , я рассматриваю большую часть информации в ответах здесь как в основном правильной – оболочка glob является более простой в использовании и, как правило, более эффективно, когда дело доходит до поиска текущего каталога, чем разбора ls . Однако они, по крайней мере, по моему мнению, не оправдывают оправдания либо распространением дезинформации, приведенной в статье выше, и не являются приемлемым оправданием « никогда не разбираться ».

Обратите внимание, что противоречивые результаты ответа Патрика в основном являются результатом его использования zsh then bash . zsh – по умолчанию – не разделяется на слово $( команда замещена ) приводит к переносимости. Итак, когда он спрашивает, куда пошли остальные файлы? ответ на этот вопрос – ваша оболочка съедает их. Вот почему вам нужно установить переменную SH_WORD_SPLIT при использовании zsh и работать с переносимым кодом оболочки. Я считаю, что его отказ отметить это в его ответе как ужасно обманчивый.

Ответ Wumpus не вычисляет для меня – в контексте списка ? character это оболочка glob. Я не знаю, как еще это сказать.

Чтобы справиться с множеством результатов, вам нужно ограничить жадность глобуса. Следующее просто создаст тестовую базу ужасных имен файлов и отобразит ее для вас:

 { 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, [ \, [ \, ] ^, ] ^, _ `, _ `, ab, a b FILE COUNT: 12 

Теперь я буду -dash каждого персонажа, который не является -dash /slash , -dash :colon или альфа-числовым символом в glob shell, а затем sort -u список для уникальных результатов. Это безопасно, потому что у нас уже есть сейф для любых печатных символов для нас. Смотреть:

 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' , 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: 'ab' FILE #12: 'a b' 

Ниже я снова подхожу к проблеме, но я использую другую методологию. Помните, что – кроме \0 null – символ / 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.

9 Solutions collect form web for “Почему * не * разбирать `ls`?”

Я совсем не убежден в этом, но давайте предположим, ради аргумента, что вы могли бы , если вы готовы приложить достаточные усилия, проанализировать вывод ls надежно, даже перед лицом «противника» – кто-то который знает код, который вы написали, и намеренно выбирает имена файлов, предназначенные для его разлома.

Даже если бы вы могли это сделать, это была бы плохая идея .

Оболочка Бурна не является хорошим языком. Его нельзя использовать для чего-либо сложного, если только экстремальная переносимость важнее любого другого фактора (например, autoconf ).

Я утверждаю, что если вы столкнулись с проблемой, при которой синтаксический анализ вывода ls выглядит как путь наименьшего сопротивления для сценария оболочки, это свидетельствует о том, что все, что вы делаете, слишком сложно для оболочки, и вы должны переписать всю вещь в Perl или Python. Вот ваша последняя программа в Python:

 import os, sys for subdir, dirs, files in os.walk("."): for f in dirs + files: ino = os.lstat(os.path.join(subdir, f)).st_ino sys.stdout.write("%d %s %s\n" % (ino, subdir, f)) 

У этого нет никаких проблем с необычными символами в именах файлов – вывод неоднозначен так же, как вывод ls неоднозначен, но это не имеет значения в «реальной» программе (в отличие от такой демонстрации), которая будет использовать результат os.path.join(subdir, f) напрямую.

Не менее важно и резко контрастировать с тем, что вы написали, но это будет иметь смысл через шесть месяцев, и будет легко изменить, когда вам это нужно, чтобы сделать что-то немного другое. В качестве иллюстрации предположим, что вы обнаруживаете необходимость исключать файлы dotfiles и редактора, а также обрабатывать все в алфавитном порядке по basename:

 import os, sys filelist = [] for subdir, dirs, files in os.walk("."): for f in dirs + files: if f[0] == '.' or f[-1] == '~': continue lstat = os.lstat(os.path.join(subdir, f)) filelist.append((f, subdir, lstat.st_ino)) filelist.sort(key = lambda x: x[0]) for f, subdir, ino in filelist: sys.stdout.write("%d %s %s\n" % (ino, subdir, f)) 

Эта ссылка упоминается много, потому что информация полностью точна, и она существует там очень долгое время.


ls заменяет непечатаемые символы символами glob yes, но эти символы не указаны в фактическом имени файла. Почему это имеет значение? 2 причины:

  1. Если вы передадите это имя файла программе, это имя файла фактически не существует. Он должен был бы расширить glob, чтобы получить реальное имя файла.
  2. Файл glob может соответствовать нескольким файлам.

Например:

 $ touch a$'\t'b $ touch a$'\n'b $ ls -1 a?b a?b стоит $ touch a$'\t'b $ touch a$'\n'b $ ls -1 a?b a?b 

Обратите внимание, что у нас есть 2 файла, которые выглядят точно так же. Как вы собираетесь отличить их, если они оба представлены как a?b ?


Автор называет это искажением имен файлов, когда ls возвращает список имен файлов, содержащих оболочки globs, а затем рекомендует использовать оболочку glob для извлечения списка файлов!

Здесь есть разница. Когда вы получите glob назад, как показано, этот glob может соответствовать более чем одному файлу. Однако, когда вы повторяете результаты, соответствующие glob, вы возвращаете точный файл, а не глобус.

Например:

 $ for file in *; do printf '%s' "$file" | xxd; done 0000000: 6109 62 ab 0000000: 610a 62 ab 

Обратите внимание, как вывод xxd показывает, что $file содержит необработанные charaters \t и \n , а не ? ,

Если вы используете ls , вы получите это вместо:

 for file in $(ls -1q); do printf '%s' "$file" | xxd; done 0000000: 613f 62 a?b 0000000: 613f 62 a?b 

«Я все равно буду итерации, почему бы не использовать ls

Ваш пример, который вы дали, на самом деле не работает. Похоже, что это работает, но это не так.

Я имею в виду следующее:

  for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done 

Я создал каталог с кучей имен файлов:

 $ for file in *; do printf '%s' "$file" | xxd; done 0000000: 6120 62 ab 0000000: 6120 2062 ab 0000000: 61e2 8082 62 a...b 0000000: 61e2 8083 62 a...b 0000000: 6109 62 ab 0000000: 610a 62 ab 

Когда я запускаю свой код, я получаю следующее:

 $ for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done ./a b ./a b 

Куда останутся остальные файлы?

Попробуем это вместо этого:

 $ for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done stat: cannot stat './a?b': No such file or directory stat: cannot stat './a??b': No such file or directory ./a b ./a b stat: cannot stat './a?b': No such file or directory stat: cannot stat './a?b': No such file or directory 

Теперь давайте использовать фактический глобус:

 $ for f in *; do stat --format='%n' "./$f"; done ./ab ./ab ./a b ./a b ./ab ./a b 

С bash

Вышеприведенный пример был с моей обычной оболочкой zsh. Когда я повторяю процедуру с bash, я получаю еще один совершенно другой набор результатов с вашим примером:

Тот же набор файлов:

 $ for file in *; do printf '%s' "$file" | xxd; done 0000000: 6120 62 ab 0000000: 6120 2062 ab 0000000: 61e2 8082 62 a...b 0000000: 61e2 8083 62 a...b 0000000: 6109 62 ab 0000000: 610a 62 ab 

Радикально разные результаты с вашим кодом:

 for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done ./ab ./a b ./a b ./ab ./a b ./ab ./a b ./a b ./ab ./a b ./a b ./ab ./a b ./ab ./a b ./a b ./ab ./a b 

С оболочкой glob она отлично работает:

 $ for f in *; do stat --format='%n' "./$f"; done ./ab ./ab ./a b ./a b ./ab ./a b 

Причина, по которой bash ведет себя таким образом, восходит к одной из точек, которые я сделал в начале ответа: «Файл glob может соответствовать более чем одному файлу».

ls возвращает один и тот же glob ( a?b ) для нескольких файлов, поэтому каждый раз, когда мы расширяем этот glob, мы получаем каждый отдельный файл, который соответствует ему.


Как воссоздать список файлов, которые я использовал:

 touch 'ab' 'ab' a$'\xe2\x80\x82'ba$'\xe2\x80\x83'ba$'\t'b a$'\n'b 

Шестнадцатеричные коды являются символами NBSP UTF-8.

Попробуем немного упростить:

 $ touch a$'\n'b a$'\t'b 'ab' $ ls aba?ba?b $ IFS=" " $ set -- $(ls -1q | uniq) $ echo "Total files in shell array: $#" Total files in shell array: 4 

Видеть? Это уже неправильно. Есть 3 файла, но bash сообщает 4. Это связано с тем, что этому set даются globs, генерируемые ls которые расширяются оболочкой перед передачей в set . Вот почему вы получаете:

 $ for x ; do > printf 'File #%d: %s\n' $((i=$i+1)) "$x" > done File #1: ab File #2: ab File #3: ab File #4: a b 

Или, если вы предпочитаете:

 $ printf ./%s\\0 "$@" | > od -A n -c -w1 | > sed -n '/ \{1,3\}/s///;H > /\\0/{g;s///;s/\n//gp;s/.*//;h}' ./ab ./ab ./a\tb ./a\nb 

Вышеизложенное было выполнено на bash 4.2.45 .

Вывод ls -q не является глотком вообще. Он использует ? означает «Здесь присутствует персонаж, который нельзя отобразить напрямую». Глобусы используют ? означает «Любой символ разрешен здесь».

Глобусы имеют другие специальные символы ( * и [] по крайней мере, а внутри пары [] больше). Ни один из них не ускользнул от ls -q .

 $ touch x '[x]' $ ls -1q [x] x 

Если вы обрабатываете вывод ls -1q есть набор глобусов и расширяйте их, вы не только получите x дважды, вы пропустите [x] полностью. Как глобус, он не соответствует себе как строка.

ls -q предназначен для сохранения ваших глаз и / или терминала от сумасшедших персонажей, а не для создания чего-то, что вы можете вернуть обратно в оболочку.

Ответ прост: особые случаи, с которыми вы должны справиться, перевешивают любую возможную выгоду. Эти особые случаи можно избежать, если вы не разбираете вывод ls .

Мантра здесь никогда не доверяет файловой системе пользователя (эквивалент которой никогда не доверяет пользовательскому вводу ). Если есть метод, который будет работать всегда, со 100% уверенностью, это должен быть тот метод, который вы предпочитаете, даже если ls делает то же самое, но с меньшей определенностью. Я не буду вдаваться в технические детали, поскольку они были покрыты тердоном и Патриком . Я знаю, что из-за рисков использования ls в важной (и, возможно, дорогостоящей) транзакции, где моя работа / престиж находится на линии, я предпочту любое решение, которое не имеет степени неопределенности, если его можно избежать.

Я знаю, что некоторые люди предпочитают некоторый риск над определенностью , но я подал отчет об ошибке .

Причина, по которой люди говорят, никогда не делает что-то, не обязательно, потому что это абсолютно невозможно сделать правильно. Мы можем это сделать, но это может быть более сложным, менее эффективным как по времени, так и по времени. Например, было бы прекрасно сказать: «Никогда не создавайте большой бэкэнд для электронной коммерции в сборке x86».

Итак, теперь под вопросом: как вы продемонстрировали, вы можете создать решение, которое анализирует ls и дает правильный результат, поэтому правильность не является проблемой.

Это сложнее? Да, но мы можем скрыть это за вспомогательной функцией.

Итак, теперь к эффективности:

Космическая эффективность: ваше решение использует uniq для фильтрации дубликатов, поэтому мы не можем генерировать результаты лениво. Таким образом, либо O(1) O(n) либо оба имеют O(n) .

Эффективность времени: наилучший случай uniq использует подход hashmap, поэтому у нас все еще есть алгоритм O(n) в количестве заготовленных элементов, возможно, хотя это O(n log n) .

Теперь реальная проблема: хотя ваш алгоритм все еще не выглядит слишком плохим, я был очень осторожен в использовании элементов, а не элементов для n. Потому что это имеет большое значение. Скажем, у вас есть файл \n\n который приведет к глобусу для ?? поэтому сопоставьте каждый 2-символьный файл в списке. Смешно, если у вас есть другой файл \n\r , который также приведет к ?? а также вернуть все 2 символьных файла. Посмотрите, где это происходит? Экспоненциальное, а не линейное поведение, безусловно, квалифицируется как «худшее поведение во время выполнения». Это разница между практическим алгоритмом и тем, который вы пишете в теоретических журналах CS.

Все любят примеры правильно? Вот так. Создайте папку под названием «test» и используйте этот скрипт python в том же каталоге, где находится папка.

 #!/usr/bin/env python3 import itertools dir = "test/" filename_length = 3 options = "\a\b\t\n\v\f\r" for filename in itertools.product(options, repeat=filename_length): open(dir + ''.join(filename), "a").close() 

Единственное, что это делает, это генерировать все продукты длиной 3 для 7 символов. Математика средней школы говорит нам, что должно быть 343 файла. Ну, это должно быть очень быстро напечатать, так что давайте посмотрим:

 time for f in *; do stat --format='%n' "./$f" >/dev/null; done real 0m0.508s user 0m0.051s sys 0m0.480s 

Теперь давайте попробуем ваше первое решение, потому что я действительно не могу получить это

 eval set -- $(ls -1qrR ././ | tr ' ' '?' | sed -e '\|^\(\.\{,1\}\)/\.\(/.*\):|{' -e \ 's//\1\2/;\|/$|!s|.*|&/|;h;s/.*//;b}' -e \ '/..*/!d;G;s/\(.*\)\n\(.*\)/\2\1/' -e \ "s/'/'\\\''/g;s/.*/'&'/;s/?/'[\"?\$IFS\"]'/g" | uniq) 

здесь для работы с Linux mint 16 (что, я думаю, говорит об объемах использования этого метода).

Во всяком случае, поскольку вышеописанное довольно просто фильтрует результат после его получения, более раннее решение должно быть как минимум быстрым, чем более позднее (в нем нет тэков inode, но они ненадежны, поэтому вы отказываетесь от правильности).

Итак, как долго

 time for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f" >/dev/null; done 

брать? Ну, я действительно не знаю, потребуется некоторое время, чтобы проверить имена файлов 343 ^ 343 – я расскажу вам после жары смерти Вселенной.

Указанное намерение ОП

предисловие и обоснование исходного ответа обновлено 2015-05-18

mikeserv (OP) заявил в последнем обновлении свой вопрос: «Я считаю, что стыдно, хотя я сначала задал этот вопрос , указав источник дезинформации, и, к сожалению, самый верный ответ здесь в значительной степени вводит в заблуждение. "

Ну ладно; Я чувствую, что было довольно стыдно, что я потратил столько времени, пытаясь понять, как объяснить свой смысл, только чтобы найти это, когда я перечитаю вопрос. Этот вопрос закончился «[генерированием] обсуждения, а не ответами» ‡, и в итоге он весил ~ 18 тыс. Текста (только для одного вопроса, чтобы быть ясным), который был бы длинным даже для сообщения в блоге.

Но StackExchange – это не ваш soapbox, и это не ваш блог. Однако, по сути, вы использовали его как минимум как в обоих. Люди в конечном итоге тратили много времени, отвечая на ваш «To-Point-Out», вместо того, чтобы отвечать на актуальные вопросы людей. На этом этапе я буду отмечать этот вопрос как не подходящий для нашего формата, учитывая, что ОП прямо заявил, что он даже не был задан вопросом вообще.

На данный момент я не уверен, был ли мой ответ до конца или нет; вероятно, нет, но он был направлен на некоторые из ваших вопросов, и, возможно, это может быть полезным ответом для кого-то другого; новички берут сердце, некоторые из них «не превращаются» в «иногда», как только вы становитесь более опытными. 🙂

Как общее правило…

пожалуйста, простите оставшиеся грубые края; я потратил слишком много времени на это уже … вместо того, чтобы напрямую ссылаться на OP (как изначально предполагалось), я попытаюсь обобщить и перефразировать.

[в значительной степени переработаны из моего первоначального ответа]
после рассмотрения, я считаю, что я неправильно читаю то внимание, которое ОП ставит на вопросы, на которые я ответил; однако затронутые вопросы были подняты, и я оставил ответы в основном неповрежденными, поскольку я считаю, что они должны быть точными и решать проблемы, которые, как я видел, затронуты и в других контекстах, а также в отношении рекомендаций начинающим.

В оригинальной статье несколько раз спрашивали, почему в различных статьях давались советы, такие как «Не разбирать вывод» или «Вы никогда не должны разбирать вывод ls » и т. Д.

Мое предложенное решение проблемы заключается в том, что примеры такого рода утверждений – это просто примеры идиомы, сформулированные несколько разными способами, в которых абсолютный квантификатор сопряжен с императивом [например, «не [когда-либо] X», «[Вы должны всегда] Y», «[следует] никогда не делать Z»] для формирования заявлений, предназначенных для использования в качестве общих правил или руководящих принципов, особенно когда они даются тем, кто новичок в предмете, а не предназначены как абсолютные истины, несмотря на очевидную форму этих заявлений.

Когда вы начинаете изучать новый предмет, и если у вас есть какое-то хорошее понимание того, почему вам может понадобиться действовать иначе, рекомендуется просто следовать общепринятым общим правилам без исключения, если только под руководством кого-то более опытного что вы сами. С ростом навыков и опыта вы становитесь еще более способными определять, когда и когда правило применяется в любой конкретной ситуации. Как только вы достигнете значительного уровня опыта, вы, скорее всего, поймете, на чем основывается общее правило, и в этот момент вы можете начать использовать свое мнение относительно того, насколько и на каком уровне действуют причины, лежащие в основе правила этой ситуации, а также относительно того, есть ли, возможно, основные проблемы.

И вот тогда эксперт, возможно, предпочтет сделать что-то в нарушение «Правил». Но это не сделало бы их менее «Правилами».

И, таким образом, к обсуждаемой теме: на мой взгляд, только потому, что эксперт может нарушить это правило, не получив полностью ударов, я не вижу никакого способа, чтобы вы могли оправдать рассказчику, что «иногда» это нормально разбирать ls output, потому что: это не так . Или, по крайней мере, для новичков это не так.

Вы всегда ставите своих пешек в центр; в первом отверстии, один ход; замок при первой же возможности; рыцари перед епископами; рыцарь на ободе мрачен; и всегда убедитесь, что вы можете увидеть свой расчет до конца! (Упс, извините, устал, это за шахматы StackExchange.)

Правила, должны быть разбиты?

Когда вы читаете статью о предмете, который нацелен на читателей или, вероятно, его читают, часто вы увидите такие вещи:

  • «Вы никогда не должны делать X».
  • «Никогда не делай!»
  • «Не делай Z».
  • «Всегда нужно делать Y!»
  • «C, несмотря ни на что».

Хотя эти утверждения, судя по всему, указывают на абсолютные и вневременные правила, они не являются; вместо этого это способ изложить общие правила [aka «руководящие принципы», «эмпирические правила», «основы» и т. д.], что, по крайней мере, возможно, является одним из подходящих способов указать их для новичков, которые могут читать эти статьи. Однако только потому, что они заявлены как абсолюты, правила, безусловно, не связаны с профессионалами и экспертами [которые, скорее всего, те, кто обобщил такие правила, в первую очередь, как способ записи и передачи знаний, полученных по мере их повторения проблемы в их конкретном ремесле.]

Эти правила, разумеется, не будут раскрывать, как эксперт будет заниматься сложной или тонкой проблемой, в которой, скажем, эти правила противоречат друг другу; или в которых проблемы, которые привели к правилу, в первую очередь просто не применяются. Эксперты не боятся (или не должны бояться!) Просто нарушать правила, которые, как они знают, не имеют смысла в конкретной ситуации. Эксперты постоянно занимаются балансированием различных рисков и проблем в своем ремесле и должны часто использовать свое мнение, чтобы выбрать нарушение этих правил, чтобы сбалансировать различные факторы и не иметь возможности просто полагаться на таблицу правил, которой следует следовать. Возьмем Goto в качестве примера: были длительные, повторяющиеся дебаты о том, являются ли они вредными. (Да, никогда не используйте gotos. D)

Модальное предложение

Нечетная особенность, по крайней мере, на английском языке, и я полагаю, что на многих других языках общие правила заключаются в том, что они заявлены в той же форме, что и модальное предложение, но эксперты в области готовы дать общее правило для ситуация, все время зная, что они нарушат правило, когда это необходимо. Поэтому очевидно, что эти утверждения не должны быть эквивалентны тем же утверждениям в модальной логике.

Вот почему я говорю, что они должны просто быть идиоматичными. Вместо того, чтобы действительно быть «никогда» или «всегда», эти правила обычно служат для кодификации общих руководящих принципов, которые, как правило, уместны в широком диапазоне ситуаций, и что, когда начинающие следовали за ними слепо, скорее всего, лучшие результаты, чем новичок, решивший пойти против них без уважительной причины. Sometimes they codify rules simply leading to substandard results rather than the outright failures accompanying incorrect choices when going against the rules.

So, general rules are not the absolute modal propositions they appear to be on the surface, but instead are a shorthand way of giving the rule with a standard boilerplate implied, something like the following:

unless you have the ability to tell that this guideline is incorrect in a particular case, and prove to yourself that you are right, then ${RULE}

where, of course you could substitute "never parse ls output" in place of ${RULE}. 🙂

Oh Yeah! What About Parsing ls Output?

Well, so, given all that… i think it's pretty clear that this rule is a good one. First of all, the real rule has to be understood to be idiomatic, as explained above…

But furthermore, it's not just that you have to be very good with shell scripting to know whether it can be broken, in some particular case. It's, also, that it's takes just as much skill to tell you got it wrong when you are trying to break it in testing! And, I say confidently that a very large majority of the likely audience of such articles (giving advice like «Don't parse the output of ls !») can't do those things , and those that do have such skill will likely realize that they figure it out on their own and ignore the rule anyway.

But… just look at this question, and how even people that probably do have the skill thought it was a bad call to do so; and how much effort the author of the question spent just getting to a point of the current best example! I guarantee you on a problem that hard, 99% of the people out there would get it wrong, and with potentially very bad results! Even if the method that is decided on turns out to be a good one; until it (or another) ls parsing idea becomes adopted by IT/developer folk as a whole, withstands a lot of testing (especially the test of time) and, finally, manages to graduate to a 'common technique' status, it's likely that a lot of people might try it, and get it wrong… with disastrous consequences.

So, I will reiterate one last time…. that, especially in this case , that is why " never parse ls output!" is decidedly the right way to phrase it.

[UPDATE 2014-05-18: clarified reasoning for answer (above) to respond to a comment from OP; the following addition is in response to the OP's additions to the question from yesterday]

[UPDATE 2014-11-10: added headers and reorganized/refactored content; and also: reformatting, rewording, clarifying, and um… "concise-ifying"… i intended this to simply be a clean-up, though it did turn into a bit of a rework. i had left it in a sorry state, so i mainly tried to give it some order. i did feel it was important to largely leave the first section intact; so only two minor changes there, redundant 'but' removed, and 'that' emphasized.]

† I originally intended this solely as a clarification on my original; but decided on other additions upon reflection

‡ see https://unix.stackexchange.com/tour for guidelines on posts

Is it possible to parse the output of ls in certain cases? Конечно. The idea of extracting a list of inode numbers from a directory is a good example – if you know that your implementation's ls supports -q , and therefore each file will produce exactly one line of output, and all you need are the inode numbers, parsing them out of ls -Rai1q output is certainly a possible solution. Of course, if the author hadn't seen advice like "Never parse the output of ls" before, he probably wouldn't think about filenames with newlines in them, and would probably leave off the 'q' as a result, and the code would be subtly broken in that edge case – so, even in cases where parsing ls 's output is reasonable, this advice is still useful.

The broader point is that, when a newbie to shell scripting tries to have a script figure out (for instance) what's the biggest file in a directory, or what's the most recently modified file in a directory, his first instinct is to parse ls 's output – understandable, because ls is one of the first commands a newbie learns.

Unfortunately, that instinct is wrong, and that approach is broken. Even more unfortunately, it's subtly broken – it will work most of the time, but fail in edge cases that could perhaps be exploited by someone with knowledge of the code.

The newbie might think of ls -s | sort -n | tail -n 1 | awk '{print $2}' as a way to get the biggest file in a directory. And it works, until you have a file with a space in the name.

OK, so how about ls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//' ? Works fine until you have a file with a newline in the name.

Does adding -q to ls 's arguments help when there's a newline in the filename? It might look like it does, until you have 2 different files that contain a non-printable character in the same spot in the filename, and then ls 's output doesn't let you distinguish which of those was biggest. Worse, in order to expand the "?", he probably resorts to his shell's eval – which will cause problems if he hits a file named, for instance,

 foo`/tmp/malicious_script`bar 

Does --quoting-style=shell help (if your ls even supports it)? Nope, still displays ? for nonprintable characters, so it's still ambiguous which of multiple matches was the biggest. --quoting-style=literal ? Nope, same. --quoting-style=locale or --quoting-style=c might help if you just need to print the name of the biggest file unambiguously, but probably not if you need to do something with the file afterwards – it would be a bunch of code to undo the quoting and get back to the real filename so that you can pass it to, say, gzip.

And at the end of all that work, even if what he has is safe and correct for all possible filenames, it's unreadable and unmaintainable, and could have been done much more easily, safely, and readably in python or perl or ruby.

Or even using other shell tools – off the top of my head, I think this ought to do the trick:

 find . -type f -printf "%s %f\0" | sort -nz | awk 'BEGIN{RS="\0"} END{sub(/[0-9]* /, "", $0); print}' 

And ought to be at least as portable as --quoting-style is.

My view 🙂

  • myself also "using" the output from ls to feed the script or other programs, but I know what me doing .
  • Of course will not use a 30 char long find to getting the names of *.txt files in daily terminal work – but again, I know what me doing . Eg i know my filenames and so on…
  • anytime me creating an script for my ~/bin – eg what I want use anytime in the future for any files – IMHO – is much better to use find or pure bash globbing and such.
  • why? because it is easier and safer.

SO, use you could "parse ls" – if you know what do you doing. Otherwise rather no.

Result? Most users asking help with simple bash scripts are beginners. Theyre probably don't know what is the danger with incorrect use. Therefore in answers (StackOverflow) I prefer not parse the ls. 🙂 🙂

  • Как отсортировать лл вывод
  • Как расширить команду ls с помощью настраиваемого столбца
  • Solaris: как запустить системную команду поверх awk и объединить весь вывод в одной строке
  • Замена команды in for loop не работает
  • Нестандартная сортировка вывода ls
  • ls --color = auto (dir colors) не работает в одном каталоге
  • Как заказать несколько опций команды (ls)?
  • Как найти индекс inode любого каталога?
  • сравнить размер файла между двумя идентичными не разреженными файлами при копировании
  • Возможно ли изменить поведение команды по умолчанию?
  • только показать исходный файл и целевой файл с помощью `ls`
  • Interesting Posts

    Предоставить доступ к Интернету на сервер, доступный по SSH, но где Интернет заблокирован

    Какие инструменты могут показать мне использование ЦП, которое не отображается в top / sar?

    Восстановление состояния памяти / помененного состояния при возобновлении спящего режима

    Как извлечь строку из файла в определенном состоянии

    Как включить учет свопинга для группы памяти в Archlinux?

    Ошибка при установке nginx для нескольких приложений ReactJS на одном сервере?

    Объединение нескольких выходов grep в переменную

    (Linux Mint-17) Не удается подключиться к беспроводной сети

    Возможно ли «tail -f / dev / vcs»?

    Как установить кактусы на SUSE Linux Enterprise Server?

    Сравнить строки в файле с общим последним столбцом, но другим в другом столбце

    Хотя цикл для целых чисел с пользовательским вводом

    gnu-параллельная многопоточная труба использует мало CPU%, но киоски сервера

    Восстановить права sudo

    Как отобразить управляющие символы (^ C, ^ D, ^ [, …) по-разному в оболочке

    Linux и Unix - лучшая ОС в мире.