【问题标题】:How to assign values to a variable dynamically in batch script?如何在批处理脚本中动态地为变量赋值?
【发布时间】:2017-04-02 04:31:14
【问题描述】:

假设循环运行在一组命令行参数上,如下所示。 现在如何将下一个命令行值分配给循环内的变量?

FOR %%q IN (%*) DO
(
IF %%q == "abc" (
set a = //next command line value
             )
)

在 linux 中可以这样做

argv=("$@");
i=0
while(($i < ${#argv[*]})); do
case "${argv[$i]}" in
abc)
  ((i++)); a="${argv[$i]}"
;;
esac
((i++))
done

我想知道批处理文件中的等效项。

【问题讨论】:

  • 查看for /f帮助和delayed expansion
  • 不要使用批处理(shell 脚本)。 PowerShell 要好得多。

标签: windows batch-file


【解决方案1】:

我不会使用 for 循环来读取参数。正如LinuxDisciple 提到的那样,不要太快地将批处理与bash 或csh 进行比较。例如。批量创建或访问数组或哈希并不容易(如果有兴趣,我在答案的末尾添加了一些链接,他们解释了它是如何实现的)。如果你真的想使用 for 循环,使用 state 变量可能是你最好的选择(参见Klitos Kyriacou's answer)。

我会使用传统标签并批量转到

@echo off
SetLocal EnableDelayedExpansion
:check_args
REM Check if arguments left
IF "%~1%" == "" goto :EndArgs
IF "%~1" == "abc" (
    REM next argument is %2
    set var=%~2
    REM Don't forget extra shift because you already handled next argument too
    shift
    echo I just saw you gave "abc", I set var to !var! 
) ELSE (
    echo It's just a normal argument: "%~1"
)
shift
goto :check_args

:EndArgs
EndLocal
exit

正如LinuxDisciple 所提到的,在分配变量时不要用空格包围“=”,这可能会产生意想不到的结果(带有尾随空格的变量名称,带有标题空格的值,...)。上面的代码将产生类似(脚本称为 argum.bat):

>argum abc blabla def
I just saw you gave "abc", I set var to "blabla"
It's just a normal argument: def

>argum "just random things" abc "this is more serious"
It's just a normal argument: "just random things"
I just saw you gave "abc", I set var to "this is more serious"

这是delayed expansion 的链接。在这种情况下需要它,因为 cmd 解析器会将 IF 子句和 FOR 循环作为一个命令加载(就好像它写在一行上一样)。除非您使用延迟扩展,否则您无法在同一命令中创建变量并从中读取。它还解释了为什么我使用!var! 而不是%var%。 我还使用%~1 而不是%1 来删除周围的引号(“)。这个link 有一些其他的“技巧”可以用来操纵参数和for 循环变量

当然,如果你想用你的参数做一些更复杂的事情,这不是一个好的选择,因为 if-else 子句会占用很大的大小。您将不得不使用其他方式来处理您的论点,就像我在问这个question 时使用的那样(请同时阅读答案)或在此question 中提出的作为答案的方法(也作为链接发布在也可以回答我的问题)。

祝你好运!

PS如许,批量解决数组的链接:

  • this tutorial 中,您会明白为什么使用普通数组索引很“棘手”
  • 另一种方法是列出字符串中的所有元素,用分隔符分隔它们(主要使用空格)并使用for /f-loop 和分隔符作为分隔符进行迭代(就像你所做的那样)。这里的问题是您必须使用额外的变量来访问其他数组元素,而不是您在迭代期间所处的元素。

【讨论】:

    【解决方案2】:

    与 J. Baoby 的解决方案一样,此解决方案不使用 for 循环或状态变量。此解决方案也不需要延迟扩展或 setlocal
    在这里为更多参数添加处理很简单;没有嵌套的IFs。
    这会解析出以“-a”开头的值和以“-b”开头的值。然后它将其他参数单独存储在一个名为 otherArgs 的变量中,以备日后使用。

    @ECHO OFF
    REM make sure we don't start with unintended values
    set avar=
    set bvar=
    set otherArgs=
    :loop
    REM Exit the loop when we run out of input
    if "%1" == "" goto outsideOfLoop
    
    REM ==Check for an argument which is preceded by '-a'===============
    if "%1"=="-a"  set avar=%2 & shift /1 & shift /1 & goto :loop
    REM ==END '-a' handling here========================================
    
    REM ==Check for an argument which is preceded by '-b'===============
    if "%1"=="-b"  set bvar=%2 & shift /1 & shift /1 & goto :loop
    REM ==END -'b' handling here========================================
    
    REM default case here
    set otherArgs=%otherArgs% %1
    shift /1
    goto :loop
    :outsideOfLoop
    echo.%%avar%% is %avar%
    echo.%%bvar%% is %bvar%
    echo.Other arguments were: %otherArgs%
    

    输出:

    C:\Users\me>junk.bat hi there def -b BValue ef -a aValue valuebc 123 456
    %avar% is aValue
    %bvar% is BValue
    Other arguments were:  hi there def ef valuebc 123 456
    

    这有消耗 %I 参数的副作用,但它不需要不断增加嵌套 if 语句的深度来处理更多参数,并且它将您处理的参数与其他已提供的内容,因此如果您以后需要处理这些内容,则不必删除已识别的内容。

    【讨论】:

      【解决方案3】:

      使用状态变量。

      setlocal
      rem Undefine state variable first, in case it's defined somewhere else.
      set is_abc=
      for %%q in (%*) DO (
         if defined is_abc (
            set a=%%q
            set is_abc=
         ) else (
            if "%%q" == "abc" set is_abc=1
         )
      )
      

      【讨论】:

        猜你喜欢
        • 2021-01-20
        • 2018-12-09
        • 2011-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-26
        • 2015-07-24
        • 1970-01-01
        相关资源
        最近更新 更多