D'autres réponses ont abordé l'un des deux principaux aspects de cette question: celui de savoir comment mener à bien l'opération de changement de nom dont vous aviez besoin. Le but de cette réponse est d'expliquer pourquoi vos commandes n'ont pas fonctionné, y compris la signification de cet étrange message d'erreur "bareword not allowed" dans le contexte de la rename
commande.
La première section de cette réponse concerne la relation entre rename
et Perl et comment rename
utilise le premier argument de ligne de commande que vous lui transmettez, qui est son argument de code. La deuxième section concerne la façon dont le shell effectue des extensions - en particulier le globbing - pour construire une liste d'arguments. La troisième section concerne ce qui se passe dans le code Perl qui donne des erreurs «Bareword not allowed». Enfin, la quatrième section est un résumé de toutes les étapes qui se déroulent entre la saisie de la commande et l'obtention de l'erreur.
1. Lorsque rename
vous donne des messages d'erreur étranges, ajoutez "Perl" à votre recherche.
Dans Debian et Ubuntu, la rename
commande est un script Perl qui effectue un changement de nom de fichier. Sur les versions antérieures - y compris 14.04 LTS, qui est toujours pris en charge au moment de la rédaction de ce document - c'était un lien symbolique pointant ( indirectement ) vers la prename
commande. Sur les versions plus récentes, il pointe à la place sur la nouvelle file-rename
commande. Ces deux commandes de changement de nom Perl fonctionnent principalement de la même manière, et je vais me référer à elles deux comme rename
pour le reste de cette réponse.
Lorsque vous utilisez la rename
commande, vous n'exécutez pas uniquement du code Perl que quelqu'un d'autre a écrit. Vous écrivez également votre propre code Perl et dites rename
de l'exécuter. Cela est dû au fait que le premier argument de ligne de commande que vous passez à la rename
commande, autre que les arguments d'option comme -n
, se compose de code Perl réel . La rename
commande utilise ce code pour opérer sur chacun des chemins d'accès que vous lui transmettez comme arguments de ligne de commande ultérieurs. (Si vous ne passez aucun argument de chemin d'accès, il rename
lit alors les noms de chemin à partir de l'entrée standard , un par ligne.)
Le code est exécuté dans une boucle , qui itère une fois par nom de chemin. En haut de chaque itération de la boucle, avant l'exécution de votre code, la $_
variable spéciale se voit attribuer le chemin d'accès en cours de traitement. Si votre code entraîne la $_
modification de la valeur de quelque chose d'autre, alors ce fichier est renommé pour avoir le nouveau nom.
De nombreuses expressions en Perl opèrent implicitement sur la $_
variable lorsqu'aucune autre expression ne leur est utilisée comme opérande . Par exemple, l'expression de substitution $str =~ s/foo/bar/
change la première occurrence de foo
dans la chaîne contenue par la $str
variable en bar
, ou la laisse inchangée si elle ne contient pas foo
. Si vous écrivez simplement s/foo/bar/
sans utiliser explicitement l' =~
opérateur , alors il fonctionne $_
. C'est-à-dire que s/foo/bar/
c'est court pour $_ =~ s/foo/bar/
.
Il est courant de passer une s///
expression à rename
comme argument de code (le premier argument de ligne de commande), mais vous ne devez pas. Vous pouvez lui donner n'importe quel code Perl que vous souhaitez qu'il s'exécute à l'intérieur de la boucle, pour examiner chaque valeur de $_
et (conditionnellement) le modifier.
Cela a beaucoup de conséquences intéressantes et utiles , mais la grande majorité d'entre elles dépassent largement le cadre de cette question et réponse. La principale raison pour laquelle j'apporte ceci ici - en fait, la principale raison pour laquelle j'ai décidé de poster cette réponse - est de faire valoir que, parce que le premier argument rename
est en fait du code Perl, chaque fois que vous obtenez un message d'erreur étrange et que vous avoir du mal à trouver des informations à ce sujet en recherchant, vous pouvez ajouter "Perl" à la chaîne de recherche (ou même remplacer "renommer" par "Perl", parfois) et vous trouverez souvent une réponse.
2. Avec rename *.DAT *.dat
, la rename
commande n'a jamais vu *.DAT
!
Une commande comme rename s/foo/bar/ *.txt
ne passe généralement pas en *.txt
tant qu'argument de ligne de commande au rename
programme, et vous ne le souhaitez pas , sauf si vous avez un fichier dont le nom est littéralement *.txt
, ce qui, espérons-le, ne vous convient pas.
rename
ne pas interpréter les modèles de glob aiment *.txt
, *.DAT
, *.dat
, x*
, *y
ou *
lorsqu'il lui a été transmis comme arguments pathname. Au lieu de cela, votre shell effectue une expansion de nom de chemin sur eux (ce qui est également appelé expansion de nom de fichier et également appelé globbing). Cela se produit avant l' rename
exécution de l' utilitaire. Le shell développe les globs en potentiellement plusieurs chemins d'accès et les transmet tous, en tant qu'arguments de ligne de commande séparés, à rename
. Dans Ubuntu, votre shell interactif est Bash , à moins que vous ne l'ayez modifié, c'est pourquoi je me suis connecté au manuel de référence Bash ci-dessus.
Il existe une situation dans laquelle un modèle glob peut être transmis sous la forme d'un seul argument de ligne de commande non développé à rename
: lorsqu'il ne correspond à aucun fichier. Différents shells présentent un comportement par défaut différent dans cette situation, mais le comportement par défaut de Bash est de simplement passer le glob littéralement. Cependant, vous en avez rarement envie! Si vous le vouliez, vous devez vous assurer que le modèle n'est pas développé, en le citant . Cela vaut pour passer des arguments à n'importe quelle commande, pas seulement à rename
.
Les citations ne sont pas uniquement destinées à la globalisation (extension de nom de fichier), car il existe d' autres extensions que votre shell effectue sur du texte non cité et, pour certaines d'entre elles mais pas pour d'autres , également sur du texte entre "
"
guillemets. En général, chaque fois que vous souhaitez passer un argument contenant des caractères qui peuvent être traités spécialement par le shell, y compris des espaces, vous devez le citer, de préférence avec des '
'
guillemets .
Le code Perl s/foo/bar/
ne contient rien de traité spécialement par le shell, mais cela aurait été une bonne idée pour moi de l'avoir aussi cité - et écrit 's/foo/bar/'
. (En fait, la seule raison pour laquelle je ne l' ai pas été qu'il serait source de confusion pour certains lecteurs, comme je l' avais pas encore parlé de citation.) La raison pour laquelle je dis cela aurait été bon est parce qu'il est très fréquent que le code Perl ne contient ces caractères, et si je devais changer ce code, je ne me souviendrais peut-être pas de vérifier si la citation était nécessaire. En revanche, si vous souhaitez que le shell développe un glob, il ne doit pas être cité.
3. Ce que l'interprète Perl veut dire par "mots nus non autorisés"
Les messages d'erreur que vous avez montrés dans votre question révèlent que, lorsque vous avez exécuté rename *.DAT *.dat
, votre shell s'est développé *.DAT
en une liste d'un ou plusieurs noms de fichiers, et que le premier de ces noms de fichiers l'était b1.DAT
. Tous les arguments suivants - tous les autres à partir de *.DAT
et tous ceux à partir de - ont été *.dat
développés après cet argument, ils auraient donc été interprétés comme des chemins d'accès.
Parce que ce qui s'est réellement exécuté était quelque chose comme rename b1.DAT ...
, et parce que rename
traite son premier argument sans option en tant que code Perl, la question devient: pourquoi b1.DAT
produit-il ces erreurs "bareword not allowed" lorsque vous l'exécutez en tant que code Perl?
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Dans un shell, nous citons nos chaînes pour les protéger contre les extensions de shell involontaires qui autrement les transformeraient automatiquement en d'autres chaînes (voir la section ci-dessus). Les shells sont des langages de programmation à usage spécial qui fonctionnent très différemment des langages à usage général (et leur syntaxe et sémantique très étranges le reflètent). Mais Perl est un langage de programmation à usage général et, comme la plupart des langages de programmation à usage général, le but principal de la citation en Perl n'est pas de protéger les chaînes, mais de les mentionner du tout. C'est en fait une façon dont la plupart des langages de programmation sont similaires au langage naturel. En anglais, et en supposant que vous avez un chien, "votre chien" est une phrase de deux mots, tandis que votre chien est un chien. De même, en Perl, '$foo'
est une chaîne, tandis que $foo
quelque chose dont le nom est $foo
.
Cependant, contrairement à peu près tous les autres langages de programmation à usage général, Perl aussi interpréter parfois le texte sans guillemets en mentionnant une chaîne - une chaîne qui est le « même » comme, dans le sens où il est composé des mêmes caractères le même ordre. Il essaiera d'interpréter le code de cette façon uniquement s'il s'agit d'un mot simple (aucun $
ou autre sigil, voir ci-dessous), et après il n'a pas pu trouver d'autre sens à lui donner. Ensuite, il le prendra comme une chaîne, sauf si vous le lui dites en activant les restrictions .
Les variables en Perl commencent généralement par un caractère de ponctuation appelé sigil , qui spécifie le type large de la variable. Par exemple, $
signifie scalaire , @
signifie tableau et %
signifie hachage . ( Il y en a d'autres. ) Ne vous inquiétez pas si vous trouvez cela déroutant (ou ennuyeux), car je ne fais que le dire pour dire que lorsqu'un nom valide apparaît dans un programme Perl mais n'est pas précédé d'un sigil, ce nom est dit être un simple mot .
Les mots nus servent à diverses fins, mais ils signifient généralement une fonction intégrée ou un sous-programme défini par l' utilisateur qui a été défini dans le programme (ou dans un module utilisé par le programme). Perl n'a pas de fonctions intégrées appelées b1
ou DAT
, donc quand l'interpréteur Perl voit le code b1.DAT
, il essaie de traiter b1
et DAT
comme les noms des sous-programmes. En supposant qu'aucun de ces sous-programmes n'a été défini, cela échoue. Ensuite, à condition que les restrictions n'aient pas été activées, il les traite comme des chaînes. Cela fonctionne, si vous avez réellement si oui ou non l' intention que cela se produise est quiconque conjecture. L' .
opérateur de Perl concatène les chaînes , donc b1.DAT
évalue la chaîneb1DAT
. Autrement dit, b1.DAT
est une mauvaise façon d'écrire quelque chose comme 'b1' . 'DAT'
ou "b1" . "DAT"
.
Vous pouvez tester cela vous-même en exécutant la commande perl -E 'say b1.DAT'
, qui transmet le court script Perl say b1.DAT
à l'interpréteur Perl, qui l'exécute, en imprimant b1DAT
. (Dans cette commande, les '
'
citations indiquent la coquille de passer say b1.DAT
comme un seul argument de ligne de commande, sinon, l'espace causerait say
et b1.DAT
à analysable comme des mots séparés et perl
les recevait comme arguments distincts. perl
Ne pas voir les citations elles - mêmes, la shell les supprime .)
Mais maintenant, essayez d'écrireuse strict;
dans le script Perl avant say
. Maintenant, il échoue avec le même type d'erreur que vous avez obtenu rename
:
$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
Cela s'est produit parce que use strict;
l'interpréteur Perl ne pouvait pas traiter les mots nus comme des chaînes. Pour interdire cette fonctionnalité particulière, il suffirait vraiment d'activer uniquement la subs
restriction. Cette commande produit les mêmes erreurs que ci-dessus:
perl -E 'use strict "subs"; say b1.DAT'
Mais généralement, les programmeurs Perl écrivent use strict;
, ce qui active la subs
restriction et deux autres. use strict;
est une pratique généralement recommandée. La rename
commande fait donc cela pour votre code. C'est pourquoi vous obtenez ce message d'erreur.
4. En résumé, voici ce qui s'est passé:
- Votre shell est passé en
b1.DAT
tant que premier argument de ligne de commande, rename
traité comme du code Perl à exécuter en boucle pour chaque argument de chemin d'accès.
- Cela a été pris pour signifier
b1
et DAT
lié à l' .
opérateur.
b1
et DAT
n'étaient pas préfixés par des sceaux, ils étaient donc traités comme des mots nus.
- Ces deux mots nus auraient été considérés comme des noms de fonctions intégrées ou de sous-programmes définis par l'utilisateur, mais rien de tel n'avait l'un de ces noms.
- Si les "sous-marins stricts" n'avaient pas été activés, ils auraient été traités comme les expressions de chaîne
'b1'
et 'DAT
'et concaténés. C'est loin de ce que vous vouliez, ce qui montre à quel point cette fonctionnalité n'est souvent pas utile.
- Mais « sous - marins stricts » a été activé, car
rename
permet toutes les restrictions ( vars
, refs
et subs
). Par conséquent, vous avez plutôt reçu une erreur.
rename
quitter en raison de cette erreur. Étant donné que ce type d'erreur s'est produit tôt, aucune tentative de changement de nom n'a été effectuée, même si vous ne l'avez pas réussi -n
. C'est une bonne chose, qui protège souvent les utilisateurs contre les changements involontaires de nom de fichier et parfois même contre la perte de données réelle.
Merci à Zanna , qui m'a aidé à combler plusieurs lacunes importantes dans une version plus récente de cette réponse . Sans elle, cette réponse aurait été beaucoup moins logique et n'aurait peut-être pas été publiée du tout.