Почему не работает команда "ls | file"?

Я изучал командную строку и узнал, что | (конвейер) предназначен для перенаправления вывода команды на ввод другой. Так почему команда ls | file не работает?

file вход является одним из нескольких имен файлов, таких как file filename1 filename2

ls вывод представляет собой список каталогов и файлов в папке, поэтому я подумал ls | file должен был показать тип файла каждого файла в папке.

Когда я использую его, однако, вывод:

    Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
        [-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
    file -C [-m magicfiles]
    file [--help]

Так как произошла ошибка с использованием file команда

8 ответов

Решение

Основная проблема заключается в том, что file ожидает имена файлов в качестве аргументов командной строки, а не в stdin. Когда ты пишешь ls | file выход ls передается в качестве входных данных для file, Не в качестве аргументов, а в качестве входных данных.

Какая разница?

  • Аргументы командной строки, когда вы пишете флаги и имена файлов после команды, как в cmd arg1 arg2 arg3, В сценариях оболочки эти аргументы доступны как переменные $1, $2, $3и т. д. В C вы можете получить к ним доступ через char **argv а также int argc аргументы main(),

  • Стандартный ввод, stdin, представляет собой поток данных. Некоторые программы, такие как cat или же wc читать из стандартного ввода, когда им не дают никаких аргументов командной строки. В сценарии оболочки вы можете использовать read чтобы получить одну строку ввода. В C вы можете использовать scanf() или же getchar()среди различных вариантов.

file обычно не читает со стандартного ввода. Ожидается, что в качестве аргумента будет передано хотя бы одно имя файла. Вот почему он печатает использование, когда вы пишете ls | fileпотому что вы не передали аргумент.

Вы могли бы использовать xargs для преобразования стандартного ввода в аргументы, как в ls | xargs file, Тем не менее, как упоминает Тердон, разбор ls плохая идея Самый прямой способ сделать это просто:

file *

Потому что, как вы говорите, вклад 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

узнал, что '|' (конвейер) предназначен для перенаправления вывода команды на ввод другой.

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

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

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

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

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

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

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

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

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

$ ls
cow.pdf  some.txt
$ ls | file -f -
cow.pdf:       PDF document, version 1.4
some.txt:        ASCII text

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

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

Это работает использовать команду, как показано ниже

ls | xargs file

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

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

file $(ls)

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

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