Perl: ¿Es quotemeta solo para expresiones regulares? ¿Es seguro para los nombres de archivo?

Mientras contestaesta pregunta sobre el escape seguro de nombre de archivo con espacios (y potencialmente otros caracteres),una de las respuestas se dice que usa el Perl incorporadocita función.

La documentación de quotemeta establece:

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.  

En la documentación de quotemeta, la única mención de su uso es escapar de todos los caracteres que no sean/[A-Za-z_0-9]/ con un\ para usar en una expresión regular. No establece el uso de nombres de archivo. Sin embargo, esto parece un efecto secundario muy agradable, aunque no documentado.

En un comentario a Sinan Ünürresponder a la pregunta anterior, hobbs afirma:

shell escapandoes diferente de escapar de regexp, y aunque no puedo encontrar una situación en la que quotemeta daría un resultado realmente inseguro, no es para la tarea. Si debe escapar, en lugar de omitir el shell, sugiero probar String :: ShellQuote, que adopta un enfoque más conservador utilizando comillas simples sh para desempañar todo excepto las comillas simples y barras diagonales inversas para comillas simples. - hobbs 13 de agosto de 2009 a las 14:25

¿Es seguro, completamente, usar quotemeta en lugar de citas de archivos más conservadoras comoCadena :: Shellquote? ¿Es seguro quotemeta utf8 o caracteres multibyte?

Realicé una prueba que no está clara. citameta funciona bien, al parecer, excepto por un nombre de archivo o nombre de directorio con un\no\r en eso. Aunque es raro, estos personajes son legales en Unix y los he visto. Recuerde que ciertos caracteres, como LF, CR y NUL no se pueden escapar con\. Leí mi disco duro con 700k archivos con quotemeta y no tuve fallas.

Sospecho (aunque todavía no lo he demostrado) que quotemeta podría fallar con caracteres multibyte donde uno o más de los bytes caen en el rango ASCII. Por ejemplo,à puede codificarse como un carácter (UTF8 C3 A0) o como dos caracteres (U + 0061 daa u + 0300 es un acento combinado de tumbas). La única falla demostrada que tengo con quotemeta es con archivos con un\n o\r en el camino que creé. Estaría interesado en otros personajes para ponernasty_names Probar.

ShellQuote funciona perfectamente en todos los nombres de archivos, excepto los terminados por un NUL al crear un archivo. Nunca he tenido un fracaso con eso.

Entonces, ¿qué usar? Para ser claros: las citas de shell no son algo que hago con frecuencia, ya que generalmente solo uso Perl open para abrir una tubería a un proceso. Ese método no sufre los problemas de shell discutidos. Estoy interesado ya que he visto que quotemeta se usa con frecuencia para el escape de nombres de archivos.

(Gracias a Ether he agregado IPC :: Sistema :: Simple)

Archivo de prueba:

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";

Respuestas a la pregunta(3)

Su respuesta a la pregunta