Quel est le bon moment (et le mauvais moment) pour utiliser des backticks?

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

  •  02-07-2019
  •  | 
  •  

Question

De nombreux programmeurs débutants écrivent du code comme celui-ci:

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

  `cp $from $to`;
}

Est-ce mauvais, et pourquoi? Est-ce que les backticks devraient être utilisés? Si oui, comment?

Était-ce utile?

La solution

Quelques personnes ont déjà mentionné le fait que vous ne devriez utiliser des contrecoups lorsque:

  • Vous devez capturer (ou supprimer) la sortie.
  • Il n’existe pas de fonction intégrée ni de module Perl pour effectuer la même tâche, ou vous avez une bonne raison de ne pas utiliser le module ou la fonction intégrée.
  • Vous désinfectez votre entrée.
  • Vous vérifiez la valeur de retour.

Malheureusement, la vérification de la valeur de retour correctement peut être assez difficile. Est-il mort à un signal? At-il abouti, mais a-t-il retourné un état de sortie amusant? Les méthodes habituelles d’essayer d’interpréter $? sont simplement terribles.

Je recommanderais d'utiliser IPC :: System :: Simple les fonctions capture () et system () du module plutôt que des backticks. La fonction capture () fonctionne exactement comme les backticks, sauf que:

  • Il fournit des diagnostics détaillés si la commande ne démarre pas, est tuée par un signal ou renvoie une valeur de sortie inattendue.
  • Il fournit des diagnostics détaillés en cas de transmission de données altérées.
  • Il fournit un mécanisme simple pour spécifier des valeurs de sortie acceptables.
  • Il vous permet d’appeler des backticks sans shell, si vous le souhaitez.
  • Il fournit des mécanismes fiables pour éviter le shell, même si vous utilisez un seul argument.

Les commandes fonctionnent également de manière cohérente sur les systèmes d'exploitation et les versions de Perl, à la différence du system () intégré de Perl, qui peut ne pas rechercher les données altérées lorsqu'il est appelé avec plusieurs arguments sur des versions antérieures de Perl (par exemple, 5.6.0 avec plusieurs arguments), ou qui peut quand même appeler le shell sous Windows.

Par exemple, l'extrait de code suivant enregistre les résultats d'un appel à perldoc dans un scalaire, évite le shell et lève une exception si la page est introuvable (puisque perldoc renvoie 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: <*> arguments\n";

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

IPC :: System :: Simple est du pur Perl, fonctionne sur les versions 5.6.0 et supérieures, et n’a aucune dépendance qui ne serait normalement pas fournie avec votre distribution Perl. (Sous Windows, cela dépend d’un module Win32 :: fourni avec ActiveState et Strawberry Perl.)

Avertissement: je suis l'auteur de IPC :: System :: Simple , je peux donc montrer un parti pris.

Autres conseils

La règle est simple: n'utilisez jamais de backtick si vous pouvez trouver un agent intégré pour faire le même travail, ou s'il s'agit d'un module robuste sur le CPAN qui le fera pour vous. Les backticks reposent souvent sur du code non portable et même si vous décompressez les variables, vous pouvez toujours vous ouvrir à de nombreuses failles de sécurité.

N'utilisez jamais de backticks avec les données utilisateur, à moins que vous n'ayez spécifié très précisément ce qui est autorisé (et non ce qui est interdit - vous manquerez des choses)! C'est très très dangereux.

Les crapauds doivent être utilisés si et seulement si vous devez capturer le résultat d'une commande. Sinon, system () devrait être utilisé. Et, bien sûr, si une fonction Perl ou un module CPAN fait le travail, utilisez-le plutôt que l'un ou l'autre.

Dans les deux cas, deux choses sont vivement encouragées:

D'abord, nettoie toutes les entrées: utilisez le mode Taint (-T) si le code est exposé à d'éventuelles entrées non fiables. Même si ce n’est pas le cas, assurez-vous de gérer (ou d’empêcher) les caractères géniaux comme l’espace ou les trois types de citation.

Deuxièmement, vérifiez le code de retour pour vous assurer que la commande a abouti. Voici un exemple de la façon de le faire:

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

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

Un autre moyen de capturer stdout (en plus des codes pid et exit) consiste à utiliser IPC :: Open3 peut éventuellement empêcher l'utilisation du système et des backticks.

Utilisez des backticks lorsque vous souhaitez collecter le résultat de la commande.

Sinon, system () est un meilleur choix, en particulier si vous n'avez pas besoin d'appeler un shell pour gérer les métacaractères ou l'analyse de commandes. Vous pouvez éviter cela en passant une liste à system (), par exemple system ('cp', 'foo', 'bar') (cependant, vous feriez probablement mieux d'utiliser un module pour cela < em> exemple particulier :))

En Perl, il y a toujours plus d'une façon de faire ce que vous voulez. Le principal point de départ est d'obtenir la sortie standard de la commande shell dans une variable Perl. (Dans votre exemple, tout ce que la commande cp imprimera sera retourné à l'appelant.) L'inconvénient de l'utilisation de backticks dans votre exemple est que vous ne vérifiez pas la valeur de retour de la commande shell; cp pourrait échouer et vous ne le remarqueriez pas. Vous pouvez l'utiliser avec la variable spéciale Perl $ ?. Lorsque je veux exécuter une commande shell, j'ai tendance à utiliser système :

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

(Notez également que cela échouera pour les noms de fichiers contenant des espaces, mais je présume que ce n'est pas le but de la question.)

Voici un exemple astucieux où les pics-arrière pourraient être utiles:

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

Pour les cas plus complexes, vous pouvez également utiliser ouvrir en tant que canal:

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

À partir du & per; perlop " page de manuel:

  

Cela ne signifie pas que vous devriez sortir de   votre moyen d'éviter les backticks quand   ils sont la bonne façon d'obtenir quelque chose   terminé. Perl a été fait pour être une colle   la langue et l’une des choses qu’elle   colle ensemble est des commandes. Juste   comprendre ce que vous obtenez   vous dans.

Pour le cas que vous affichez à l'aide de Fichier :: Copier le module est probablement le meilleur. Cependant, pour répondre à votre question, chaque fois que je dois exécuter une commande système, je me base généralement sur IPC: : Run3 . Il fournit de nombreuses fonctionnalités telles que la collecte du code de retour et des sorties standard et d'erreur.

Quoi que vous fassiez, ainsi que pour nettoyer l’entrée et vérifier la valeur de retour de votre code, assurez-vous d’appeler tous les programmes externes avec leur chemin complet, explicite. par exemple. dire

my $user = `/bin/whoami`;

ou

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

Dire simplement "whoami" ou " cp " risque d'exécuter accidentellement une commande autre que celle que vous aviez prévue si le chemin d'accès de l'utilisateur était modifié - ce qui constitue une vulnérabilité de sécurité qu'un attaquant malveillant pourrait tenter d'exploiter.

Votre exemple est mauvais car il existe des fonctions intégrées à Perl qui sont portables et généralement plus efficaces que l'alternative backtick.

Ils ne doivent être utilisés qu’en l'absence d'alternative Perl (ou module). C'est à la fois pour les backticks et les appels system (). Les backticks sont destinés à capturer la sortie de la commande exécutée.

Les backticks ne sont supposés être utilisés que lorsque vous voulez capturer une sortie. Les utiliser ici "a l’air idiot". Cela révélera à tous ceux qui consultent votre code que vous ne connaissez pas très bien Perl.

Utilisez des backticks si vous souhaitez capturer une sortie. Utilisez system si vous souhaitez exécuter une commande. Un des avantages que vous obtiendrez est la possibilité de vérifier l'état des retours. Utilisez des modules si possible pour la portabilité. Dans ce cas, File :: Copy convient parfaitement.

En règle générale, il vaut mieux utiliser système au lieu de contrecoups car:

  1. système encourage l'appelant à vérifier le code de retour de la commande.

  2. le système autorise "l'objet indirect" " la notation, qui est plus sécurisée et ajoute de la flexibilité.

  3. Les backticks sont culturellement liés aux scripts shell, ce qui peut ne pas être le cas chez les lecteurs du code.

  4. Les Backtick utilisent une syntaxe minimale pour ce qui peut être une commande lourde.

L'une des raisons pour lesquelles les utilisateurs peuvent être amenés à utiliser des backticks au lieu de système consiste à masquer STDOUT à l'utilisateur. Ceci est accompli plus facilement et de manière flexible en redirigeant le flux STDOUT:

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

En outre, il est facile de se débarrasser de STDERR:

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

Dans les situations où il est judicieux d'utiliser des backticks, je préfère utiliser qx {} afin de souligner le fait qu'une commande très lourde est utilisée.

D'autre part, avoir une autre façon de faire peut vraiment aider. Parfois, il suffit de voir quelle commande est imprimée sur STDOUT. Les backticks, lorsqu'ils sont utilisés comme dans des scripts de shell, ne sont que le bon outil pour le travail.

Perl a une double personnalité. D'une part, c'est un excellent langage de script qui peut remplacer l'utilisation d'un shell. Dans ce type d'utilisation ponctuelle du type «Je regarde les résultats», les backticks sont pratiques.

En cas d'utilisation d'un langage de programmation, les backticks doivent être évités. C'est un manque d'erreur vérifier et, si les backticks séparés du programme peuvent être évités, l’efficacité est gagné.

Outre ce qui précède, la fonction système doit être utilisée lorsque le résultat de la commande n'est pas utilisé.

Les backticks sont pour les amateurs. La solution à l'épreuve des balles est un "Safe Pipe Open". (voir "man perlipc"). Vous exécutez votre commande dans un autre processus, ce qui vous permet de commencer par utiliser STDERR, setuid, etc. args | "), ce qui n'est pas fiable. Vous pouvez rediriger STDERR et modifier les privilèges utilisateur sans modifier le comportement de votre programme principal. C’est plus bavard que des backticks mais vous pouvez l’envelopper dans votre propre fonction, comme 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top