Как я могу сделать рекурсивную команду 'ls' в Perl? - PullRequest
0 голосов
/ 17 октября 2019

Я пробовал это в Perl:

#!/usr/bin/perl 
use 5.010;
state @files;

sub marin{
if (opendir DIR, $_){
   marin $_; 
} else {
  push @files, $_; 
}   
 closedir DIR;
}
$input = opendir DIR, '/home';
marin $input;
print "@files";

Но возникает ошибка: 'Cant opendir (' DIR ', undef): нет такого файла или каталога (строка 7). Что такое if(open DIR, $_) ... почему я не могу использовать $ _ в качестве файла пути? (и, таким образом, сделать это как мою файловую ручку?) Неважно, если я сделаю $_ в начале морской функции как еще один скаляр $_ = $dir_to_open, это не поможет. Пожалуйста, что я делаю не так?

1 Ответ

3 голосов
/ 17 октября 2019

В вашей программе Perl есть особая проблема. Вы не используете аргумент, который передаете своей подпрограмме:

sub marin{
    if (opendir DIR, $_){
       marin $_; 
    } else {
       push @files, $_; 
    }   
  closedir DIR;
  }

Вы хотели использовать первый аргумент подпрограммы ($_[0]), но вы используете переменную темы по умолчанию (* 1005)*) вместо. Поскольку в $_ нет значения, вы получаете сообщение об ошибке.

Вместо этого вы можете сказать:

opendir DIR, $_[0]

Но это немного уродливо. Это действительно одноэлементный доступ к массиву @_, список аргументов подпрограммы. Вместо уродливой формы используйте присвоение списка, чтобы дать эти имена:

sub marin{
    my( $file ) = @_;
    if (opendir DIR, $file){
       marin $file; 
    } else {
       push @files, $file; 
    }   
  closedir DIR;
  }

Следующая большая проблема заключается в том, что вы используете дескриптор файла голого слова (DIR) в рекурсивной подпрограмме. По сути, это глобальная переменная, поэтому каждый раз, когда вы открываете ее, вы закрываете предыдущую. Вместо этого вы лексическая переменная. Если вы используете недавно объявленную лексическую переменную или переменную со значением undef, opendir (или open) поместит ссылку на открытый дескриптор файла в переменной. Теперь каждый дескриптор каталога является частным для подпрограммы call (не определение подпрограммы!). Для каждого уровня есть новый дескриптор каталога в дополнение ко всем другим, которые вы уже открыли:

sub marin{
    my( $file ) = @_;
    if (opendir my $dh, $file){
       marin $file; 
    } else {
       push @files, $file; 
    }   
  }

Теперь это должно приблизить вас к тому, что вы хотите. Это только первые проблемы, хотя. Каждый раз, когда вы звоните marin, вам нужно решить, что он собирается вернуть, и как вы будете сообщать об этом на уровень выше. Пока вы работаете

У нас есть пример этой задачи в главе «Справочные трюки» в Intermediate Perl . Это похоже на этот пример:

use v5.10;

use Data::Dumper;

my $listing = marin( '/etc' );

sub marin {
    my( $dir ) = @_;

    if( opendir my $dh, $dir ) {
        my %hash;
        while( my $file = readdir($dh) ) {
            next if $file eq '.' || $file eq '..';
            next if -l $file; # symlinks can make a loop!
            $hash{$file} = marin( "$dir/$file" )
            }
        return \%hash
        }

    return;
    }

say Dumper( $listing );

Это выдает что-то вроде этого, где значение хеша undef указывает простой файл, а ссылочное значение хеша - это подкаталог:

$VAR1 = {
          'php.ini.default-previous~orig' => undef,
          'networks' => undef,
          'csh.login~orig' => undef,
          'rtadvd.conf~previous' => undef,
          'group~previous' => undef,
          'mach_init_per_user.d' => {},
          'manpaths.d' => {
                            'Wireshark' => undef,
                            'MacGPG2' => undef
                          },
          'rmtab' => undef,
          'passwd' => undef,
          'master.passwd' => undef,
          ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...