Вы спросили: «Первый передает файл NAMES в Perl, а второй передает файл СОДЕРЖАНИЕ, как кажется. Всегда ли это так в Unix или в специальном свойстве Perl?»Это поведение не является специфичным для Perl.Часть этого делается Unix.Это более широко соблюдаемая конвенция.Поведение конвейера (команды, сопровождаемые |
) выполняется ОС.То, что программа делает со своим вводом командной строки или выводом, которое она производит, зависит от команды.
Примеры.Пожалуйста, выполните на вашем компьютере в Bash.
$ mkdir pipetestdir; cd pipetestdir
$ for f in {a..z}; do printf "%s\n" "File: $f, line: "{1..1000} > $f.txt; done
Это создаст пустой каталог, перейдите в него и создайте 26 файлов по 1000 строк в каждом пустом каталоге.
С утилитой Ubuntu / Linux cat *.txt
ваша банкапосмотреть содержимое файлов.*.txt
- это , расширенный с помощью Bash до всех 26 .txt
файлов.с помощью wc -l *.txt
вы можете проверить количество строк всех 26 файлов.Вы можете использовать форму wc -l {a..e}.txt
, где Bash использует расширения brace .Вы можете преобразовать эти формы в канал и использовать cat *.txt | wc -l
, чтобы просто получить счетчик строк из всех 26 файлов.В первом примере wc -l *.txt
открывает 26 файлов, считает строки и отображает результат.Во втором примере cat *.txt | wc -l
программа cat
открывает 26 файлов и создает объединенный текстовый поток для STDOUT;|
превращает это в канал, который направлен на следующую программу;в этом случае wc -l
, который получает этот вывод на свой STDIN и считает строки этого без какого-либо отношения к отдельным файлам.
С помощью вкладышей Perl one вы можете легко искать эти файлы.Пример:
$ perl -lne 'print if /^.*666/' *.txt # the devil's line from 26 files...
Вы можете использовать egrep
или awk
, чтобы сделать то же самое:
$ egrep '^.*666$' *.txt
$ awk "/^.*666$/ {print}" *.txt
Если вы превратите эту форму в трубу, вы работаете с OUTPUTпредыдущей команды слева от Perl (или awk или egrep).Вывод STDOUT предыдущей части передается в STDIN Perl.Если эта команда создает имена файлов, вы работаете с именами файлов:
$ ls *.txt | perl -lne 'print if /c|d|z/'
$ find . -name '*.txt' | perl -lne 'print if /c|d|z/'
Если вы не расширили их сначала с помощью cat
:
$ cat *.txt | perl -lne 'print if /^.*?(c|d|z).*?666$/'
Что выводится аналогично этому:
$ perl -lne 'print if /^.*?(c|d|z).*?666$/' *.txt
Возможно, именно здесь вы запутались в том, что формы взаимозаменяемы?Они не!Происходят две совершенно разные вещи.Если вы используете cat *.txt | perl '...'
, все файлы объединяются в один длинный текстовый поток и отправляются на следующий этап в конвейере;в этом случае perl '...'
.Perl не сможет различить, какой текст и из какого файла.Только потому, что мы ставим отметку в каждом файле, когда создаем их, мы можем видеть, какой файл какой.
В другой форме, perl '...' *.txt
, perl открывает файлы и имеет полный контроль над каждым текстовым потоком и файлом.Вы можете контролировать, открываете ли вы файл или нет, печатаете имя файла или нет, и т.д ...
Избегайте, однако, конкретной формы cat a.txt | perl '...'
(то есть используйте cat для одного файла), чтобыИзбегайте страшных Бесполезное использование премии Кэт : -}
Вы спрашивали конкретно о форме:
$ perl -nle '... # same yada yada' `find . -type f`
Как указал Брайан Д. Фой , есть ограничения на длину командной строки, и вы должны быть осторожны с этой формой.Вы также можете неожиданно изменить имена файлов с помощью обратных тиков.Вместо формы обратной галочки используйте find
с xargs
:
$ find . -type f -print0 | xargs -0 perl -nle 'print if /^.*666$/'
. Чтобы увидеть проблему с разрывом имен файлов, введите следующие команды:
$ mv z.txt "file name with spaces"
$ perl -ple '' `find . -name "file*"` #fails...
$ find . -name "file*" -print0 | xargs -0 perl -ple '' #works...
$ find . -type f -exec perl -wnl -e '/\s1$/ and print' {} + #alternative