Я изучал командную строку и узнал, что |
(конвейер) предназначен для перенаправления вывода команды на ввод другой. Так почему же команда ls | file
не работает?
file
input является одним из нескольких имен файлов, например file filename1 filename2
ls
output является списком каталогов и файлов в папке, поэтому я подумал, что 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
Фундаментальная проблема в том, что file
ожидает имена файлов в качестве аргументов командной строки, а не на stdin. При записи ls | файла
вывод ls
передается как входной файл . Не как аргументы, а как входные.
Какая разница?
Аргументы командной строки - это когда вы пишете флаги и имена файлов после команды, как в cmd arg1 arg2 arg3
. В скриптах командной строки эти аргументы доступны в виде переменных $1
, $2
, $3
и др. На языке Си они доступны через аргументы char **argv
и int argc
к main()
.
Стандартный вход, stdin, представляет собой поток данных. Некоторые программы, такие как cat
или wc
, читают из stdin, когда им не заданы аргументы командной строки. В скрипте оболочки можно использовать read
для получения одной строки ввода. В C вы можете использовать scanf()
или getchar()
, среди различных опций.
file
обычно не читается из stdin. Он ожидает, что в качестве аргумента будет передано по крайней мере одно имя файла. Поэтому он распечатывает использование при записи ls | file
, потому что вы не передали аргумент.
Вы могли бы использовать xargs
для преобразования stdin в аргументы, как в ls | xargs file
. Тем не менее, как упоминает terdon, парсинг ls
- плохая идея. Самый прямой способ сделать это - просто:
file *
узнал, что '|' (pipeline) предназначен для того, чтобы перенаправить вывод из команды на ввод другой.
Он не "перенаправляет" вывод, а берет вывод программы и использует его как ввод, в то время как файл берет не вводы, а имена файлов в качестве аргументов, которые затем тестируются. Переадресация не передает эти имена файлов в качестве аргументов, как это делает piping, чем позже вы это сделаете.
Что вы можете сделать, так это прочитать имена файлов из файла с опцией --files-from
, если у вас есть файл, который перечисляет все файлы, которые вы хотите протестировать, в противном случае просто передайте пути к вашим файлам в качестве аргументов.
Принятый ответ объясняет, почему команда трубопровода не работает сразу, а с помощью команды файла *
он предлагает простое, простое решение.
Я хотел бы предложить другую альтернативу, которая может пригодиться в какой-то момент. Хитрость в том, чтобы использовать символ (`)
. Здесь бэктик объяснен очень подробно. Короче говоря, он берет вывод команды, заключенной в бэктик, и заменяет его как строку на оставшуюся команду. Итак, find `ls`
получит вывод команды ls
, и подставить его в качестве аргументов для команды find
. Это длиннее и сложнее принятого решения, но варианты этого могут быть полезны и в других ситуациях.
Вывод ls
через канал представляет собой сплошной блок данных с размером 0x0a, разделяющий каждую строку - т.е. символ linefeed - и file
получает его как один параметр, где ожидает работу нескольких символов по одному за раз.
Как правило, никогда не используйте ls
для генерации источника данных для других команд - в один прекрасный день он обрушится ... на rm
и тогда у вас будут проблемы!
Лучше использовать цикл, например для i в *; do file "$i"; done
, который выдаст нужный вам результат, предсказуемо. Там кавычки в случае имен файлов с пробелами.
Потому что, как вы говорите, на вход файла
должны быть введены имена файлов . Однако вывод 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
Она работает с помощью команды, как показано ниже
ls | xargs file
Она будет работать лучше для меня
Если вы хотите использовать канал для передачи файла , используйте опцию
-f
, за которой обычно следует имя файла, но вы также можете использовать один дефис -
для чтения из stdin, Поэтому
$ ls
cow.pdf some.txt
$ ls | file -f -
cow.pdf: PDF document, version 1.4
some.txt: ASCII text
Трюк с дефисом -
работает со многими стандартными утилитами командной строки (хотя иногда это --
), так что всегда стоит попробовать.
Инструмент xarg
намного мощнее и в большинстве случаев нужен только в том случае, если список аргументов слишком длинный (подробности см. в -).
Это также должно работать:
file $(ls)
, как также обсуждается здесь: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff- и прочее