Perl: Ist Quotemeta nur für reguläre Ausdrücke? Ist es sicher für Dateinamen?

Während der Beantwortung vonDie Frage zum sicheren Entkommen von Dateinamen mit Leerzeichen (und möglicherweise anderen Zeichen), eine der Antworten sagte zu Perl's eingebautem quotemeta Funktion.

In der Dokumentation von quotemeta heißt es:

quotemeta (and \Q ... \E ) are useful when interpolating strings 
into regular expressions, because by default an interpolated variable 
will be considered a mini-regular expression.  

In der Dokumentation für quotemeta wird nur erwähnt, dass es verwendet wird, um alle Zeichen außer @ zu maskiere/[A-Za-z_0-9]/ mit einer\ zur Verwendung in einem regulären Ausdruck. Die Verwendung von Dateinamen wird nicht angegeben. Dies scheint jedoch ein sehr angenehmer, wenn auch nicht dokumentierter Nebeneffekt zu sein.

In einem Kommentar zu Sinan ÜnürAntworte zur vorigen frage sagt hobbs:

Shell entkommen ist anders als regexp-Flucht, und obwohl ich nicht auf eine Situation kommen kann, in der Quotemeta ein wirklich unsicheres Ergebnis liefern würde, ist es nicht für die Aufgabe gedacht. Wenn Sie es vermeiden müssen, anstatt die Shell zu umgehen, empfehle ich String :: ShellQuote zu verwenden, das einen konservativeren Ansatz verfolgt und einfache Anführungszeichen verwendet, um alles außer einfachen Anführungszeichen selbst und Backslashes für einfache Anführungszeichen zu defangieren. - Hobbs 13. August 09 um 14: 25

Ist es sicher - vollständig -, quotemeta anstelle von konservativeren Dateianführungszeichen wie @ zu verwendeString :: Shellquote? Ist quotemeta utf8 oder Multibyte-Zeichen sicher?

Ich habe einen Test zusammengestellt, der unklar ist. quotemeta funktioniert anscheinend gut, mit Ausnahme eines Dateinamens oder eines Verzeichnisnamens mit einem\n, oder\r drin. Obwohl selten, sind diese Zeichen in Unix legal und ich habe sie gesehen. Denken Sie daran, dass bestimmte Zeichen wie LF, CR und NUL nicht mit @ maskiert werden könne\. Ich habe meine Festplatte mit 700k Dateien mit Quotemeta gelesen und hatte keine Fehler.

Ich habe den Verdacht (obwohl ich es noch nicht demonstriert habe), dass Quotemeta mit Multibyte-Zeichen fehlschlagen könnte, wenn eines oder mehrere der Bytes in den ASCII-Bereich fallen. Beispielsweiseà kann als ein Zeichen (UTF8 C3 A0) oder als zwei Zeichen (U + 0061 ergibta u + 0300 ist ein kombinierender Grabakzent. Der einzige nachgewiesene Fehler, den ich mit quotemeta habe, sind Dateien mit einem\n oder\r in dem Pfad, den ich erstellt habe. Ich wäre an anderen Charakteren interessiert, die ich in @ einfügen könntnasty_names zu testen

ShellQuote funktioniert einwandfrei für alle Dateinamen, mit Ausnahme derjenigen, die beim Erstellen einer Datei durch eine NUL abgeschlossen werden. Ich habe noch nie einen Fehler damit gehabt.

So was zu benutzen? Nur um es klar zu machen: Shell-Zitate mache ich nicht oft, da ich normalerweise nur Perl open verwende, um eine Pipe für einen Prozess zu öffnen. Bei dieser Methode treten die besprochenen Shell-Probleme nicht auf. Ich bin interessiert, da ich gesehen habe, dass Quotemeta häufig für das Entziehen von Dateinamen verwendet wird.

(Dank Ether habe ich IPC :: System :: Simple hinzugefügt)

Testdatei:

use strict; use warnings; use autodie;
use String::ShellQuote;
use File::Find;
use File::Path;
use IPC::System::Simple 'capturex';

my @nasty_names;
my $top_dir = '/Users/andrew/bin/pipetestdir/testdir';
my $sub_dir = "easy_to_remove_me";
my (@qfail, @sfail, @ipcfail);

sub wanted { 
    if ($File::Find::name) { 
         my $rtr;
         my $exec1="ls ".quotemeta($File::Find::name);
         my $exec2="ls ".shell_quote($File::Find::name);
         my @exec3= ("ls", $File::Find::name);

         $rtr=`$exec1`;  
         push @qfail, "$exec1" 
              if $rtr=~/^\s*$/ ;

         $rtr=`$exec2`;
         push @sfail, "$exec2" 
              if $rtr=~/^\s*$/ ;

         $rtr = capturex(@exec3);
         push @ipcfail, \@exec3
              if $rtr=~/^\s*$/ ;     
    }
}

chdir($top_dir) or die "$!";
mkdir "$top_dir/$sub_dir";
chdir "$top_dir/$sub_dir";

push @nasty_names, "name with new line \n in the middle";
push @nasty_names, "name with CR \r in the middle";
push @nasty_names, "name with tab\tright there";
push @nasty_names, "utf \x{0061}\x{0300} combining diacritic";
push @nasty_names, "utf e̋ alt combining diacritic";
push @nasty_names, "utf e\x{cc8b} alt combining diacritic";
push @nasty_names, "utf άέᾄ greek";
push @nasty_names, 'back\slashes\\Not\\\at\\\\end';
push @nasty_names, qw|back\slashes\\IS\\\at\\\\end\\\\|;

sub create_nasty_files {
    for my $name (@nasty_names) {
       open my $fh, '>', $name ; 
       close $fh;
    }
}

for my $dir (@nasty_names) {
    chdir("$top_dir/$sub_dir");
    mkpath($dir);
    chdir $dir;
    create_nasty_files();
}

find(\&wanted, $top_dir);

print "\nquotemeta failed on:\n", join "\n", @qfail;
print "\nShell Quote failed on:\n", join "\n", @sfail;
print "\ncapturex failed on:\n", join "\n", @ipcfail;
print "\n\n\n",
      "Remove \"$top_dir/$sub_dir\" before running again...\n\n";

Antworten auf die Frage(6)

Ihre Antwort auf die Frage