Skript na vyhledávní souborů

V dnešní perlovské glose implementujeme jednoduchý skript, který vyhledá všechny zadané soubory v aktuálním adresáři a ve všech jeho podadresářích. Naučíme se přitom, jak pracovat v Perlu s linuxovými příkazy, regulárními výrazy a ještě k tomu všemu použijeme rekurzi.

Na začátku si uložíme hledané slovo z parametru programu do globální proměnné.

$filename = $ARGV[0];

Běh celého programu pak vlastně obstará jediná funkce, pojmenujme ji například search. Funkce bude přebírat jeden parametr a to jméno současné složky. Název současné složky musíme nejdříve umět získat. Zde poprvé použijeme systémový příkaz, konkrétně pwd, který zjištění názvu složky provádí.
$dir = `pwd`;

Všimněte si, že příkaz pwd je obalen zpětnými uvozovkami (na české klávesnici pravý alt + h) a že se jeho výstup jednoduše ukládá do proměnné $dir. Vše je v pořádku až na jeden detail a tím je znak konce řádku, který je součástí výstupu příkazu pwd. Musíme jej odstranit. Nabízí se použití regulárního výrazu, ale jako vždy je tu ještě lepší možnost. Perl nám nabízí speciální funkci chop, jež smaže poslední znak v řetězci. Použijeme ji a potom zavoláme naši vyhledávací funkci.
chop $dir;
search( $dir );

Nyní se podívejme, co bude funkce search vykonávat.
Nejprve si nadeklaruje své lokální proměnné, jejich význam napíšu ke každé zvlášť do komentáře.
my @lines;    # pole všech řádků výstupu příkazu ls -l
my $line;     # obsahuje jednotlivé řádky z @lines v cyklu foreach
my $file;     # pokud byl nějaký soubor nalezen, bude uložen zde
my $dir;      # jméno složky učiníme lokálním, protože se bude měnit
my $subdir;   # jméno podsložky
$dir = $_[0]; # uložíme první parametr funkce

Dále je potřeba zkontrolovat, zda máme oprávnění přistupovat k obsahu adresáře. Perl nám situaci opět zjednodušuje testovacím operátorem -x (kontrola práv na spouštění).
if ( -x $dir )

Pokud máme dostatečná práva, zavoláme posloupnost příkazů, která nás přesune do prohlížené složky (příkaz cd), poté vypíše její obsah do tabulky (ls -l) a vybere řádky tabulky obsahující hledaný výraz. Za povšimnutí zde stojí způsob, jakým se proměnné z Perlu vkládají do systémových příkazů.
@lines = `cd $dir; ls -l | grep $filename`;

Je-li v poli @lines něco uloženo, pak jsou to nalezené soubory. Pomocí cyklu foreach si je vypíšeme. Abychom však nevypisovali přímo řádky výstupu příkazu ls, musíme použít regulární výrazy.
foreach $line (@lines) {
  ($file) = $line =~ /\s+(\S+)$/;
  print "Found $file in $dir\n";
}

Regulární výraz \s+(\S+)$ odpovídá vždy poslednímu slovu na řádku. \s určuje hledání bílých znaků (mezera, tabulátor, nový řádek) a \S naopak nebílých znaků.
Protože chceme hledat také ve všech podsložkách, je nutné zjistit, jaké složky se v aktuální složce nacházejí. Znovu tedy naplníme pole @lines výstupem příkazu ls -l a jednotlivé řádky budeme procházet cyklem foreach. Nyní ale budeme hledat řádky, na kterých je první písmeno malé d (directory). Jméno podsložky je opět poslední slovo na řádku, a proto můžeme použít stejný regulární výraz jako pro získání jména souboru.
@lines = `cd $dir; ls -l`;
foreach $line (@lines) {
  if ( $line =~ /^d/ ) {
  $line =~ /\s+(\S+)$/;
  $subdir = $dir."/".$1;
  search($subdir); # funkce rekurzívně zavolá sama sebe pro hledání v podsložkách
}

Funkce v tomto cyklu volá sama sebe a bude v tom pokračovat, dokud bude narážet na další a další podsložky prohledávaných složek.
Skript je kompletní a nezbývá nic jiného než jej vyzkoušet. Pojmenujme si program třeba hledej. Pokud si skript vložíme do složky /usr/bin, můžeme jej v budoucnu odkukoliv spouštět zadáním příkazu do příkazové řádky.
hledej soubor

Kompletní kód skriptu na vyhledávání souborů si můžete prohlédnout zde.

#!/usr/bin/perl
if ($#ARGV != 0) {
  print "usage: hledej soubor\n";
  exit;
}
$filename = $ARGV[0];
$dir = `pwd`;
chop $dir;
search($dir);
 
sub search {
  my(@lines, $line, $file, $dir, $subdir);
  $dir = $_[0];
  if ( -x $dir ) {
    @lines = `cd $dir; ls -l | grep $filename`;
    foreach $line (@lines) {
      ($file) = $line =~ /\s+(\S+)$/;
      print "Found $file in $dir\n";
    }
    @lines = `cd $dir; ls -l`;
    foreach $line (@lines) {
      if ( $line =~ /^d/ ) {
        $line =~ /\s+(\S+)$/;
        $subdir = $dir."/".$1;
        search($subdir);
      }
    }
  }
}