Quando é o momento certo (e na hora errada) para usar acentos graves?

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Muitos programadores iniciantes escrever código como este:

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

  `cp $from $to`;
}

Isto é ruim, e por quê? Deve backticks nunca ser usado? Se sim, como?

Foi útil?

Solução

Algumas pessoas já mencionamos que você só deve usar acentos graves quando:

  • Você precisa de captura (ou suprimem) a saída.
  • Não existe função built-in ou módulo Perl para fazer a mesma tarefa, ou você tem uma boa razão para não usar o módulo ou built-in.
  • Você higienizar sua entrada.
  • Você verifica o valor de retorno.

Infelizmente, as coisas como verificar o valor de retorno corretamente pode ser bastante desafiador. Será que morrer para um sinal? Será que executar para conclusão, mas retornar um status engraçado saída? As formas padronizadas de tentar interpretar $? são apenas horrível.

Eu recomendo usar o IPC :: System :: Simples capture() e system() funções do módulo ao invés de backticks. A função capture() funciona como acentos graves, exceto que:

  • Ele fornece diagnósticos detalhados se o comando não for iniciado, é morto por um sinal, ou retorna um valor de saída inesperada.
  • Ele fornece um diagnóstico detalhado se aprovada dados contaminados.
  • Ele fornece um mecanismo fácil para especificar valores de saída aceitáveis.
  • Ele permite que você chamar backticks sem a casca, se você quiser.
  • Prevê mecanismos confiáveis ??para evitar a casca, mesmo se você usar um único argumento.

Os comandos também funcionam consistentemente em sistemas operacionais e versões de Perl, ao contrário do Perl embutido system() que não pode verificar se há dados contaminado quando chamado com vários argumentos em versões antigas do Perl (por exemplo, 5.6.0 com múltiplos argumentos), ou que pode chamar o shell de qualquer maneira no Windows.

Como exemplo, o seguinte trecho de código irá salvar os resultados de uma chamada para perldoc em um escalar, evita o shell, e lança uma exceção se a página não pode ser encontrada (desde Perldoc retornos 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 :: System :: Simples é Perl puros, obras em 5.6.0 e acima, e não tem quaisquer dependências que normalmente não vêm com a sua distribuição Perl. (No Windows, ele depende de um Win32 :: módulo que vem com ActiveState e morango Perl).

Disclaimer: Eu sou o autor da IPC :: System :: Simples , para que eu possa mostrar algum viés.

Outras dicas

A regra é simples: nunca use backticks se você pode encontrar um built-in para fazer o mesmo trabalho, ou se o seu é um módulo robusto no CPAN que vai fazer isso por você. Acentos graves muitas vezes dependem de código portável e mesmo se você untaint as variáveis, você ainda pode abrir-se a uma série de falhas de segurança.

Nunca use backticks com os dados do usuário a menos que tenha muito firmemente especificados o que é permitido (não o que não é permitido - você vai perder as coisas)! Isto é muito, muito perigoso.

Backticks deve ser usado se e somente se você precisa capturar a saída de um comando. Caso contrário, deve ser utilizado o sistema (). E, claro, se há uma função Perl ou módulo CPAN que faz o trabalho, isso deve ser usado em vez de qualquer um.

Em ambos os casos, duas coisas são fortemente encorajados:

Primeiro, higienizar todas as entradas: Use o modo Taint (T) se o código está exposta a possível entrada não confiável. Mesmo se não for, certifique-se de punho (ou prevenir) caracteres funk como espaço ou os três tipos de citação.

Em segundo lugar, verificar o código de retorno para garantir que o comando foi bem sucedido. Aqui está um exemplo de como fazê-lo:

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

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

Outra forma de stdout de captura (para além de código pid e saída) é a utilização de IPC :: Open3 possibily negando o uso de ambos sistema e backticks.

Use backticks quando pretende recolher a saída do comando.

Caso contrário system() é uma escolha melhor, especialmente se você não precisa de invocar um shell para metacharacters manipular ou de análise de comando. Você pode evitar que ao fazer passar uma lista para system (), por exemplo system('cp', 'foo', 'bar') (no entanto, você provavelmente fazer melhor usar um módulo para que particular, exemplo:))

Em Perl, há sempre mais do que uma maneira de fazer o que quiser. O principal ponto de backticks é obter a saída padrão do comando shell em uma variável Perl. (No seu exemplo, qualquer coisa que o comando cp impressões será devolvido para o chamador.) A desvantagem de usar acentos graves no seu exemplo é que você não verificar o valor de retorno do comando shell; cp pode falhar e você não iria notar. Você pode usar isso com o especial $ variável Perl ?. Quando eu quero executar um comando shell, que tendem a usar sistema :

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

(Observe também que este irá falhar em nomes de arquivos com espaços incorporados, mas presumo que não é o ponto da questão.)

Aqui está um exemplo artificial de onde backticks pode ser útil:

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

Para casos mais complicados, você também pode usar open como um tubo:

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

Do "perlop" manpage:

Isso não significa que você deve sair de seu caminho para backticks evitar ao eles são o caminho certo para obter algo feito. Perl foi feito para ser uma cola linguagem, e uma das coisas que colas juntos é comandos. Somente entender o que você está recebendo em si mesmo.

Para o caso você está mostrando usando o href="http://search.cpan.org/search?query=File+Copy&mode=module" rel="nofollow noreferrer"> arquivo módulo é provavelmente o melhor. No entanto, para responder à sua pergunta, sempre que eu preciso para executar um comando do sistema Eu normalmente contam com IPC: : RUN3 . Ele oferece uma série de funcionalidades, tais como a recolha o código de retorno e a saída padrão e erro.

Faça o que fizer, bem como higienização de entrada e verificar o valor de retorno do seu código, certifique-se de chamar qualquer programas externos com o seu explícito, caminho completo. por exemplo. digamos

my $user = `/bin/whoami`;

ou

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

Dizer apenas "whoami" ou "cp" corre o risco de acidentalmente executar um comando diferente do que você pretendia, se as mudanças caminho do usuário -. O que é uma vulnerabilidade de segurança que um invasor mal-intencionado poderia tentar explorar

do seu mau exemplo, porque há builtins perl para fazer aquilo que são portáteis e geralmente mais eficiente do que a alternativa crase.

Eles devem ser usados ??somente quando não há alternativa Perl embutido (ou módulo). Isto é tanto para acentos graves e system () chamadas. Backticks são destinados para capturar saída do comando executado.

Backticks só deve ser usado quando você quer saída de captura. Usá-los aqui "parece bobo." Vai pista alguém olhar para o seu código para o fato de que você não está muito familiarizado com Perl.

Use backticks se você quer saída de captura. Usar o sistema se você quiser executar um comando. Uma vantagem que você vai ganhar é a capacidade de verificar o status de retorno. Use módulos, sempre que possível para a portabilidade. Neste caso, File :: Copiar se encaixa no projeto.

Em geral, é melhor usar sistema em vez de acentos graves, porque:

  1. sistema incentiva o chamador para verificar o código de retorno do comando.

  2. sistema permite notação "objeto indireto", que é mais seguro e adiciona flexibilidade.

  3. Backticks são culturalmente ligada à shell script, que pode não ser comum entre os leitores do código.

  4. Backticks usar sintaxe mínima para o que pode ser um comando pesado.

Uma usuários razão pode ser temped para usar acentos graves em vez de sistema é esconder STDOUT do usuário. Isto é mais fácil e flexível feito, redirecionando o fluxo STDOUT:

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

Além disso, se livrar de STDERR é facilmente realizado:

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

Em situações em que faz sentido backticks uso, eu prefiro usar o qx {} , a fim de enfatizar que existe um comando peso-pesado ocorrendo.

Por outro lado, ter outra maneira de fazer pode realmente ajudar. Às vezes você só precisa ver o que um comando imprime para stdout. Acentos graves, quando usado como em scripts shell são apenas a ferramenta certa para o trabalho.

Perl tem uma personalidade dividida. Na mão um é uma grande linguagem de script que pode substituir o uso de uma concha. Neste tipo de uso one-off I-assistindo-the-resultado, backticks são convenientes.

Quando usado uma linguagem de programação, acentos graves devem ser evitados. Isso é uma falta de erro verificação e, se os acentos graves programa separado executar pode ser evitado, a eficiência é ganhou.

Além do acima, a função do sistema deve ser usado quando a saída do comando não está sendo usado.

Backticks são para amadores. A solução à prova de bala é uma "Safe Cachimbo Open" (ver "homem perlipc"). Você exec seu comando em outro processo, o que permite que você primeiro futz com STDERR, setuid, etc. Vantagens: ele faz não contar com a Shell para @ARGV análise, ao contrário open ( "$ cmd $ args | "), que não é confiável. Você pode redirecionar STDERR e alterar privilégios de usuários sem alterar o comportamento do seu programa principal. Esta é mais detalhado do que acentos graves, mas você pode envolvê-lo em sua própria função 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top