Запустите Perl-скрипт с Python для нескольких файлов одновременно в папке - PullRequest
2 голосов
/ 13 мая 2019

Это мой Perl-скрипт на данный момент:

#!/usr/bin/perl
use open qw/:std :utf8/;
use strict;
use warnings;

if (defined $ARGV[0]){
my $filename = $ARGV[0];
my %count;

open (my $fh, $filename) or die "Can't open '$filename' $!";
while (<$fh>)
{
        $count{ lc $1 }++ while /(\w+)/g;
}
close $fh;

my $array = 0;

foreach my $word ( sort { $count{$b} <=> $count{$a} } keys %count)
{
    print "$count{$word} $word\n" if $array++ < 10;
}

}else{
print "Please enter the name of the file: ";
my $filename = ($_ = <STDIN>);

my %count;

open (my $fh, $filename) or die "Can't open '$filename' $!";
while (<$fh>)
{
        $count{ lc $1 }++ while /(\w+)/g;
}
close $fh;

my $array = 0;

foreach my $word ( sort { $count{$b} <=> $count{$a} } keys %count)
{
    print "$count{$word} $word\n" if $array++ < 10;
}
}

А это мой Python-скрипт на данный момент:

#!/usr/bin/env python3
import os

perlscript = "perl " + " perlscript.pl " + " /home/user/Desktop/data/*.txt " + " >> " + "/home/user/Desktop/results/output.txt"
os.system(perlscript)

Проблема : Когда естьесли в папке данных находятся несколько txt-файлов, скрипт запускается только для одного файла и игнорирует все остальные txt-файлы.Есть ли способ запустить perlscript на всех txt-файлах одновременно?

Другая проблема: Я также пытаюсь удалить txt-файлы с помощью os.remove после их выполнения, но они удаляются до того, как perlscript сможет выполнить.

Есть идеи?:)

1 Ответ

3 голосов
/ 13 мая 2019

Этот Perl-скрипт обрабатывает один файл. Кроме того, эта строка, переданная в оболочку с помощью os.system, не может быть развернута в допустимую команду со списком файлов, как предполагалось с глобусом оболочки *.

Вместо этого создайте список файлов в Python, используя os.listdir или glob.glob или os.walk. Затем выполните итерацию по списку и вызовите этот сценарий Perl для каждого файла, если он должен обрабатывать только один файл за раз, или измените сценарий Perl для обработки нескольких файлов и запускайте его один раз со всем списком.

Чтобы сохранить текущий Perl-скрипт и запускать его для каждого файла

import os

data_path   = "/home/user/Desktop/data/"
output_path = "/home/user/Desktop/result/"

for file in os.listdir(data_path):
    if not file.endswith(".txt"):
        continue

    print("Processing " + file)                      # better use subprocess
    run_perlscript = "perl " + " perlscript.pl " + \
        data_path + file  + " >> " + output_path + "output.txt"
    os.system(run_perlscript)

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

Однако лучше использовать модуль subprocess для запуска и управления внешними командами. Это рекомендуется даже в самой документации os.system . Например

import subprocess

with open(output_path + "output.txt", "a") as fout:
    for file in os.listdir(path):
        if not file.endswith(".txt"):
            continue 
        subprocess.run(["perl", "script.pl", data_path + file], stdout=fout)

, где файл открывается в режиме добавления ("a") после перенаправления вопроса >>.

Рекомендуемый subprocess.run доступен начиная с python 3.5; в противном случае используйте Popen .

Другой, возможно, «правильный» вариант - настроить скрипт Perl так, чтобы он мог обрабатывать несколько файлов. Тогда вам нужно только запустить его один раз, со всем списком файлов.

use strict;
use warnings;
use feature 'say';    
use open ':std', ':encoding(UTF-8)';

foreach my $filename (@ARGV) {
    say "Processing $filename";

    my %count;

    open my $fh, '<', $filename  or do {
       warn "Can't open '$filename': $!";
       next;
    };
    while (<$fh>) {   
        $count{ lc $1 }++ while /(\w+)/g;
    }   
    close $fh;

    my $prn_cnt = 0;
    foreach my $word ( sort { $count{$b} <=> $count{$a} } keys %count) {   
        print "$count{$word} $word\n" if $prn_cnt++ < 10; 
    }   
}

Это выводит предупреждение на файл, который он не может открыть, и переходит к следующему. Если вы хотите, чтобы сценарий завершал работу с любым неожиданным файлом, замените or do { ... }; на исходный die.

Тогда (используя glob.glob в качестве примера сейчас)

import subprocess

data_path   = "/home/user/Desktop/data/"
output_path = "/home/user/Desktop/result/"

files = glob.glob(data_path + "*.txt")

with open(output_path + "output.txt", "a") as fout:
    subprocess.run(["perl", "script.pl", files], stdout=fout)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...