2012-11-20 4 views
4

У меня есть 100 000 файлов, которые я хотел бы проанализировать. В частности, я хотел бы рассчитать процент печатаемых символов из образца файла произвольного размера. Некоторые из этих файлов относятся к мэйнфреймам, Windows, Unix и т. Д., Поэтому вполне вероятно, что включены двоичные и управляющие символы.PERL для подсчета непечатаемых символов

Я начал с использования команды «файл» Linux, но для моих целей он не предоставил достаточно деталей. Следующий код передает то, что я пытаюсь сделать, но не всегда работает.

#!/usr/bin/perl -n 

    use strict; 
    use warnings; 

    my $cnt_n_print = 0; 
    my $cnt_print = 0; 
    my $cnt_total = 0; 
    my $prc_print = 0; 

    #Count the number of non-printable characters 
    while ($_ =~ m/[^[:print:]]/g) {$cnt_n_print++}; 

    #Count the number of printable characters 
    while ($_ =~ m/[[:print:]]/g) {$cnt_print++}; 

    $cnt_total = $cnt_n_print + $cnt_print; 
    $prc_print = $cnt_print/$cnt_total; 

    #Print the # total number of bytes read followed by the % printable 
    print "$cnt_total|$prc_print\n" 

Это тестовый вызов, который работает:

echo "test_string of characters" | /home/user/scripts/prl/s16_count_chars.pl 

Это, как я собираюсь назвать его, и работает для одного файла:

find /fct/inbound/trans/ -name "TRNST.20121115231358.xf2" -type f -print0 | xargs -0 head -c 2000 | /home/user/scripts/prl/s16_count_chars.pl 

Это не работает правильно:

find /fct/inbound/trans/ -type f -print0 | xargs -0 head -c 2000 | /home/user/scripts/prl/s16_count_chars.pl 

Ничего из этого:

find /fct/inbound/trans/ -type f -print0 | xargs -0 head -c 2000 | perl -0 /home/user/scripts/prl/s16_count_chars.pl 

Вместо выполнения сценария один раз для строки EACH, возвращаемой поиском, он выполняет ВСЕ ДЛЯ ВСЕХ результатов.

Заранее спасибо.


исследований до сих пор:

Трубы и XARGS и сепараторы

http://help.lockergnome.com/linux/help-understand-pipe-xargs--ftopict549399.html

http://en.wikipedia.org/wiki/Xargs#The_separator_problem


Осветление (ы):
1.) Желаемый результат: если в каталоге имеется 932 файла, выход будет состоять из 932 строк списка имен файлов, общее количество байтов, считанных из файла, и%, которые были печатаемыми символами.
2.) Многие из файлов являются двоичными. Сценарий должен обрабатывать встроенные двоичные файлы eol или eof последовательностей.
3.) Многие из файлов большие, поэтому я хотел бы только прочитать первые/последние байты xx. Я пытался использовать head -c 256 или tail -c 128 для чтения первых 256 байтов или последних 128 байтов соответственно. Решение может либо работать в линии трубопровода, либо ограничивать байты в perl-скрипте.

+0

'в то время как ($ _ = ~ м/[^ [: печать:]]/г) {$ cnt_n_print ++};' лучше сделано как '$ cnt_n_print + = (() = m/[^ [: print:]]/g); '(или еще лучше, используя tr ///, только тот, который не поддерживает классы POSIX) – ysth

+0

« Лучше »= быстрее, более сжато, но использует больше памяти. На самом деле, возможно, намного больше. (Целый строковый скаляр для соответствующего символа!) – ikegami

+0

Noooo! Не '-n' на линии shebang! – Borodin

ответ

0

Вот мой рабочего раствора на основе обратной связи предоставлена.

Я был бы признателен за любую дополнительную обратную связь по форме и более эффективных методов:

#!/usr/bin/perl 

    use strict; 
    use warnings; 

    # This program receives a file path and name. 
    # The program attempts to read the first 2000 bytes. 
    # The output is a list of files, the number of bytes 
    # actually read and the percent of tbe bytes that are 
    # ASCII "printable" aka [\x20-\x7E]. 

    my ($data, $n_bytes, $file_name, $cnt_n_print, $cnt_print, $prc_print); 

    # loop through each file 
    foreach(@ARGV) { 
     $file_name = shift or die "Pass the file name on the command line.\n"; 

     # open the file read only with "<" in "<$file_name" 
     open(FILE, "<$file_name") or die "Can't open $file_name: $!"; 

     # open each file in binary mode to handle non-printable characters 
     binmode FILE; 

     # try to read 2000 bytes from FILE, save the results in $data and the 
     # actual number of bytes read in $n_bytes 
     $n_bytes = read FILE, $data, 2000; 

     $cnt_n_print = 0; 
     $cnt_print = 0; 

     # count the number of non-printable characters 
     ++$cnt_n_print while ($data =~ m/[^[:print:]]/g); 

     $cnt_print = $n_bytes - $cnt_n_print; 
     $prc_print = $cnt_print/$n_bytes; 

     print "$file_name|$n_bytes|$prc_print\n"; 
     close(FILE); 
    } 

Вот пример того, как вызвать выше сценарий:

find /some/path/to/files/ -type f -exec perl this_script.pl {} + 

Вот список ссылки Я нашел полезным:

POSIX Bracket Expressions
Opening files in binmode
Read function
Open file read only

+0

@mob @ikegami В дальнейшем тестировании я нахожу, что это решение пропускает некоторые файлы в каталоге, если вызывается с использованием указанной выше команды 'find'. Например, в одном каталоге имеется 39 файлов, но скрипт выводит информацию только на 20. Если я запускаю сценарий по каждому из файлов по отдельности, он также работает без ошибок для 19 пропущенных с помощью 'find'. Есть ли у вас какие-либо идеи о том, как запустить скрипт для всех файлов в каталоге? – Stan

+0

@mob @ikegami @ysth Если я вызываю скрипт Perl из пакетного скрипта, он запускается для всех файлов: 'find/some/path/to/files/-type f -print | while read filename do perl/path/to/this_script.pl $ filename done' Что такое «ПРАВИЛЬНЫЙ» способ сделать это? – Stan

4

Опция -n обертывает весь код в блоке while(defined($_=<ARGV>) { ... }. Это означает, что ваш my $cnt_print и другие объявления переменных повторяются для каждой строки ввода, по существу, сбрасывая все ваши значения переменных.

Решение является использование глобальных переменных (объявлять их our, если вы хотите продолжать использовать use strict), а не инициализировать их 0, так как они будут инициализируются для каждой строки ввода.Можно сказать, что-то вроде

our $cnt_print //= 0; 

, если вы не хотите $cnt_print и его друзей, чтобы быть неопределенным для первой строки ввода.

См. this recent question с аналогичной проблемой.

+0

Спасибо за быстрый ответ ... Что касается опции «-n», подразумеваемый цикл while - это то, что я хочу. Если я передаю скрипт 172 файлам, я хочу 172 отдельных выхода (по одному для каждого файла). Есть ли практика использования «-n» или явного «while»? – Stan

1

У вас может быть find передать вам один аргумент за раз.

find /fct/inbound/trans/ -type f -exec perl script.pl {} \; 

Но я бы продолжать передачу нескольких файлов одновременно, либо через xargs, или с помощью GNU find «ы -exec +.

find /fct/inbound/trans/ -type f -exec perl script.pl {} + 

Следующие фрагменты кода поддерживают оба.

Вы можете продолжить чтение построчно:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $cnt_total = 0; 
my $cnt_n_print = 0; 

while (<>) { 
    $cnt_total += length; 
    ++$cnt_n_print while /[^[:print:]]/g; 
} continue { 
    if (eof) { 
     my $cnt_print = $cnt_total - $cnt_n_print; 
     my $prc_print = $cnt_print/$cnt_total; 

     print "$ARGV: $cnt_total|$prc_print\n"; 

     $cnt_total = 0; 
     $cnt_n_print = 0; 
    } 
} 

Или вы могли бы прочитать весь файл в то время:

#!/usr/bin/perl 

use strict; 
use warnings; 

local $/; 
while (<>) { 
    my $cnt_n_print = 0; 
    ++$cnt_n_print while /[^[:print:]]/g; 

    my $cnt_total = length; 
    my $cnt_print = $cnt_total - $cnt_n_print; 
    my $prc_print = $cnt_print/$cnt_total; 

    print "$ARGV: $cnt_total|$prc_print\n"; 
} 
+0

Я не понимаю, почему это было приостановлено. – memowe

+0

Спасибо !!! ДЕЙСТВИТЕЛЬНО закрываю, но я думаю, что он задыхается от двоичных файлов, и мне нужно прочитать только первые X байтов (см. Пояснение выше). Также я мог заставить GNU-exec работать. Можете ли вы помочь обновить сценарий, чтобы он мог работать в конвейере linux с командой head/tail, например: a) find/fct/inbound/trans/-name "TRNST.20121115231358.xf2" -type f -print0 | xargs -0 head -c 2000 | /home/user/scripts/prl/s16_count_chars.pl или что-то вроде: b) find/path/to/analysis/-type f -exec perl script.pl {} first 264 + c) find/path/to/analysis/-type f -exec perl script.pl {} last 128 + – Stan

+1

Ну, 'readline' (' <> ') не очень подходит для двоичных файлов. Вместо этого вы хотите «читать». Итерации по файлам с помощью 'for (@ARGV)' и откройте их самостоятельно. – ikegami

Смежные вопросы