如何创建一个采用可选参数的批处理文件

时间:2016-01-22 00:49:46

标签: batch-file optional-parameters

我不是Windows人,来自Linux方面。 我的问题是,如何编写一个将可选开关作为布尔值或值参数的批处理文件?例如:

ssh.cmd / X / l登录remotehost

其中/ X是布尔开关,意思是"打开特征X"和/ l是一个"值开关"意味着命令行上的下一个标记将以特殊方式使用。例如,在unix中,您可以这样做:

ssh -X -l login remotehost

这里,-X是一个布尔告诉ssh来转发X端口,-l是一个"值开关"告诉ssh我的用户名是"登录"。 remotehost是必需参数。我知道如何只处理命令文件中的最后一个,并且无法在Googlespace中的任何地方找到解决方案。

我认为这样做的唯一方法就是痛苦地浏览寻找符合正确格式的字符串的论据,然后说出#34;啊哈!发现了一个开关!"那是对的吗?

提前致谢。

2 个答案:

答案 0 :(得分:3)

这是拒绝无法识别的交换机的另一个选项,接受/-作为有效的交换机前缀,执行不区分大小写的交换机检查,并ping远程主机以检查有效性。

@echo off
setlocal enabledelayedexpansion

if not "%~1"=="" goto :switches

:usage
echo Usage: %~nx0 [/X] [/L loginname] remotehost
goto :EOF

:switches
rem // make sure variables are undefined
for %%I in (forward login host switch valid) do set "%%I="

rem // loop through all arguments
for %%I in (%*) do (

    rem // if this iteration is a value for the preceding switch
    if defined switch (

        rem // if that switch was -L or /L (case-insensitive)
        if /i "!switch:~1!"=="L" set "login=%%~I"

        rem // clear switch variable
        set "switch="

    ) else (

        rem // if this iteration is a switch
        echo(%%~I | >NUL findstr "^[-/]" && (

            set "switch=%%~I"

            rem // if this is a valid switch
            for %%x in (X L) do (
                if /i "!switch:~1!"=="%%x" set "valid=true"
            )

            if not defined valid (
                echo Unrecognized switch: %%~I
                goto usage
            )

            set "valid="

            if /i "!switch:~1!"=="X" (
                set "forward=true"
                set "switch="
            )
        ) || (

            rem // not a switch.  Must be a host.
            ping -n 1 %%~I | findstr /i "^Reply.*time.*[0-9]" >NUL && (
                set "host=%%~I"
            ) || (
                echo Host %%~I is unreachable.
            )
        )
    )
)

if not defined host goto usage

<NUL set /P "=Args passed: "
if defined forward <NUL set /P "=FORWARD "
if defined login <NUL set /P "=LOGIN %login% "
echo %host%

您知道,如果您不熟悉Windows Batch脚本的脚本,您可能会考虑使用其他脚本语言。许多人会建议PowerShell,这是一个值得的选择。但如果您不想学习全新的语言和格式,如果您熟悉JavaScript,JScript就非常相似。我将演示,我希望你能够看到相似之处。使用.js扩展名保存。要运行它,请执行cscript /nologo scriptname.js switches here

// convert WSH.Arguments object to a JS array
for (var args=[], i=0; i<WSH.Arguments.Length; i++)
    args.push(WSH.Arguments(i));

function error(txt, code) {
    WSH.StdErr.WriteLine(txt);
    WSH.StdErr.WriteLine('Usage: '+WSH.ScriptName+' [/X] [/L loginname] remotehost');
    WSH.Quit(code);
}

for (var i=0; i<args.length; i++) {
    switch (args[i].toLowerCase().replace("-","/")) {

        case "/x":
            var forward = true;
            break;

        case "/l":
            if (++i == args.length || /^\//.test(args[i]))
                error('Invalid login name.', 1);
            else var login = args[i];
            break;

        default:
            if (/^\//.test(args[i]))
                error('Unrecognized option: ' + args[i], 2);
            else var host = args[i];
    }
}

if (!host) error('No host specified.', 3);

WSH.Echo([
    'Command: ',
    (forward ? 'FORWARD ' : ''),
    (login ? 'LOGIN ' + login + ' ' : ''),
    host
].join(''));

JScript和JavaScript有很多共同之处。您可以调用相同的RegExp对象方法和Array对象方法,修改原型,将数据强制转换为兼容的数据类型或测试其真实性,等等。 JScript JavaScript,只是使用“WScript”的根祖先对象(或懒惰的“WSH”)而不是“Window”。

// convert WSH.Arguments proprietary object to a JS array
for (var args=[], i=0; i<WSH.Arguments.Length; i++)
    args.push(WSH.Arguments(i));

function error(txt, code) {
    WSH.StdErr.WriteLine(txt);
    WSH.StdErr.WriteLine('Usage: '+WSH.ScriptName+' [/X] [/L loginname] remotehost');
    WSH.Quit(code);
}

// returns idx of array element matching rxp (or -1 if no match)
Array.prototype.match = function(rxp) {
    for (var i=this.length; i--;) if (rxp.test(this[i])) return i;
    return -1;
}

var X = args.match(/^[/-]X$/i);
if (X > -1) var forward = args.splice(X, 1)[0];

var L = args.match(/^[/-]L$/i);
if (L > -1) var login = args.splice(L, 2)[1];

for (var invalid=[], i=args.length; i--;)
    if (/^[/-]/.test(args[i])) invalid.push(args[i]);

if (invalid.length) error('Unrecognized option: ' + invalid.join(), 1);
if (!args.length) error('No host specified.', 2);

WSH.Echo([
    'Params: ',
    (forward ? 'FORWARD ' : ''),
    (login ? 'LOGIN ' + login + ' ' : ''),
    args[0]
].join(''));

如果您想使用这些参数来启动外部程序,请查看objShell.Run

如果要将脚本部署到其他用户,将其转换为Batch + JScript混合格式并使用.bat扩展名保存,则会使其更易于执行。只需将其置于上述任一解决方案的顶部,并使用.bat扩展名保存脚本。

@If (@CodeSection == @Batch) @then
@echo off & setlocal
cscript /nologo /e:JScript "%~f0" %*
goto :EOF
@end // end Batch / begin JScript hybrid chimera
// JScript code here

它的工作方式是,首先,脚本使用cmd批处理解释器加载。由于(@CodeSection不等于@Batch),因此永远不会尝试其余行。继续直到cscript行,它使用JScript解释器重新加载此脚本,并传递命令行参数。

JScript确定@CodeSection实际上不等于@Batch,并跳至@end。在JScript执行结束时,cscript.exe退出,批处理线程将恢复到goto :EOFexit /b

添加混合代码后,语法现为scriptname.bat arguments here。如果您想绕过批处理代码并仅执行JScr​​ipt部分,cscript /nologo /e:JScript scriptname.bat argumentss here也可以。

答案 1 :(得分:2)

@echo off
setlocal EnableDelayedExpansion

rem Define the "value switches"
set "valSwitch=L M N"

rem Load parameters in "switch" and "argv" vectors
set "argc=0"

:nextParam
   set "param=%~1"
   if "%param:~0,1%" equ "/" (

      rem Is a switch
      for %%s in (%param:~1,1%) do (
         if "!valSwitch:%%s=!" equ "%valSwitch%" (
            rem Is a boolean switch
            set "switch[%%s]=True"
         ) else (
            rem Is a value switch
            set "switch[%%s]=%~2"
            shift
         )
      )

   ) else (

      rem Is a standard parameter
      set /A argc+=1
      set "argv[!argc!]=%~1"

   )

   shift
if "%~1" neq "" goto nextParam

SET SWITCH
SET ARGV

输出示例:

C:\Users\Antonio\Test> test.bat /X /l login remotehost
switch[l]=login
switch[X]=True
argv[1]=remotehost

C:\Users\Antonio\Test> test.bat /X remotehost /l login
switch[l]=login
switch[X]=True
argv[1]=remotehost

C:\Users\Antonio\Test> test.bat remotehost /l login /X
switch[l]=login
switch[X]=True
argv[1]=remotehost

C:\Users\Antonio\Test> test.bat /Y /A /M "login1 login2" remotehost "last parameter"
switch[A]=True
switch[M]=login1 login2
switch[Y]=True
argv[1]=remotehost
argv[2]=last parameter