web-dev-qa-db-fra.com

Est-il possible d'indiquer les n derniers paramètres d'un fichier de commandes?

Dans l'exemple suivant, je souhaite appeler un fichier de commandes enfant à partir d'un fichier de commandes parent et transmettre tous les paramètres restants à l'enfant.

C:\> parent.cmd child1 foo bar
C:\> parent.cmd child2 baz zoop
C:\> parent.cmd child3 a b c d e f g h i j k l m n o p q r s t u v w x y z

Dans parent.cmd, je dois supprimer% 1 de la liste des paramètres et ne transmettre que les paramètres restants au script enfant.

set CMD=%1
%CMD% <WHAT DO I PUT HERE>

J'ai étudié l'utilisation de SHIFT avec% *, mais cela ne fonctionne pas. Bien que SHIFT déplace les paramètres de position de 1 à 1,% * fait toujours référence aux paramètres d'origine.

Quelqu'un a des idées? Devrais-je abandonner et installer Linux?

38
Dave

%* sera toujours étendu à tous les paramètres d'origine, malheureusement. Mais vous pouvez utiliser l'extrait de code suivant pour créer une variable contenant tout le paramètre sauf le premier:

rem throw the first parameter away
shift
set params=%1
:loop
shift
if [%1]==[] goto afterloop
set params=%params% %1
goto loop
:afterloop

Je pense que cela peut être fait plus rapidement, cependant ... Je n'écris pas ce genre de choses très souvent :)

Devrait fonctionner, cependant.

69
Joey

Voici une approche d'une ligne utilisant la commande "pour" ...

for /f "usebackq tokens=1*" %%i in (`echo %*`) DO @ set params=%%j

Cette commande attribue le premier paramètre à "i" et le reste (désigné par *) est attribué à "j", qui est ensuite utilisé pour définir la variable "params".

15
max_diff

la ligne

%CMD% <WHAT DO I PUT HERE>

sera remplacé par:

(
  SETLOCAL ENABLEDELAYEDEXPANSION
  SET skip=1

  FOR %%I IN (%*) DO IF !skip! LEQ 0 (
        SET "params=!params! %%I"
    ) ELSE SET /A skip-=1
)
(
  ENDLOCAL
  SET "params=%params%"
)
%CMD% %params%

bien sûr, vous pouvez définir skip var avec un nombre quelconque d'arguments.

Explaned:

(
:: Starting block here, because it's read once and executed as one
:: otherwise cmd.exe reads file line by line, which is waaay slower.

SETLOCAL ENABLEDELAYEDEXPANSION
SET skip=1

:: if value contains unquoted non-paired parenthesis, SET varname=value 
:: confuses cmd.exe. SET "a=value" works better even if value has quotes.
  FOR %%I IN (%*) DO (
    IF !skip! LEQ 0 (
      SET "params=!params! %%I"
      :: newline after SET to lower amount of pitfalls when arguments 
      :: have unpaired quotes
    ) ELSE (
      SET /A skip-=1
    )
)
(
:: get variables out of SETLOCAL block
:: as whole block in parenthesis is read and expanded before executing,
:: SET after ENDLOCAL in same block will set var to what it was before
:: ENDLOCAL. All other envvars will be reset to state before SETLOCAL.
ENDLOCAL
SET "params=%params%"
)
:: doing this outside of parenthesis block to avoid
:: cmd.exe confusion if params contain unquoted closing round bracket
%CMD% %params%
10
LogicDaemon

Vous pouvez réellement faire ceci:

%*

Si c'est la seule chose sur la ligne, alors le premier paramètre be est exécuté, avec tous les autres paramètres qui lui sont transmis. :)

7
Alexander Bird

J'ai amélioré la réponse de Moshe Goren parce qu'elle ne semblait pas fonctionner pour moi:

set _input=%*
call set params=%%_input:%1 =%%
@echo %params%
1
RichardSchorrig

Une autre manière (presque identique à celle d’Alxander Bird) sans exécuter ECHO dans un sous-shell:

FOR /F "usebackq tokens=1*" %%I IN ('%*') DO SET params=%%J

alors, ligne

%CMD% <WHAT DO I PUT HERE>

ressemblera:

FOR /F "usebackq tokens=1*" %%I IN ('%*') DO %CMD% %%J

le problème est que si les paramètres incluent les chaînes citées avec des espaces, cmd.exe les analysera de manière appropriée pour les utiliser comme arguments numérotés (% 1), mais FOR ignorera les guillemets. Dans ce cas particulier, il sera préjudiciable que le premier paramètre inclue un espace ou plus, ce qui est tout à fait possible, étant donné que le premier argument peut être, par exemple, "C:\Program Files\Internet Explorer\iexplore.exe".

Donc, voici une autre réponse.

1
LogicDaemon

Bien que la solution 'for' soit supérieure dans de nombreuses circonstances, pour quelque chose de simple, je vais souvent sauvegarder et déplacer les autres arguments, puis utiliser %* comme d'habitude (pratiquement la même stratégie fonctionne souvent pour $* ou $@ dans {,ba,da,k,*}sh):

exemple:

:: run_and_time_me.cmd - run a command with the arguments passed, while also piping
::                       the output through a second passed-in executable

@echo off

set run_me=%1
set pipe_to_me=%2
shift
shift

:: or
:: set run_me=%1
:: shift
:: set pipe_to_me=%1
:: shift

%run_me% %* | %pipe_to_me%

Quoi qu’il en soit, j’ai vu la réponse à la question posée longtemps, mais j’ai pensé que c’était une chose que je ne voyais pas, et c’était la réponse dont j’avais besoin quand j’y suis finalement tombé quelques années auparavant .. .et est allé "oh ... duh." :)

0
shelleybutterfly

J'ai rencontré cette question alors que je faisais face à un problème similaire, mais je l'ai résolue en utilisant une approche différente. le premier paramètre de la chaîne d'entrée.

set _input=%*
set params=!_input:%1 =!
call OTHER_BATCH_FILE.cmd %params%

Bien entendu, cela suppose que tous les paramètres sont différents.

0
Moshe Goren