Como usar o comando find no script Perl?

0

Alguém poderia me dizer por que o comando find sempre vai para o diretório raiz, mas não para o diretório especificado em $srceDir ?

my $srceDir = "/mnt/SDrive/SV/Capture Data/";

my $find_cmd = 'find $srceDir -type f -newermt 2013-02-14 ! -newermt 2013-02-15';

open(FIND_FILE, "$find_cmd |");
while(<FIND_FILE>){ 
    next if /^total/; # because we're only interested in real output
    print $_; 
}
    
por oanh 15.02.2013 / 18:59

2 respostas

5

Porque você usa aspas simples em vez de aspas duplas.

O Perl não interpola as variáveis entre aspas simples , então o que você está fazendo é enviar o string '$ srceDir' para o shell que normalmente será desfeito (em branco) a menos que você tenha definido em seu ambiente em algum lugar.

Tente isto:

my $find_cmd = "find $srceDir -type f -newermt 2013-02-14 ! -newermt 2013-02-15";

ou melhor isso:

my $find_cmd = sprintf
    'find "%s" -type f -newermt 2013-02-14 ! -newermt 2013-02-15',
    $srceDir;

... se preocupam com espaços, enquanto find_cmd seria executado sob o forking sh .

* Observação importante *

Como @vonbrand comentou corretamente: oferece muitas bibliotecas para garantir a comunicação entre o seu programa e muitas outras coisas.

Para a operação do sistema de arquivos find , o perl usa o File library module File::Find , para o qual existe um pequeno utilitário find2perl , que converterá sua linha de comando find em um pequeno script perl:

$ find2perl -type f -mtime -3 ! -mtime -2;
#! /usr/bin/perl -w
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
        if 0; #$running_under_some_shell
use strict;
use File::Find ();

# Set the variable $File::Find::dont_use_nlink if you're using AFS,
# since AFS cheats.

# for the convenience of &wanted calls, including -eval statements:
use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

sub wanted;

# Traverse desired filesystems
File::Find::find({wanted => \&wanted}, '.');
exit;

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);
    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    -f _ &&
    (int(-M _) < 3) &&
    ! (int(-M _) < 2)
    && print("$name\n");
}

Então, sua necessidade pode se tornar algo assim:

#! /usr/bin/perl -w

my $srceDir   = "/mnt/SDrive/SV/Capture Data/";
my $startDate = "2013-02-14";
my $endDate   = "2013-02-15";

use strict;
use File::Find ();
use POSIX qw|mktime|;

use vars qw/*name *dir *prune/;
*name   = *File::Find::name;
*dir    = *File::Find::dir;
*prune  = *File::Find::prune;

my ($sDay,$eDay)=map {
    my ($year,$month,$day)=split("-",$_);
    (time()-mktime(0,0,0,$day,$month-1,$year-1900))/86400
} ($startDate,$endDate);

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    -f _ &&
    (-M _ < $sDay) &&
    ! (-M _ < $eDay)
    && print("$name\n");
}
File::Find::find({wanted => \&wanted}, $srceDir );

A melhor vantagem de fazer isso, em vez de open $fh,"find ...|" , é que isso é muito robusto; você não precisa se preocupar com caracteres presentes em nomes de arquivos (como espaços, citações, e comercial ...).

    
por 15.02.2013 / 19:07
3

Se você der uma string com caracteres meta do shell (como espaço) para o seu comando, ela será interpretada como uma linha de comando do shell, o que significa duas coisas:

  1. Um comando extra precisa ser executado (o shell para analisar essa linha de comando), que não é muito eficiente. Com algumas implementações de shell, isso significa mesmo um processo extra.
  2. Você precisa escapar de caracteres especiais para o shell.

Melhor é executar o comando diretamente, isto é, dando a lista de argumentos ao comando para executar, em vez de pedir que um shell divida uma linha de comando para construir essa lista de argumentos.

Além disso, a menos que você use -print0 , a saída de find não pode ser pós-processada com segurança porque os registros são separados por caracteres de nova linha, enquanto a nova linha é um caractere perfeitamente válido em um nome de arquivo. em minha resposta à sua pergunta semelhante , você precisaria escrever como:

my $srceDir = "/mnt/SDrive/SV/Capture Data"; my @find_cmd = ("find", $srceDir, "-type", "f", "-newermt", "14 Feb 2013", "-print0"); open FIND, "-|", @find_cmd; $/ = "my $srceDir = "/mnt/SDrive/SV/Capture Data"; my @find_cmd = ("find", $srceDir, "-type", "f", "-newermt", "14 Feb 2013", "-print0"); open FIND, "-|", @find_cmd; $/ = "%pre%"; # set perl's record separator while (<FIND>) { ... } close FIND or warn $! ? "Error closing find pipe: $!" : "find exited with non-zero exit status: $?"; "; # set perl's record separator while (<FIND>) { ... } close FIND or warn $! ? "Error closing find pipe: $!" : "find exited with non-zero exit status: $?";

(e BTW, find não gera total linhas, você pode estar confuso com ls ).

    
por 15.02.2013 / 21:52

Tags