Почему команда “ ls | файл & Rdquo; Работа?

Измените эту строку в /etc/hosts:

127.0.1.1     your-old-hostname

на:

127.0.1.1     your-new-hostname
1
задан 12 July 2016 в 23:49

7 ответов

Поскольку, как вы говорите, вход file должен быть именем файла. Однако вывод ls - это просто текст. То, что это список имен файлов, не меняет того факта, что это просто текст, а не расположение файлов на жестком диске.

Когда вы видите вывод, напечатанный на экране, то, что вы видите, является текстом. Является ли этот текст стихотворением или списком имен файлов, не имеет никакого значения для компьютера. Все, что он знает, это то, что это текст. Вот почему вы можете передавать вывод ls в программы, которые принимают текст в качестве входных данных (хотя вы действительно этого не должны делать):

$ ls / | grep etc
etc

Итак, чтобы использовать вывод команды, которая перечисляет имена файлов в виде текста (например, ls или find) в качестве ввода для команды, которая принимает имена файлов, вам нужно использовать некоторые трюки. Типичным инструментом для этого является xargs:

$ ls
file1 file2

$ ls | xargs wc
 9  9 38 file1
 5  5 20 file2
14 14 58 total

Как я уже говорил ранее, вы действительно не хотите разбирать вывод ls. Что-то вроде find лучше (print0 печатает \0 вместо newilne после каждого имени файла, а -0 из xargs позволяет иметь дело с таким вводом, это трюк, чтобы заставить ваши команды работать с именами, содержащими новые строки):

$ find . -type f -print0 | xargs -0 wc
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

У этого также есть свой способ сделать это, без необходимости xargs:

$ find . -type f -exec wc {} +
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

Наконец, вы также можете использовать цикл оболочки. Однако обратите внимание, что в большинстве случаев xargs будет намного быстрее и эффективнее. Например:

$ for file in *; do wc "$file"; done
 9  9 38 file1
 5  5 20 file2
18
ответ дан 23 May 2018 в 08:27
  • 1
    Боковая проблема заключается в том, что file, по-видимому, не читает stdin, если не указано явное место -: сравните file foo, echo foo | file и echo foo | file -; на самом деле это, вероятно, причина использования сообщения в случае OPs (т. е. на самом деле это не потому, что вывод ls является «просто текстом», а скорее потому, что список аргументов в file пуст) – steeldriver 6 July 2016 в 20:01
  • 2
    @steeldriver да. AFAIK - это случай для всех программ, которые ожидают файлы, а не текст в качестве входных данных. По умолчанию они игнорируют stdin. Обратите внимание, что echo foo | file - фактически не запускает file в файле foo, а в потоке stdin. – terdon♦ 6 July 2016 в 20:06
  • 3
    Ну, есть странные утки (?!), Подобные cat, за исключением stdin без -, за исключением случаев, когда аргументы файла также я думаю? – steeldriver 6 July 2016 в 20:32
  • 4
    Этот ответ не объясняет разницу между аргументами stdin и командной строки, и поэтому, несмотря на то, что он больше на точке, чем принятый ответ, по-прежнему глубоко вводит в заблуждение по той же причине. – zwol 6 July 2016 в 20:52
  • 5
    @terdon Я думаю, что это серьезная ошибка в этом случае. «файл (1) принимает список файлов для работы в качестве аргументов командной строки, а не как стандартный ввод» является фундаментальным , чтобы понять, почему команда OP не работает, а различие является основополагающим для сценариев оболочки вообще; вы не делаете им никакой пользы, замаскивая ее. – zwol 6 July 2016 в 20:57
узнал, что '|' (конвейер) предназначен для перенаправления вывода из команды на вход другого.

Он не «перенаправляет» вывод, а выводит результат программы и использует его как вход, а файл не принимает входные данные, а имена файлов в качестве аргументов, которые затем проверяются. Переопределения не передают эти имена файлов в качестве аргументов, которые ни один из них не выполняет, а что дальше.

Что вы можете сделать, это прочитать имена файлов из файла с опцией --files-from, если у вас есть файл, список которого все файлы, которые вы хотите протестировать, иначе просто передайте пути к вашим файлам в качестве аргументов.

6
ответ дан 23 May 2018 в 08:27

Принятый ответ объясняет, почему команда pipe не работает сразу, а с помощью команды file * предлагает простое, простое решение.

Я хотел бы предложить еще одну альтернативу, которая может пригодиться в какой-то момент. Трюк использует символ обратного хода (`). Здесь подробно рассмотрен обратный ход. Короче говоря, он принимает вывод команды, заключенной в backticks, и заменяет ее как строку на оставшуюся команду.

Итак, find `ls` выведет вывод команды ls и заменит его как аргументы для команды find. Это длиннее и сложнее, чем принятое решение, но варианты этого могут быть полезны в других ситуациях.

6
ответ дан 23 May 2018 в 08:27
  • 1
    Я читаю книгу об использовании командной строки в Linux (сомнение пришло от меня, когда я экспериментировал с ней), и совпадение, которое я только что прочитал о «подстановке команд». Вы можете использовать либо $ (команда) , либо command (не можете найти код обратной косой черты на моем телефоне), чтобы расширить вывод команды в bash и использовать это как параметр для других команд. Действительно полезно, хотя использование этого в этом случае (с ls ) все равно приведет к некоторым проблемам из-за специальных символов в некоторых именах файлов. – IanC 7 July 2016 в 03:28
  • 2
    @IanC К сожалению, большинство книг и руководств по поводу bash - это мусор, испорченный плохой практикой, устаревший синтаксис, тонкие ошибки; (единственными) заслуживающими доверия ссылками являются разработчики bash, то есть руководство и канал #bash IRC на freenode (также проверьте ресурсы, связанные в канале тема). – ignis 8 July 2016 в 00:25
  • 3
    Использование подстановки команд может быть действительно полезно время от времени, но в этом контексте оно довольно извращенное - особенно с ls. – Joe 17 July 2016 в 10:57
  • 4

Выход ls через канал представляет собой сплошной блок данных с 0x0a, разделяющим каждую строку, то есть символ перевода строки, и file получает это как один параметр, где он ожидает, что несколько символов будут работать на одном из time.

Как правило, никогда не используйте ls для генерации источника данных для других команд - в один прекрасный день он будет транслироваться в rm, а затем у вас проблемы!

Лучше использовать цикл, например for i in *; do file "$i" ; done, который будет выдавать желаемый результат. Кавычки существуют в случае имен файлов с пробелами.

5
ответ дан 23 May 2018 в 08:27
  • 1
    проще: file * ;-) – Wayne_Yux 6 July 2016 в 19:05
  • 2
    @IanC Я действительно не могу подчеркнуть, что синтаксический анализ вывода ls является очень, очень плохой идеей . Не только потому, что вы можете передать его чему-то вредоносному, например rm, что более важно, потому что оно разбивается на любое нестандартное имя файла. – terdon♦ 6 July 2016 в 19:49
  • 3
    Первый абзац находится где-то между вводящим в заблуждение и прямой бессмыслицей. Линейные переводы не имеют отношения. Второй абзац прав по неправильной причине. Плохо разбирать ls, но не потому, что это может быть каким-то волшебным образом «пропущено». к rm. – John Kugelman 6 July 2016 в 20:23
  • 4
    Использует ли rm имена файлов со стандартного ввода? Думаю, нет. Кроме того, как правило, ls был одним из основных примеров источника данных для использования конвейеров Unix с самого начала Unix. Вот почему он по умолчанию использует простой однофайловое имя без атрибутов или украшений, когда его вывод является каналом, в отличие от обычного стандартного форматирования, когда вывод является терминалом. – davidbak 6 July 2016 в 20:42
  • 5
    @DewiMorgan Этот сайт в основном предназначен для нетехнической аудитории, поэтому распространение / поощрение вредных привычек здесь наносит вред и ничего хорошего не делает. На unix.SE или в другом технологическом сообществе, у пользователей которого есть знания / средства, направленные на то, чтобы приблизиться к их ногам, не снимая самих ног, ваша точка может иметь место (относительно других практик), но здесь это не делает ваш комментарий более умным. – ignis 7 July 2016 в 13:27

Если вы хотите использовать канал для подачи file, используйте параметр -f, за которым обычно следует имя файла, но вы также можете использовать один дефис - для чтения из stdin, поэтому

[ f1]

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

Инструмент xarg намного более мощный и в большинстве случаев нужен только в том случае, если список аргументов слишком длинный (подробнее см. в этом сообщении).

4
ответ дан 23 May 2018 в 08:27
  • 1
    Когда это --? Я этого никогда не видел. -- обычно представляет собой «конец флагов». индикатор. – John Kugelman 6 July 2016 в 23:34
  • 2
    Да, но я нашел его в нескольких экземплярах (ab), используемых таким образом программистом. Я не могу вспомнить, где именно (добавлю комментарий, если я это сделаю), но я помню проклятия, которые я произнес, когда я это обнаружил, и эти проклятия были определенно NSFW ;-) – deamentiaemundi 7 July 2016 в 00:09

Он использует команду, как показано ниже

ls | xargs file

Это будет работать лучше для меня

2
ответ дан 23 May 2018 в 08:27

Это также должно работать:

file $(ls)

, также обсуждаемое здесь: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff

1
ответ дан 23 May 2018 в 08:27

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

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