Почему не работает команда "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