2 votes

L'utilisation d'une variable d'indexation DelayedExpansion pour un tableau dans une instruction IF échoue.

J'ai un fichier batch dans lequel j'affiche tous les fichiers *.pem d'un répertoire, en donnant à l'utilisateur la possibilité d'en choisir un, et en essayant pour l'instant de montrer à l'utilisateur quel fichier il a choisi en utilisant le numéro qu'il a choisi pour ECHO le contenu du tableau. Je pense que, d'une manière ou d'une autre, parce qu'elle se trouve à l'intérieur de l'instruction IF, la variable d'index "selectedPem" ne se développe pas dans les parenthèses d'index du tableau parce qu'elle utilise le % et non l'expansion retardée ! qui ne semble pas fonctionner dans les parenthèses d'index du tableau. Je pense que c'est parce que si je place la variable "selectedPem" avant l'instruction IF, elle s'ECHOppe correctement. Je pense que la seule option pour que cela fonctionne est d'utiliser une sous-routine. Pourquoi la variable selectedPem ne fonctionne-t-elle pas dans l'instruction IF ?

@echo off
setlocal enabledelayedexpansion 
set dir=%~dp0
cd %dir%

ECHO Performing initial search of private and public keys...
set pemI=0
set keyI=0
for %%p in (*.pem) do (
    REM echo %%~nxp
    set pems[%pemI%]=%%~nxp
    REM echo !pems[%pemI%]!
    set /a pemI=%pemI%+1
)
REM echo %pems[0]%
set /a totalPems=%pemI%-1
REM This is the test selectedPem variable that showed me the variable works 
REM if I set it outside 
REM of the "if defined pems[0]" statement
REM set selectedPem=
if defined pems[0] (
    echo PEM Files Found:
    for /l %%p in (0,1,%totalPems%) do (
        echo %%p^) !pems[%%p]!
    )
    ECHO Select a pem file to be used as your private key. Otherwise, press 
    Q to not select one.
    set /P selectedPem=Enter a number or Q:
    IF "!selectedPem!"=="q" (
        ECHO Skipping private key selection.
    ) ELSE (
        ECHO !selectedPem!
        ECHO %pems[0]%
        REM The below ECHO only works when the above selectedPem is set 
        REM outside of the "IF defined" statement
        ECHO !pems[%selectedPem%]!

        REM Tried these other implementations:
        REM ECHO !pems[!!selectedPem!!]!
        REM ECHO %pems[!selectedPem!]%
        REM setlocal disabledelayedexpansion
        REM ECHO %pems[%%selectedPem%%]%

        set privatekey=!pems[%selectedPem%]!
        ECHO You chose %privatekey%
    )   
)`

Sortie :

Performing initial search of private and public keys...
PEM Files Found:
0) brandon.pem
Select a pem file to be used as your private key. Otherwise, press Q to not 
select one.
Enter a number or Q:0
0
brandon.pem
ECHO is off.
You chose

Je comprends que "ECHO is off." est sorti à la place parce qu'il interprète ma référence de variable de tableau comme vide. J'apprécie le temps que vous consacrez à la lecture de mon script, je le complique probablement trop.

3voto

aschipfl Points 17476

Vous semblez être bien conscient de expansion retardée puisque vous l'utilisez correctement la plupart du temps.

Cependant, ce que vous avez dans votre code, je l'appelle généralement des variables imbriquées, parce qu'il n'y a pas de véritables tableaux dans les scripts batch puisque chaque élément de tableau n'est rien d'autre qu'une variable scalaire individuelle ( pems[0] , pems[1] , pems[2] ) ; on peut donc parler de pseudo-raies.

Bref, quelque chose comme echo !pems[%selectedPem%]! peut être correctement développée, à moins qu'elle ne soit placée dans une ligne ou un bloc de code dans lequel selectedPem est mis à jour avant, parce qu'alors nous aurions besoin d'une expansion retardée pour selectedPem également. echo !pems[!selectedPem!]! ne fonctionne pas, car il essaie d'étendre les variables pems[ y ] et selectedPem est interprété comme une chaîne littérale.

Avant d'aller plus loin, revenons d'abord à echo !pems[%selectedPem%]! : pourquoi est-ce que c'est extensible ? Eh bien, l'astuce est que nous avons deux phases d'expansion ici, l'expansion normale ou immédiate ( % ), suivie par l'expansion retardée ( ! ). L'important est la séquence : la variable intérieure des variables imbriquées (donc l'indice du tableau) doit être dépensée avant la variable extérieure (donc l'élément du tableau). Quelque chose comme echo %pems[!selectedPem!]% ne peut pas être développé correctement, car il n'y a (très probablement) aucune variable nommée pems[!selectedPem!] et après l'avoir étendu à une chaîne de caractères vide, les deux ! disparaissent et la phase d'expansion retardée ne les voit donc jamais.

Allons maintenant un peu plus loin : pour développer quelque chose de similaire à echo !pems[%selectedPem%]! à l'intérieur d'une ligne ou d'un bloc de code qui met également à jour selectedPem nous devons éviter une expansion immédiate. Nous avons déjà appris que l'expansion différée ne peut pas être imbriquée, mais il existe d'autres expansions :

  1. Le site call commande introduit une autre phase d'expansion après l'expansion retardée, qui gère à nouveau % -La séquence complète est donc l'expansion immédiate, l'expansion différée et une autre % -Expansion. Ignorant la première, utilisons les deux dernières de manière à ce que la variable intérieure des variables imbriquées soit développée avant la variable extérieure, c'est-à-dire : call echo %%pems[!selectedPem!]%% . Pour sauter la phase d'expansion immédiate, il suffit de doubler la valeur du % qui sont remplacés par un signe littéral chacun, de sorte que nous avons echo %pems[!selectedPem!]% après une expansion immédiate. L'étape suivante est l'expansion différée, puis la dite suivante % -Expansion.
    Vous devez prendre note que le call est assez lente, de sorte qu'une utilisation intensive pourrait réduire considérablement les performances globales de votre script.
  2. Expansion de for boucle Les variables se produisent après l'expansion immédiate, mais avant l'expansion différée. Enveloppons donc un for boucle autour qui itère une seule fois et renvoie la valeur de selectedPem comme ceci : for %%Z in (!selectedPem!) do echo !pems[%%Z]! . Cela fonctionne, car for n'accède pas au système de fichiers, sauf si un joker * ou ? apparaît, qui ne devrait pas être utilisé dans les noms de variables ou les index de (pseudo-)tableaux de toute façon.
    Au lieu d'un for boucle, for /F pourrait également être utilisé : for /F %%Z in ("!selectedPem!") do echo !pems[%%Z]! (il n'y a pas de chaîne d'options comme "delims=" nécessaire en cas de selectedPem est censé ne contenir qu'un numéro d'index).
    A for /L boucle pourrait également être utilisé (pour les index numériques, bien sûr) : for /L %%Z in (!selectedPem!,1,!selectedPem!) do echo !pems[%%Z]! .
  3. Expansion implicite établie par le set /A commande ce qui signifie que ni l'un ni l'autre % ni ! est nécessaire pour lire les variables, peut aussi être utilisée, mais seulement si l'élément du tableau contient une valeur numérique (notez que la fonction /A représente l'arithmétique). Comme il s'agit d'une fonctionnalité spécifique à set /A ce type d'expansion se produit pendant l'exécution de la commande, qui se produit à son tour après une expansion retardée. Donc on peut utiliser ça comme ça : set /A "pem=pems[!selectedPem!]" & echo !pem! .
  4. Par souci d'exhaustivité, voici une autre façon de procéder : set /A lorsqu'il est exécuté dans cmd plutôt que dans le contexte d'un lot, affiche le (dernier) résultat sur la console ; étant donné que cela constitue le numéro d'index, nous pourrions capturer cela par for /F et développer l'élément du tableau comme ceci : for /F %%Z in ('set /A "selectedPem"') do echo !pems[%%Z]! . set /A est exécuté dans une nouvelle cmd instance par for /F Cette approche n'est donc pas la plus rapide, bien entendu.

Il existe un article très complet sur ce sujet que vous devriez lire en détail : Tableaux, listes chaînées et autres structures de données dans cmd.exe (batch) script. .

Pour l'analyse des lignes de commande et des scripts ainsi que l'expansion des variables en général, référez-vous à ce fil de discussion génial : Comment l'interpréteur de commandes Windows (CMD.EXE) analyse-t-il les scripts ?


Maintenant, jetons un coup d'oeil à votre code. Il y a plusieurs problèmes :

  • utiliser cd /D au lieu de cd afin de changer également le variateur si nécessaire ; d'ailleurs, la variable intermédiaire dir n'est pas nécessaire (je recommande de ne pas utiliser de noms de variables qui correspondent à des commandes internes ou externes pour des raisons de lisibilité), utilisez simplement cd sur le chemin immédiatement ;
  • vous avez omis d'utiliser l'expansion retardée en ligne set pems[%pemI%]=%%~nxp il faut lire set pems[!pemI!]=%%~nxp como pemI est mis à jour dans l'environnement for boucle ;
  • vous avez soit besoin d'une expansion retardée en ligne set /a pemI=%pemI%+1 aussi, ou vous utilisez l'expansion implicite de variable de set /A Así que set /A pemI=pemI+1 fonctionnerait ; on peut même simplifier davantage : set /A pemI+=1 ce qui est totalement équivalent ;
  • J'utiliserais la comparaison insensible à la casse, en particulier lorsqu'il s'agit d'une saisie de l'utilisateur, comme dans le cas de votre vérification de la présence de Q qui serait if /I "!selectedPem!"=="q" ;
  • Nous arrivons maintenant à une ligne qui nécessite ce que nous avons appris ci-dessus : ECHO !pems[%selectedPem%]! doit devenir call echo %%pems[!selectedPem!]%% ; une autre façon d'utiliser for est également inséré dans un commentaire ( rem ) ;
  • puis il y a une autre ligne avec le même problème : set privatekey=!pems[%selectedPem%]! doit devenir call set privatekey=%%pems[!selectedPem!]%% (ou l'approche basée sur for aussi) ;
  • une fois de plus vous avez manqué l'utilisation de l'expansion retardée, cette fois en ligne ECHO You chose %privatekey% qui devrait se lire echo You chose !privatekey! ;
  • enfin j'ai amélioré la citation de votre code, en particulier celle de set qui devraient plutôt être écrites comme set "VAR=Value" La valeur elle-même est protégée si elle contient des caractères spéciaux, mais les guillemets ne font pas partie de la valeur, ce qui peut être particulièrement gênant pour la contaténation ;

Voici donc le code corrigé (avec tous vos originaux rem remarques supprimées) :

@echo off
setlocal EnableDelayedExpansion 
cd /D "%~dp0"

echo Performing initial search of private and public keys...
set "pemI=0"
set "keyI=0"
for %%p in (*.pem) do (
    set "pems[!pemI!]=%%~nxp"
    set /A "pemI+=1"
)
set /A "totalPems=pemI-1"
if defined pems[0] (
    echo PEM Files Found:
    for /L %%p in (0,1,%totalPems%) do (
        echo %%p^) !pems[%%p]!
    )
    set /P selectedPem="Enter a number or Q: "
    if /I "!selectedPem!"=="q" (
        echo Skipping private key selection.
    ) else (
        echo !selectedPem!
        echo %pems[0]%
        call echo %%pems[!selectedPem!]%%
        rem for %%Z in (!selectedPem!) do echo !pems[%%Z]!
        call set "privatekey=%%pems[!selectedPem!]%%"
        rem for %%Z in (!selectedPem!) do set "privatekey=!pems[%%Z]!"
        echo You chose !privatekey!
    )   
)

Je dois admettre que je n'ai pas vérifié la logique de votre script en détail cependant.

0voto

Avec quelques goto's, vous pouvez éviter la plupart des (blocs de code).

Dans les cas où cela n'est pas possible, utilisez l'option ! et une alternative d'expansion retardée avec un appel et un doublé %% correctement.

Non testé :

@echo off
setlocal enabledelayedexpansion 
set dir=%~dp0
cd %dir%

ECHO Performing initial search of private and public keys...
set pemI=0
set keyI=0
for %%p in (*.pem) do (
    REM echo %%~nxp
    set pems[!pemI!]=%%~nxp
    REM call echo %%pems[!pemI!]%%
    set /a pemI+=1
)
REM echo %pems[0]%
set /a totalPems=%pemI%
REM This is the test selectedPem variable that showed me the variable works 
REM if I set it outside 
REM of the "if defined pems[0]" statement
REM set selectedPem=

if pemI==0 (Echo no *.pem files found & goto :End)

echo PEM Files Found:
for /l %%p in (0,1,%totalPems%) do (
    echo %%p^) !pems[%%p]!
)
ECHO Select a pem file to be used as your private key. Otherwise, press 
Q to not select one.
set /P selectedPem=Enter a number or Q:
IF /I "!selectedPem!"=="q" (ECHO Skipping private key selection.& goto :End

ECHO !selectedPem!
ECHO %pems[0]%
REM The below ECHO only works when the above selectedPem is set 
REM outside of the "IF defined" statement
ECHO !pems[%selectedPem%]!

REM Tried these other implementations:
REM ECHO !pems[!!selectedPem!!]!
REM ECHO %pems[!selectedPem!]%
REM setlocal disabledelayedexpansion
REM ECHO %pems[%%selectedPem%%]%

set privatekey=!pems[%selectedPem%]!
ECHO You chose %privatekey%
)   
:end
REM ddo wahatever
pause

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X