如何创建带有可选参数的批处理文件
How to create a batch file that takes optional parameters
我不是 Windows 人,来自 Linux 方面。
我的问题是,如何编写一个将可选开关作为布尔值或值参数的批处理文件?例如:
ssh.cmd /X /l 登录远程主机
其中 /X 是一个布尔开关,意思是 "turn on feature X",而 /l 是一个 "value switch",意思是命令行上的下一个标记将以特殊方式使用。例如,在 unix 中,您可以这样做:
ssh -X -l 登录远程主机
这里,-X 是一个布尔值,告诉 ssh 转发 X 端口,-l 是一个 "value switch",它告诉 ssh 我的用户名是 "login"。 remotehost 是强制参数。我知道如何处理命令文件中的最后一个,并且无法在 Googlespace 的任何地方找到解决方案。
我认为做到这一点的唯一方法是痛苦地浏览参数,寻找符合正确格式的字符串,然后说 "aha! found a switch!" 对吗?
提前致谢。
@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
这是另一个拒绝无法识别的开关的选项,接受 /
和 -
作为有效的开关前缀,执行 case-insensitive 开关检查,并 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 批处理脚本,您可以考虑另一种脚本语言。许多人会建议使用 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 扩展名保存它可以使它更 user-friendly 执行。只需将其放在上述任一解决方案的顶部,并使用 .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
退出并且 Batch 线程恢复,直到 goto :EOF
或 exit /b
.
添加混合代码后,语法为 scriptname.bat arguments here
。如果您想绕过批处理代码并只执行 JScript 部分,cscript /nologo /e:JScript scriptname.bat argumentss here
也可以。
我不是 Windows 人,来自 Linux 方面。 我的问题是,如何编写一个将可选开关作为布尔值或值参数的批处理文件?例如:
ssh.cmd /X /l 登录远程主机
其中 /X 是一个布尔开关,意思是 "turn on feature X",而 /l 是一个 "value switch",意思是命令行上的下一个标记将以特殊方式使用。例如,在 unix 中,您可以这样做:
ssh -X -l 登录远程主机
这里,-X 是一个布尔值,告诉 ssh 转发 X 端口,-l 是一个 "value switch",它告诉 ssh 我的用户名是 "login"。 remotehost 是强制参数。我知道如何处理命令文件中的最后一个,并且无法在 Googlespace 的任何地方找到解决方案。
我认为做到这一点的唯一方法是痛苦地浏览参数,寻找符合正确格式的字符串,然后说 "aha! found a switch!" 对吗?
提前致谢。
@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
这是另一个拒绝无法识别的开关的选项,接受 /
和 -
作为有效的开关前缀,执行 case-insensitive 开关检查,并 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 批处理脚本,您可以考虑另一种脚本语言。许多人会建议使用 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 扩展名保存它可以使它更 user-friendly 执行。只需将其放在上述任一解决方案的顶部,并使用 .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
退出并且 Batch 线程恢复,直到 goto :EOF
或 exit /b
.
添加混合代码后,语法为 scriptname.bat arguments here
。如果您想绕过批处理代码并只执行 JScript 部分,cscript /nologo /e:JScript scriptname.bat argumentss here
也可以。