1
votes

Processus Perl ARGV dans un script appelé avec les options -p et -f

J'ai un script perl que j'appelle avec les options -p et -f. Je voudrais transmettre les paramètres de ligne de commande à ARGV dans le script.

Par exemple, opl.pl est un script qui concatène chaque ligne qui ne commence pas par xx em> sur la ligne précédente commençant par xx , avec '#' comme séparateur, après avoir signalé les caractères '#' préexistants:

yy line __hash__1#line 2#line 3
yy line 4#line 5

Le script fonctionne lorsqu'aucun paramètre supplémentaire n'est sur la ligne de commande. Par exemple, perl -pf oplx.pl filexx.txt avec filexx.txt:

yy line #1
line 2
line 3
yy line 4
line 5

Produit (approximativement):

xx line __hash__1#line 2#line 3
xx line 4#line 5

Je voudrais utiliser perl -pf oplx.pl filexyy.txt yy avec fileyy.txt :

xx line #1
line 2
line 3
xx line 4
line 5

pour produire (approximativement):

# Usage: perl -pf opl.pl file.txt
BEGIN {$recmark = @ARGV[0] if $#ARGV; }
$recmark  = "xx" if (! defined $recmark);
chomp;
print "\n" if /$recmark/;
s/#/\_\_hash\_\_/g;
$_ .= "#"

Malheureusement, perl analyse l'argument de ligne de commande yy code> comme nom de fichier, plutôt que comme argument.


7 commentaires

BTW: Je suis conscient et prêt à vivre avec les faits que: 1) Le script fait une première ligne vierge et omet un \ n de la dernière ligne. 2) Le script vérifie inutilement la définition de la variable $ recmark à chaque ligne.


De manière générale, la réponse est Comment puis-je traiter les options en utilisant Perl en mode -n ou -p? . Mais dans ce cas précis, la meilleure solution est d'arrêter d'utiliser -p comme le disent les réponses.


@ikegami, qui utilise déjà le script dans divers scripts bash et a d'autres utilisateurs qui utilisent le script. Supprimer l'option -p n'est pas une option pour moi.


Vous ne devriez JAMAIS passer -p à un script. Cela n'a absolument aucun sens pour l'appelant de fournir une partie du programme. Et bien sûr, vous pouvez entrer dans ce problème même.


@ikegami, la prochaine fois, je serai prêt pour la dernière fois.


@Wes Avec cette question, vous allez modifier la façon dont le script est utilisé, non? Ensuite, vous pouvez inclure le changement par lequel -p n'est plus nécessaire, et dans le script ajouter while (<>) {} et traiter les fichiers à l'intérieur, comme je le montre dans mon réponse par exemple. (Sans oublier que vous devez introduire des arguments nommés appropriés; cela le rendra beaucoup mieux.)


Si vous voulez ignorer -p s'il est fourni, vous pouvez utiliser quelque chose comme BEGIN {if (! @ARGV || $ ARGV [0] ne 'deflagged') {exec $ ^ X , '-', $ 0, 'deflagged', @ARGV ou die $ !; } shift @ARGV; } .


3 Réponses :


1
votes

Le -n commutateur de commande

fait en sorte que Perl assume la boucle suivante autour de votre programme, ce qui le fait itérer sur des arguments de nom de fichier un peu comme sed -n ou awk :

while (<>) { ... }

filehandle est spécial comme

L'entrée de provient soit de l'entrée standard, soit de chaque fichier répertorié sur la ligne de commande.

En d'autres termes, il lit les lignes de tous les fichiers donnés sur la ligne de commande. Le -p fait la même chose sauf qu'il imprime également $ _ à chaque fois.

Ces noms de fichiers se trouvent dans @ARGV variable , qui dans votre exemple a filexyy.txt code> et yy , et qui sont donc traités comme des noms de fichiers.

Une solution: supprimez les paramètres nécessaires ( yy ici) de @ ARGV , dans un bloc BEGIN . Ensuite, l'opération de n'aura en effet que des noms de fichiers avec lesquels travailler.

Ceci soulève la question de l'interface souhaitée par votre programme. Si vous souhaitez que ce paramètre soit fourni en dernier sur la ligne de commande

my $param;
BEGIN {
    $param = pop @ARGV;
}

depuis pop supprime de l'arrière d'un tableau; si vous voulez que le paramètre soit donné en premier, utilisez shift . Notez que votre $ recmark devrait également être supprimé de @ARGV.

Garder une trace de tout cela est sujet aux erreurs et peu pratique à la fois pour l'utilisation et travaux ultérieurs.

Il serait de loin préférable de traiter ces arguments en utilisant un bon module, comme Getopt :: Long . Ensuite, vous pouvez leur donner des noms, changer facilement l'interface en cas de besoin et faire vérifier chaque invocation correctement par le module.

Notez également qu'avec les noms de fichiers dans @ARGV , qui est ce qui reste après que vous (ou Getopt :: Long ) avez terminé avec les options, vous pouvez traiter toutes les lignes de tous les fichiers à l'intérieur de

LINE:
  while (<>) {
     ...        # your program goes here
  }

en utilisant le même mentionné ci-dessus. Dans un script, c'est bien mieux que -p.


2 commentaires

Il h. Publié juste avant que j'allais terminer ma réponse. :)


@Alhadis Ouais, environ 6 minutes. Cela m'est arrivé aussi (pas une seule fois), et probablement à tout le monde



1
votes

Depuis la page de manuel perlrun (1) :

-p
fait en sorte que Perl assume la boucle suivante autour de votre programme, ce qui le fait itérer sur des arguments de nom de fichier un peu comme sed :

use Getopt::Long;
my $foo = "";
my $bar = "";
GetOptions("foo=s" => \$foo, "bar=s" => \$bar);

L'utilisation la plus appropriée du commutateur -p est pour les one-liners, où chaque argument de fichier est traité tour à tour, ligne par ligne, avec le résultat l'exécution du programme imprimée sur stdout.

Les crochets angulaires de Perl, qui ont été implicitement ajoutés par le commutateur -p , prennent un descripteur de fichier en entrée et parcourent chaque ligne jusqu'à ce que EOF soit atteint :

perl -f opt.pl --foo ABC --bar XYZ  file1.txt file2.txt …

CEPENDANT , si aucun descripteur de fichier n'est passé, les crochets angulaires seront par défaut @ARGV , traitant chacun argument disponible comme nom de fichier. Si @ARGV est vide, revient à l'entrée standard (équivalent à l'utilisation de ).

Si vous voulez passer les deux arguments Noms de fichiers et en ligne de commande, vous avez deux choix:

  1. Classez les arguments de manière à ce que les arguments non liés aux fichiers soient placés en premier, comme ceci:

    my $first = shift;  # Modifies @ARGV in-place, placing "ABC" in $first
    my $second = shift; # Same again, this time plucking "XYZ" from @ARGV and putting it in `$second`
    

Et dans votre script:

perl -f opt.pl ABC XYZ file1.txt file2.txt
  1. Ou utilisez le Getopt :: Long module pour passer les arguments sans nom de fichier comme commutateurs (ou "options"):

    while(<$opened_file_handle>) {
        …
    }
    

Et le code Perl pour faire ça:

 LINE:
   while (<>) {
       ...             # your program goes here
   } continue {
       print or die "-p destination: $!\n";
   }

Utiliser Getopt :: Long est le moyen le plus propre (et recommandé) pour passer des arguments lors du traitement d'une liste de fichiers.

J'espère que cela vous aidera!


1 commentaires

Je pense que je vais devoir utiliser Getopt :: Long



1
votes

Pensez à utiliser une variable d'environnement comme alternative à la manipulation avec vos arguments de ligne de commande.

recmark=yy perl -pf opl.pl file1 file2 ...

BEGIN { $recmark = $ENV{recmark} // "xx" };
...


1 commentaires

Je ferais cela sauf que le script doit également fonctionner sous Windows (Strawberry Perl).