Что делает $ (ls * .txt)?

В терминале unix, если да:

egrep "Stuff" $(ls *.txt)

Результат очевиден. Но что делает

$(ls *.txt) самостоятельно? Я вижу, что могу воспроизвести эффект

egrep "Stuff" *.txt

Итак, что $(ls *.txt)?

5
задан 10 August 2017 в 11:17

10 ответов

"что делает $(ls *.txt) do"

Короткий ответ

$(ls *.txt) собирает список имен файлов, а затем управляет ими. Не используйте это.

Более длинный ответ

ls предназначен для считывания человеком. Как писал один из сопровождающих ls, в ответ на вопрос, почему ls не предложит вариант --null: если бы мы это сделали, то это интерфейс, который мы будем использовать. Однако ls действительно является инструментом прямого потребления человеком, и в этом случае дальнейшая обработка менее полезна. Для дальнейшей обработки find (1) более подходит. [курсив добавил] Другими словами, использование ls для чего-либо, кроме отображения для людей, просто не поддерживается. ls сопровождающие, например, недавно изменили формат вывода по умолчанию на то, что они, хотя и более удобны для людей без предупреждения. Итак, следуйте их советам: если вы собираетесь выполнять дальнейшую обработку имен файлов, используйте find вместо ls. $(...) - подстановка команд. Одним из последствий использования подстановки команд является то, что конечные символы новой строки удаляются. Если последнее имя файла в списке содержит завершающие символы новой строки, они будут удалены. Поскольку $(...) не является двойным кавычком, текст, который он производит, будет подчиняться: Разделение слов Расширение Pathname. Результатом обоих этих действий является дальнейшее искажение имен файлов. не говоря уже о проблемах с именами файлов, начиная с - из-за отсутствия -- (как для ls, так и egrep, поскольку Ubuntu egrep, являющийся реализацией GNU, принимает параметры даже после аргументов без опционов, если только POSIXLY_CORRECT находится в среде). также, если какой-либо из этих файлов txt имел тип каталога, ls перечислил бы их содержимое вместо себя. и если файлов txt нет, вывод ls будет пустым (хотя вы увидите ошибку о отсутствующем файле *.txt), а поскольку egrep не получит аргумент файла, он будет искать материал в своем стандартном вводе (и, казалось бы, зависании).

Пример

Давайте создадим 4 файла, содержащие Stuff в нашем каталоге:

$ echo Stuff | tee file1 file2 'a b c.txt' 'f* .txt' Stuff $ ls -Q "a b c.txt" "file1" "file2" "f* .txt"

Теперь давайте запустим команду egrep:

$ egrep "Stuff" $(ls *.txt) grep: a: No such file or directory grep: b: No such file or directory grep: c.txt: No such file or directory file1:Stuff file2:Stuff f* .txt:Stuff grep: .txt: No such file or directory

Обратите внимание, что мы получаем 4 сообщения об ошибках о несуществующих файлах. Это связано с «что делает $(ls *.txt)» . Результат также показывает совпадения с двумя файлами file1 и file2, которые не должны были быть обысканы, потому что они не заканчиваются на .txt. Это из-за расширения _pathname.

Правильно написанная команда производит два успешных совпадения и отсутствие ошибок:

$ egrep -- "Stuff" *.txt a b c.txt:Stuff f* .txt:Stuff

Рекомендуемое решение

Использование:

egrep -- "Stuff" *.txt

или POSIXly:

grep -E -- "Stuff" *.txt

или:

grep -E -e Stuff -- *.txt

Это будет работать с любым именем файла и не имеет ограничений в ls.

28
ответ дан 18 July 2018 в 08:46

Этот вопрос, похоже, мало полезен в реальном мире, но

. Лучший способ получить полезный результат:

echo $(ls *.txt)

Или просто

ls *.txt

Здесь будут перечислены все файлы с расширением .txt. Чтобы найти текстовые файлы, содержащие определенную строку,

egrep -l "Stuff" *.txt

Для получения дополнительной информации о инструментах поиска сделайте

man egrep
0
ответ дан 18 July 2018 в 08:46
$( ... )

Является заменой команды, она назначает вывод одной или нескольких команд в качестве ввода другой команды. Старый синтаксис: ` ... `.

Различие заключается в том, что ваши команды будут запускаться в разных оболочках:

echo $BASHPID $( echo $BASHPID ) 5718 7138

На самом деле он вызывает подоболочку:

echo $BASH_SUBSHELL $( echo $BASH_SUBSHELL ) 0 1
2
ответ дан 18 July 2018 в 08:46

Команда $( ) запускает команду ls *.txt и возвращает STDOUT команды.

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

egrep 'Stuff' *.txt работает, как вы сказали, за исключением файлов, названных как-то вроде A File.txt. Выход ls для ввода программы неразумный. См. Причины $( ls *.txt) имена файлов mishandles с пробелами и другими забавными символами. find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' является более пуленепробиваемым способом.

Надуманный пример расширения / путаницы оболочки оболочки в ответ на @ 8bittree:

$ /bin/ls -l -b total 36 -rw------- 1 w3 walt 2 Aug 11 13:41 A\ \\012\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:42 A\ \n\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:40 A\ File.txt $ grep "STUFF" $(ls *.txt) grep: A: No such file or directory grep: \012: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep '1' ./A File.txt:1
6
ответ дан 18 July 2018 в 08:46

Ничего себе, на мгновение я подумал, что вы задаетесь вопросом о `$(ls *.txt)` (с $(..) плюс backquotes)

По мере ответа на этот вопрос я просто предупрежу вас об одном. Вот последовательность команд, чтобы показать вам, почему это может быть опасно:

set -x Чтобы увидеть, что выполнено touch 'rm -f *.txt' Создайте текстовый файл с именем «rm -f * .txt», я использую простой цитаты здесь, поэтому Bash не будет расширять подстановочные знаки и пробелы. `$(ls *.txt)` Вот сложная часть. Команда ls *.txt будет запущена, а затем перенаправит результат на STDOUT. Этот результат rm -f *.txt будет теперь выполнен из-за обратных запросов. Итак, rm удалит все текстовые файлы, в нашем случае файл rm -f *.txt

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

3
ответ дан 18 July 2018 в 08:46

"что делает $(ls *.txt) do"

Короткий ответ

$(ls *.txt) собирает список имен файлов, а затем управляет ими. Не используйте это.

Более длинный ответ

ls предназначен для считывания человеком. Как писал один из сопровождающих ls, в ответ на вопрос, почему ls не предложит вариант --null: если бы мы это сделали, то это интерфейс, который мы будем использовать. Однако ls действительно является инструментом прямого потребления человеком, и в этом случае дальнейшая обработка менее полезна. Для дальнейшей обработки find (1) более подходит. [курсив добавил] Другими словами, использование ls для чего-либо, кроме отображения для людей, просто не поддерживается. ls сопровождающие, например, недавно изменили формат вывода по умолчанию на то, что они, хотя и более удобны для людей без предупреждения. Итак, следуйте их советам: если вы собираетесь выполнять дальнейшую обработку имен файлов, используйте find вместо ls. $(...) - подстановка команд. Одним из последствий использования подстановки команд является то, что конечные символы новой строки удаляются. Если последнее имя файла в списке содержит завершающие символы новой строки, они будут удалены. Поскольку $(...) не является двойным кавычком, текст, который он производит, будет подчиняться: Разделение слов Расширение Pathname. Результатом обоих этих действий является дальнейшее искажение имен файлов. не говоря уже о проблемах с именами файлов, начиная с - из-за отсутствия -- (как для ls, так и egrep, поскольку Ubuntu egrep, являющийся реализацией GNU, принимает параметры даже после аргументов без опционов, если только POSIXLY_CORRECT находится в среде). также, если какой-либо из этих файлов txt имел тип каталога, ls перечислил бы их содержимое вместо себя. и если файлов txt нет, вывод ls будет пустым (хотя вы увидите ошибку о отсутствующем файле *.txt), а поскольку egrep не получит аргумент файла, он будет искать материал в своем стандартном вводе (и, казалось бы, зависании).

Пример

Давайте создадим 4 файла, содержащие Stuff в нашем каталоге:

$ echo Stuff | tee file1 file2 'a b c.txt' 'f* .txt' Stuff $ ls -Q "a b c.txt" "file1" "file2" "f* .txt"

Теперь давайте запустим команду egrep:

$ egrep "Stuff" $(ls *.txt) grep: a: No such file or directory grep: b: No such file or directory grep: c.txt: No such file or directory file1:Stuff file2:Stuff f* .txt:Stuff grep: .txt: No such file or directory

Обратите внимание, что мы получаем 4 сообщения об ошибках о несуществующих файлах. Это связано с «что делает $(ls *.txt)» . Результат также показывает совпадения с двумя файлами file1 и file2, которые не должны были быть обысканы, потому что они не заканчиваются на .txt. Это из-за расширения _pathname.

Правильно написанная команда производит два успешных совпадения и отсутствие ошибок:

$ egrep -- "Stuff" *.txt a b c.txt:Stuff f* .txt:Stuff

Рекомендуемое решение

Использование:

egrep -- "Stuff" *.txt

или POSIXly:

grep -E -- "Stuff" *.txt

или:

grep -E -e Stuff -- *.txt

Это будет работать с любым именем файла и не имеет ограничений в ls.

28
ответ дан 24 July 2018 в 19:10
  • 1
    довольно четкий подход – solfish 10 August 2017 в 10:42
  • 2
    Ваша первая мысль об изменении имен файлов верна только тогда, когда вывод идет на терминал, а не когда он идет на канал, как в этом случае. – Stéphane Chazelas 11 August 2017 в 09:40
  • 3
    @ StéphaneChazelas Интересно. Я тоже это вижу. Благодарю. Я просто обновил ответ, чтобы заменить этот оператор цитатой от одного из сопровождающих ls, заявив, что ls не должен использоваться ни для чего, кроме потребления человеком. – John1024 18 August 2017 в 01:41

Этот вопрос, похоже, мало полезен в реальном мире, но

. Лучший способ получить полезный результат:

echo $(ls *.txt)

Или просто

ls *.txt

Здесь будут перечислены все файлы с расширением .txt. Чтобы найти текстовые файлы, содержащие определенную строку,

egrep -l "Stuff" *.txt

Для получения дополнительной информации о инструментах поиска сделайте

man egrep
0
ответ дан 24 July 2018 в 19:10
  • 1
    Как echo $(ls *.txt) больше используется , чем echo *.txt. Это определенно намного менее надежно, потому что большинство пунктов, перечисленных в ответе Джона, также применяются там. Обратите внимание, что ls *.txt не не перечисляет все файлы с расширением .txt . Для этого вам понадобится ls -d -- *.txt. – Stéphane Chazelas 11 August 2017 в 13:52
$( ... )

Является заменой команды, она назначает вывод одной или нескольких команд в качестве ввода другой команды. Старый синтаксис: ` ... `.

Различие заключается в том, что ваши команды будут запускаться в разных оболочках:

echo $BASHPID $( echo $BASHPID ) 5718 7138

На самом деле он вызывает подоболочку:

echo $BASH_SUBSHELL $( echo $BASH_SUBSHELL ) 0 1
2
ответ дан 24 July 2018 в 19:10

Команда $( ) запускает команду ls *.txt и возвращает STDOUT команды.

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

egrep 'Stuff' *.txt работает, как вы сказали, за исключением файлов, названных как-то вроде A File.txt. Выход ls для ввода программы неразумный. См. Причины $( ls *.txt) имена файлов mishandles с пробелами и другими забавными символами. find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' является более пуленепробиваемым способом.

Надуманный пример расширения / путаницы оболочки оболочки в ответ на @ 8bittree:

$ /bin/ls -l -b total 36 -rw------- 1 w3 walt 2 Aug 11 13:41 A\ \\012\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:42 A\ \n\ File.txt -rw------- 1 w3 walt 2 Aug 11 13:40 A\ File.txt $ grep "STUFF" $(ls *.txt) grep: A: No such file or directory grep: \012: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory grep: A: No such file or directory grep: File.txt: No such file or directory $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep 'Stuff' $ find . -maxdepth 1 -type f -iname '*.txt' -print0 | xargs -0 egrep '1' ./A File.txt:1
6
ответ дан 24 July 2018 в 19:10
  • 1
    Кроме того, использование find 's -exec действия, а не -print0 + xargs -0 было бы более эффективным, хотя я соглашусь, что его синтаксис назойливый. В этом случае это будет find [...] -exec egrep 'Stuff' {} \; или find [...] -exec egrep 'Stuff' {} +; во втором случае grep обрабатывает сразу несколько файлов, поэтому результат будет иметь префикс пути файла (который может быть отключен флагом grep -h), – Aaron 10 August 2017 в 17:17
  • 2
    @Aaron Я никогда не видел & quot; {} + & quot; синтаксис поиска, очень интересный. Использование "-exec egrep 'Stuff' {} \; & quot; без + фактически будет менее эффективным, так как он будет отказываться от нового процесса grep для каждого файла. Кроме того, я обычно запускаю grep с дополнительным файлом / dev / null, поэтому я получаю имена файлов, перечисленные на выходе, даже если в команде grep попадает только 1 файл. например найти ... -print0 | xargs -0 egrep 'Stuff' / dev / null – Aner 11 August 2017 в 00:08
  • 3
    @Aner относительно эффективности {} \;, вы правы. Кроме того, флаг grep -H короче, чем /dev/null;) Всегда полезно перечитывать команду --help (если не ее человек) время от времени, такие лакомства возникают каждый раз версии. – Aaron 11 August 2017 в 01:14
  • 4
    find -print0 | xargs не более устойчив, чем grep -E *txt. Globs абсолютно безопасны с любым именем файла, которое вы бросаете на них. Ваш пример find также будет работать, да, но он не имеет никакой пользы от glob; это всего лишь много более сложный. – terdon♦ 11 August 2017 в 12:10
  • 5
    Это не пример того, что glob путается пробелами, это просто плохой пример разбора ls. Использование globs означает использование шаблона glob, а не результаты работы ls в этом шаблоне glob. Как указывал 8bittree, grep STUFF *txt будет работать нормально, как for i in *txt; do grep STUFF "$i"; done. Нет имени файла, которое сделает глобусный дроссель. Для этого они предназначены. – terdon♦ 13 August 2017 в 13:31

Ничего себе, на мгновение я подумал, что вы задаетесь вопросом о `$(ls *.txt)` (с $(..) плюс backquotes)

По мере ответа на этот вопрос я просто предупрежу вас об одном. Вот последовательность команд, чтобы показать вам, почему это может быть опасно:

set -x Чтобы увидеть, что выполнено touch 'rm -f *.txt' Создайте текстовый файл с именем «rm -f * .txt», я использую простой цитаты здесь, поэтому Bash не будет расширять подстановочные знаки и пробелы. `$(ls *.txt)` Вот сложная часть. Команда ls *.txt будет запущена, а затем перенаправит результат на STDOUT. Этот результат rm -f *.txt будет теперь выполнен из-за обратных запросов. Итак, rm удалит все текстовые файлы, в нашем случае файл rm -f *.txt

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

3
ответ дан 24 July 2018 в 19:10
  • 1
    Так как это было добавлено другим пользователем, и поскольку `...` не имеет никаких проблем в заголовках вопросов, я удалил лишние обратные ссылки. – muru 10 August 2017 в 11:18
  • 2
    Да, ты прав, название изменилось! Во всяком случае, я думаю, это никому не повредит, чтобы понять, что может выполнять двойное командование. – Sayardiss 10 August 2017 в 11:54

Другие вопросы по тегам:

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