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

Я последовательно вижу ответы, цитирующие эту ссылку , в которых указано окончательно "Не разбирайте ls! " Это беспокоит меня по нескольким причинам:

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

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

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

  

... когда вы запрашиваете [ls] для списка   файлов, существует огромная проблема: Unix позволяет практически любому персонажу в   имя файла, включая пробелы, символы новой строки, запятые, символы труб и   почти все, что вы когда-либо пытались использовать в качестве разделителя, кроме   NUL. ... ls разделяет имена файлов с новыми символами. Это отлично   пока у вас не будет файла с новой строкой в ​​его имени. И так как я не   знать о любой реализации ls, которая позволяет вам завершить работу   имена файлов с символами NUL вместо строк новой строки, это оставляет нас   не удалось получить список имен файлов с помощью ls.

Баммер, да? Как ever мы можем обрабатывать переносимый новой строкой набор данных для данных, которые могут содержать символы новой строки? Ну, если люди, отвечающие на вопросы на этом веб-сайте, ежедневно не делали такого рода вещи, я мог бы подумать, что у нас были какие-то проблемы.

Правда, большинство реализаций 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 иногда искажает данные вашего файла (в нашем   case, он превратил символ \n между словами "a" и    «новая строка» в вопросительный знак ? ...

     

...

     

Если вы просто хотите перебрать все файлы в текущем   , используйте цикл for и glob:

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

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

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

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 - (цифра цифра одна.) Принудительный вывод для одной записи в строке.

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

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

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

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

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

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

Чтобы обработать несколько случаев с результатами, вам необходимо ограничить жадность 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" $# )
}

OUTPUT

`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 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.

172 голоса | спросил mikeserv 12 Mayam14 2014, 05:42:00

9 ответов


154

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

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

Оболочка Bourne не является хорошим языком. Его нельзя использовать для чего-либо сложного, если только экстремальная переносимость важнее любого другого фактора (например, 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))
ответил zwol 13 Mayam14 2014, 02:55:42
163

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

A


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

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

Например:

$ 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                                  a.b
0000000: 610a 62                                  a.b

Обратите внимание, что вывод xxd показывает, что $file содержит исходные символы char \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                                  a b
0000000: 6120 2062                                a  b
0000000: 61e2 8082 62                             a...b
0000000: 61e2 8083 62                             a...b
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

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

$ 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                        
./a b
./a  b
./a b
./a b
./a b
./a
b

С bash

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

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

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

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

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

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

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

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

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


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

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

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

ответил Patrick 12 Mayam14 2014, 05:57:35
51

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

$ touch a$'\n'b a$'\t'b 'a b'
$ ls
a b  a?b  a?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: a b
File #2: a b
File #3: a    b
File #4: a
b

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

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

Вышеуказанное было выполнено на bash 4.2.45.

ответил terdon 12 Mayam14 2014, 06:37:55
46

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

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

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

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

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

ответил Wumpus Q. Wumbley 12 Maypm14 2014, 20:01:03
38

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

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

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

ответил Braiam 12 Maypm14 2014, 17:16:17
25

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

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

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

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

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

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

Теперь реальная проблема: хотя ваш алгоритм все еще не выглядит слишком плохим, я действительно старался использовать элементы закупленные , а не элементы для n. Потому что это имеет большое значение. Предположим, что у вас есть файл \n\n, который приведет к glob для ??, чтобы соответствовать каждому 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 - я расскажу вам после жары смерти Вселенной.

ответил Voo 14 Maypm14 2014, 19:11:53
23

Описанное намеренное намерение OP

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

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

Хорошо, ладно; Я чувствую, что было довольно стыдно, что я потратил столько времени, пытаясь понять, как объяснить свой смысл, только чтобы найти , что , когда я перечитываю вопрос. Этот вопрос закончил «дискуссию», а не «ответы» â € ¡, и в итоге она оказалась в ~ 18K текста (только для одного вопроса ), который был бы длинным даже для сообщения в блоге.

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

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

В качестве общего правила ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Таким образом, общие правила не являются абсолютными модальными суждениями, которые они кажутся на поверхности, но вместо этого являются сокращенным способом предоставления правила со стандартным шаблоном подразумеваемого следующего:

, если у вас нет возможности сказать, что это руководство неверно в конкретном случае, и убедитесь, что вы правы, затем $ {RULE}

, где, конечно, вы можете заменить «никогда не разбирать ls output» вместо $ {RULE}. :)

О да! Что О Разбор ls Вывод?

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

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

Но ... просто посмотрите на этот вопрос, и как даже люди, которые, вероятно, имеют навык, думали, что это плохой вызов; и сколько усилий автор вопроса потратил, просто добравшись до точки настоящего лучшего примера! Я гарантирую вам, что трудно, что 99% людей там ошибаются, и с потенциальными very плохими результатами! Даже если выбранный метод окажется хорошим; пока (или другой) ls идея синтаксического анализа не будет принята IT /разработчиком в целом, выдерживает много испытаний (особенно тест времени) и, наконец, умудряется окончить общий технику ", вполне вероятно, что многие люди могут попробовать это, и ошибиться ... с катастрофическими последствиями.

Итак, я повторю последний раз ... что, особенно в этом случае , , что почему " никогда parse ls! " это явно right способ его фразы.

[ОБНОВЛЕНИЕ 2014-05-18: разъясненное обоснование ответа (см. выше) для ответа на комментарий от OP; следующее дополнение относится к добавлению OP к вопросу со вчерашнего дня]

[ОБНОВЛЕНИЕ 2014-11-10: добавлены заголовки и реорганизованный /реорганизованный контент; а также: переформатирование, переписывание, уточнение и ум ... «лаконичный» ... я намеревался просто очистить, хотя это и превратилось в переделку. я оставил его в жалком состоянии, поэтому я в основном пытался дать ему какой-то порядок. я действительно чувствовал, что важно оставить первый раздел в целости; так что только два незначительных изменения там, избыточное, но «удалено» и «это» подчеркнуто.]

«Я первоначально намеревался это исключительно как пояснение к моему оригиналу; но принял решение о других дополнениях при отражении

â € ¡см. https://unix.stackexchange.com/tour для рекомендаций по сообщениям

ответил shelleybutterfly 15 Mayam14 2014, 09:01:27
14

Можно ли в некоторых случаях анализировать вывод ls? Конечно. Идея извлечь список индексов из каталога - хороший пример - если вы знаете, что ls вашей реализации поддерживает -q, и поэтому каждый файл будет производить ровно один строка вывода, и все, что вам нужно, это номера индексных дескрипторов, разбор их из вывода ls -Rai1q, безусловно, является возможным решением. Конечно, если автор не видел совета, подобного «Никогда не анализируйте вывод ls» раньше, он, вероятно, не думал бы о именах файлов с новыми символами в них и, вероятно, оставил бы «q» в результате, а код будет тонко разбит в этом случае края - поэтому даже в тех случаях, когда вывод синтаксического анализа ls является разумным, этот совет по-прежнему полезен.

Более широкая точка заключается в том, что когда сценарий новичков в оболочку пытается определить сценарий (например), какой самый большой файл в каталоге или какой последний измененный файл в каталоге, его первый инстинкт заключается в том, чтобы parse ls - понятно, потому что ls - одна из первых команд, которые узнает новичок.

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

Новичок может подумать о ls -s | sort -n | tail -n 1 | awk '{print $2}' как способ получить самый большой файл в каталоге. И он работает, пока у вас нет файла с пробелом в имени.

ОК, так как насчет ls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//'? Работает нормально, пока у вас нет файла с новой строкой в ​​имени.

Помогает ли добавление аргументов -q в ls, когда в имени файла есть новая строка? Это может выглядеть так, пока у вас нет двух разных файлов, содержащих непечатаемый символ в том же месте в имени файла, а затем вывод ls не позволяет вам различать, какой из этих был самым большим. Хуже того, чтобы расширить «?», Он, вероятно, прибегает к eval своей оболочки), что вызовет проблемы, если он попадет в файл с именем, например,

foo`/tmp/malicious_script`bar

Помогает ли --quoting-style=shell (если ваш ls поддерживает его)? Нет, все еще отображается? для непечатаемых персонажей, поэтому все еще неоднозначно, что из нескольких матчей было самым большим. --quoting-style=literal? Нет, то же самое. --quoting-style=locale или --quoting-style=c может помочь, если вам просто нужно напечатать имя самого большого файла однозначно, но, вероятно, нет, если вы нужно что-то сделать с файлом после этого - это будет куча кода, чтобы отменить цитату и вернуться к истинному имени файла, чтобы вы могли передать ее, скажем, gzip.

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

Или даже используя другие инструменты оболочки - от верхней части головы, я думаю, что это должно сделать трюк:

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

И должен быть, по крайней мере, переносимым, как --quoting-style.

ответил godlygeek 16 Maypm14 2014, 19:50:06
2

Мой взгляд:)

  • сам также «использовал» вывод из ls для подачи сценария или других программ , но Я знаю, что я делаю .
  • Конечно, не будет использовать длинный find длиной 30 символов для получения имен *.txt файлов в ежедневной работе терминала , но снова , Я знаю, что я делаю . Например. я знаю свои имена и т. д.
  • В любое время я создаю скрипт для моего ~/bin - например. то, что я хочу использовать в любое время в будущем для любых файлов - IMHO - гораздо лучше использовать find или чистые bash globing и т. д.
  • почему? потому что это проще и безопаснее.

SO, вы можете «разобрать ls» - если вы знаете, что вы делаете. В противном случае нет.

Результат? Большинство пользователей, обращающихся за помощью к простым скриптам bash, являются новичками. Вероятно, они не знают, в чем опасность с неправильным использованием. Поэтому в ответах (StackOverflow) я предпочитаю не анализировать ls. :):)

ответил jm666 22 MarpmWed, 22 Mar 2017 14:15:10 +03002017-03-22T14:15:10+03:0002 2017, 14:15:10

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132