¿Cuándo es el momento adecuado (y el momento equivocado) para utilizar comillas invertidas?

StackOverflow https://stackoverflow.com/questions/115809

  •  02-07-2019
  •  | 
  •  

Pregunta

Muchos programadores principiantes escriben código como este:

sub copy_file ($$) {
  my $from = shift;
  my $to = shift;

  `cp $from $to`;
}

¿Es esto malo y por qué?¿Deberían usarse alguna vez comillas invertidas?¿Si es así, cómo?

¿Fue útil?

Solución

Algunas personas ya han mencionado que solo debes usar comillas invertidas cuando:

  • Necesita capturar (o suprimir) la salida.
  • No existe ninguna función integrada o módulo Perl para realizar la misma tarea, o tiene una buena razón para no utilizar el módulo o el módulo integrado.
  • Desinfectas tu entrada.
  • Verifica el valor de retorno.

Desafortunadamente, cosas como comprobar el valor de retorno adecuadamente puede ser bastante desafiante.¿Murió ante una señal?¿Se ejecutó hasta su finalización, pero devolvió un estado de salida divertido?Las formas estándar de intentar interpretar $? son simplemente horribles.

Recomiendo usar el IPC::Sistema::Simple del módulo capture() y system() funciones en lugar de comillas invertidas.El capture() La función funciona igual que las comillas invertidas, excepto que:

  • Proporciona diagnósticos detallados si el comando no se inicia, una señal lo cancela o devuelve un valor de salida inesperado.
  • Proporciona diagnósticos detallados si se pasan datos contaminados.
  • Proporciona un mecanismo sencillo para especificar valores de salida aceptables.
  • Le permite llamar a comillas invertidas sin el shell, si así lo desea.
  • Proporciona mecanismos confiables para evitar el shell, incluso si usa un solo argumento.

Los comandos también funcionan de manera consistente en todos los sistemas operativos y versiones de Perl, a diferencia del comando integrado de Perl. system() que puede no comprobar si hay datos contaminados cuando se llama con múltiples argumentos en versiones anteriores de Perl (por ejemplo, 5.6.0 con múltiples argumentos), o que puede llamar al shell de todos modos en Windows.

Como ejemplo, el siguiente fragmento de código guardará los resultados de una llamada a perldoc en un escalar, evita el shell y genera una excepción si no se puede encontrar la página (ya que perldoc devuelve 1).

#!/usr/bin/perl -w
use strict;
use IPC::System::Simple qw(capture);

# Make sure we're called with command-line arguments.
@ARGV or die "Usage: $0 arguments\n";

my $documentation = capture('perldoc', @ARGV);

IPC::Sistema::Simple es Perl puro, funciona en 5.6.0 y superiores y no tiene dependencias que normalmente no vienen con su distribución de Perl.(En Windows depende de un Win32::módulo que viene con ActiveState y Strawberry Perl).

Descargo de responsabilidad:soy el autor de IPC::Sistema::Simple, por lo que puedo mostrar cierto sesgo.

Otros consejos

La regla es simple:nunca use comillas invertidas si puede encontrar una función integrada para hacer el mismo trabajo, o si hay un módulo sólido en el CPAN que lo hará por usted.Las comillas invertidas a menudo dependen de código no portátil e incluso si elimina las variables, aún puede exponerse a muchos agujeros de seguridad.

Nunca ¡use comillas invertidas con los datos del usuario a menos que haya especificado muy estrictamente lo que está permitido (no lo que no está permitido, se perderá cosas)!Esto es muy, muy peligroso.

Las comillas invertidas deben usarse si y solo si necesita capturar la salida de un comando.De lo contrario, se debe utilizar system().Y, por supuesto, si hay una función Perl o un módulo CPAN que hace el trabajo, se debe usar este en lugar de cualquiera de los dos.

En cualquier caso, se recomiendan encarecidamente dos cosas:

Primero, desinfectar todos los insumos: Utilice el modo contaminación (-T) si el código está expuesto a posibles entradas que no son de confianza.Incluso si no lo es, asegúrese de manejar (o evitar) caracteres originales como el espacio o los tres tipos de comillas.

Segundo, comprobar el código de retorno para asegurarse de que el comando tuvo éxito.A continuación se muestra un ejemplo de cómo hacerlo:

my $cmd = "./do_something.sh foo bar";
my $output = `$cmd`;

if ($?) {
   die "Error running [$cmd]";
}

Otra forma de capturar stdout (además del pid y el código de salida) es usar IPC::Abierto3 posiblemente negando el uso tanto del sistema como de las comillas invertidas.

Utilice comillas invertidas cuando desee recopilar el resultado del comando.

De lo contrario system() es una mejor opción, especialmente si no necesita invocar un shell para manejar metacaracteres o analizar comandos.Puedes evitarlo pasando una lista al sistema(), por ejemplo system('cp', 'foo', 'bar') (sin embargo, probablemente sería mejor usar un módulo para eso particular ejemplo :))

En Perl, siempre hay más de una forma de hacer lo que quieras.El objetivo principal de las comillas invertidas es obtener la salida estándar del comando de shell en una variable de Perl.(En su ejemplo, todo lo que imprima el comando cp se devolverá a la persona que llama). La desventaja de usar comillas invertidas en su ejemplo es que no verifica el valor de retorno del comando Shell;cp podría fallar y usted no lo notaría.Puedes usar esto con la variable especial de Perl $?.Cuando quiero ejecutar un comando de shell, tiendo a usar sistema:

system("cp $from $to") == 0
    or die "Unable to copy $from to $to!";

(Observe también que esto fallará en nombres de archivos con espacios incrustados, pero supongo que ese no es el punto de la pregunta).

A continuación se muestra un ejemplo artificial de dónde podrían resultar útiles las comillas invertidas:

my $user = `whoami`;
chomp $user;
print "Hello, $user!\n";

Para casos más complicados, también puedes usar abierto como una pipa:

open WHO, "who|"
    or die "who failed";
while(<WHO>) {
    # Do something with each line
}
close WHO;

Desde la página de manual "perlop":

Eso no significa que debas salir de tu camino para evitar retrocesos cuando son la forma correcta de hacer algo.Perl estaba hecho para ser un lenguaje de pegamento, y una de las cosas que coloca juntas son los comandos.Solo comprende en qué te estás metiendo.

Para el caso que estás mostrando usando el Copia de archivo El módulo es probablemente el mejor.Sin embargo, para responder a su pregunta, cada vez que necesito ejecutar un comando del sistema, normalmente confío en IPC::Ejecutar3.Proporciona muchas funciones, como recopilar el código de retorno y la salida estándar y de error.

Hagas lo que hagas, además de desinfectar la entrada y comprobar el valor de retorno de tu código, asegúrate de llamar a cualquier programa externo con su ruta completa y explícita.p.ej.decir

my $user = `/bin/whoami`;

o

my $result = `/bin/cp $from $to`;

Decir simplemente "whoami" o "cp" corre el riesgo de ejecutar accidentalmente un comando distinto al previsto, si la ruta del usuario cambia, lo cual es una vulnerabilidad de seguridad que un atacante malintencionado podría intentar explotar.

Su ejemplo es malo porque hay elementos integrados de Perl para hacer eso que son portátiles y generalmente más eficientes que la alternativa de acento grave.

Deben usarse sólo cuando no existe una alternativa integrada (o módulo) de Perl.Esto es tanto para comillas invertidas como para llamadas al sistema().Las comillas invertidas están destinadas a capturar el resultado del comando ejecutado.

Se supone que las comillas invertidas solo deben usarse cuando desea capturar la salida.Usarlos aquí "Parece una tontería". Va a dar toque a cualquiera que mire su código en el hecho de que no está muy familiarizado con Perl.

Utilice comillas invertidas si desea capturar la salida.Utilice el sistema si desea ejecutar un comando.Una ventaja que obtendrá es la posibilidad de comprobar el estado de la devolución.Utilice módulos siempre que sea posible para la portabilidad.En este caso, File::Copy se ajusta a los requisitos.

En general, es mejor utilizar sistema en lugar de comillas invertidas porque:

  1. sistema anima a la persona que llama a comprobar el código de retorno del comando.

  2. sistema permite la notación de "objetos indirectos", que es más segura y añade flexibilidad.

  3. Las comillas invertidas están culturalmente ligadas a los scripts de shell, lo que puede no ser común entre los lectores del código.

  4. Las comillas invertidas utilizan una sintaxis mínima para lo que puede ser un comando pesado.

Una de las razones por las que los usuarios podrían verse tentados a utilizar comillas invertidas en lugar de sistema es ocultar STDOUT al usuario.Esto se logra de manera más fácil y flexible redirigiendo la secuencia STDOUT:

my $cmd = 'command > /dev/null';
system($cmd) == 0 or die "system $cmd failed: $?"

Además, deshacerse de STDERR se logra fácilmente:

my $cmd = 'command 2> error_file.txt > /dev/null';

En situaciones en las que tiene sentido utilizar comillas invertidas, prefiero utilizar la qx{} para enfatizar que se está produciendo un comando de peso pesado.

Por otro lado, tener Otra manera de hacerlo puede ser de gran ayuda.A veces sólo necesitas ver qué imprime un comando en STDOUT.Las comillas invertidas, cuando se usan como en los scripts de shell, son la herramienta adecuada para el trabajo.

Perl tiene una doble personalidad.Por un lado, es un excelente lenguaje de programación que puede sustituir el uso de un shell.En este tipo de uso único de "yo observando el resultado", las comillas invertidas son convenientes.

Cuando se utiliza un lenguaje de programación, se deben evitar las comillas invertidas.Esta es una falta de comprobación de errores y, si se puede evitar el programa de programas separado, se puede evitar la eficiencia.

Aparte de lo anterior, la función del sistema debe usarse cuando no se usa la salida del comando.

Las comillas invertidas son para aficionados.La solución a prueba de balas es una "tubería abierta segura" (ver "man perlipc").Ejecutas tu comando en otro proceso, lo que te permite usar primero STDERR, setuid, etc.Ventajas:lo hace no confíe en el shell para analizar @ARGV, a diferencia de open("$cmd $args|"), que no es confiable.Puede redirigir STDERR y cambiar los privilegios de los usuarios sin cambiar el comportamiento de su programa principal.Esto es más detallado que las comillas invertidas, pero puede incluirlo en su propia función como run_cmd($cmd,@args);


sub run_cmd {
  my $cmd = shift @_;
  my @args = @_;

  my $fh; # file handle
  my $pid = open($fh, '-|');
  defined($pid) or die "Could not fork";
  if ($pid == 0) {
    open STDERR, '>/dev/null';
    # setuid() if necessary
    exec ($cmd, @args) or exit 1;
  }
  wait; # may want to time out here?
  if ($? >> 8) { die "Error running $cmd: [$?]"; }
  while (<$fh>) {
    # Have fun with the output of $cmd
  }
  close $fh;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top