PowerShell中运行CMD命令的技巧总结(解决名称冲突和特殊字符等问题)
引言
我从老旧的 cmd.exe 命令行换到优秀的 powsershell.exe 已经有一段时间啦。您可能知道新的 windows powershell 可以运行任何旧命令。不过有些旧命令的名称或语法可能会产生问题。但这都不是事儿。
麻烦 1:名称冲突
powershell 的 cmdlet 别名和旧命令的名称有冲突是个常见的问题。比如说您喜欢的服务控制命令 sc.exe。sc.exe 非常灵活!我能理解您为什么喜欢它(不要为用 net.exe 管理服务找借口)。如果您想查看 smb server 服务的状态,可以在 cmd.exe 里这样用:
c:\>sc query lanmanserver
service_name: lanmanserver
type : 20 win32_share_process
state : 4 running
(stoppable, pausable, ignores_shutdown)
win32_exit_code : 0 (0x0)
service_exit_code : 0 (0x0)
checkpoint : 0x0
wait_hint : 0x0
如果您在 powershell 中尝试同样的事,会得到:
ps c:\> sc query lanmanserver
set-content : access to the path 'c:\query' is denied.
at line:1 char:1
+ sc query lanmanserver
+ ~~~~~~~~~~~~~~~~~~~~~
+ categoryinfo : permissiondenied: (c:\query:string) [set-content], unauthorizedaccessexception
+ fullyqualifiederrorid : getcontentwriterunauthorizedaccesserror,microsoft.powershell.commands.setcontentcommand
因为 sc 是 set-content 的别名。它优先于 sc.exe 文件。
方案 1a:使用 .exe 扩展名
为了克服这个问题,您可以简单地将 .exe 扩展名包含进旧命令。这消除了歧义并使相同的命令在 cmd.exe 和 powershell 里都能用。还可以清楚告诉使用您脚本的人这里用的是旧 .exe 命令而非 powershell 别名。
service_name: lanmanserver
type : 20 win32_share_process
state : 4 running
(stoppable, pausable, ignores_shutdown)
win32_exit_code : 0 (0x0)
service_exit_code : 0 (0x0)
checkpoint : 0x0
wait_hint : 0x0
方案 1b:使用 cmd /c
另一个办法是把您的命令用引号括起让 cmd.exe 来运行。但这样做没啥效率,仅仅为了执行您的命令就得运行一个 cmd.exe 实例。
service_name: lanmanserver
type : 20 win32_share_process
state : 4 running
(stoppable, pausable, ignores_shutdown)
win32_exit_code : 0 (0x0)
service_exit_code : 0 (0x0)
checkpoint : 0x0
wait_hint : 0x0
方案 1c:用等效的 powershell
很多情况下,可以用 powershell cmdlet 来代替您的旧命令。
例如这里您就可以直接使用 get-service:
ps c:\> get-service lanmanserver | fl
name : lanmanserver
displayname : server
status : running
dependentservices : {browser}
servicesdependedon : {samss, srv}
canpauseandcontinue : true
canshutdown : false
canstop : true
servicetype : win32shareprocess
麻烦 2:powershell 的特殊字符
有时旧命令的参数使用的字符在 powershell 里有特殊意义。
比如您想让某个目录被所有用户完全控制。在 cmd.exe 里您可以这样做:
c:\>icacls.exe c:\test /grant users:(f)
processed file: c:\test
successfully processed 1 files; failed processing 0 files
在 cmd.exe 做这些没问题,但如果你在 powershell 运行就会报错:
ps c:\> icacls.exe c:\test /grant users:(f)
the term 'f' is not recognized as the name of a cmdlet, function, script file, or operable program. check the spelling
of the name, or if a path was included, verify that the path is correct and try again.
at line:1 char:34
+ icacls.exe c:\test /grant users:(f)
+ ~
+ categoryinfo : objectnotfound: (f:string) [], commandnotfoundexception
+ fullyqualifiederrorid : commandnotfoundexceptionn
试图给名字以 $ 结尾的电脑对象授权时也会引起一个类似的错误。
ps c:\> icacls.exe c:\test /grant computername$:(f)
at line:1 char:39
+ icacls.exe c:\test /grant computername$:(f)
+ ~~
invalid variable reference. '$' was not followed by a valid variable name character. consider using ${} to delimit the
name.
+ categoryinfo : parsererror: (:) [], parentcontainserrorrecordexception
+ fullyqualifiederrorid : invalidvariablereference
这个问题是因为括号和美元符在 powershell 中都有特殊意义。例如大括号之类常用字符也会引发相似的冲突。也有几种不同的方案来解决这个问题。
方案 2a:使用 cmd /c
和第一个问题一样,你可以引号括起您的命令交给 cmd.exe 来处理。先不考虑效率,powershell 不会去解析引号里的字符串,这样就能正常工作。
ps c:\> cmd.exe /c "icacls.exe c:\test /grant users:(f)"
processed file: c:\test
successfully processed 1 files; failed processing 0 files
方案 2b:使用 powershell 的转义字符
对于这个方案,您必须先知道使用的字符哪些对 powershell 有特殊意义。然后在它们每一个前面加上个反引号(`),它就是 powershell 的转义字符。这个方案的主要问题是你必须知道哪些字符需要转义,这让读写您的脚本更困难。
我们的例子里,你需要处理 ( 和 ) 这两个字符:
ps c:\> icacls.exe c:\test /grant users:`(f`)
processed file: c:\test
successfully processed 1 files; failed processing 0 files
方案 2c:使用 powershell v3 的新语法“–%”
在 powershell v3 中有另一种选择来解决这个问题。您只需在命令行的任意位置添加 –% 序列(两个短划线和一个百分号)powershell 就不会再去解析剩下的部分。
我们的例子里,您可以这样用:
ps c:\> icacls.exe --% c:\test /grant users:(f)
processed file: c:\test
successfully processed 1 files; failed processing 0 files
也可以这样用:
ps c:\> icacls.exe c:\test --% /grant users:(f)
processed file: c:\test
successfully processed 1 files; failed processing 0 files
方案 2d:使用等效的 powershell
使用等效的 powershell 也是种选择。icacls.exe 可以用 set-acl 代替。可以从这篇博客中找到更多的 set-acl 例子。
混搭
这里展示如何让您安全地享受 powershell 结合您的旧命令带来的灵活性。您可能会学到几个技巧并以全新的方式开始新老结合。
例如您能用灵活的 get-service 通配符代替 sc.exe 里晦涩选项:
或者您可以使用 powershell 的 get-item(别名 dir)过滤文件子集传递给 icacls.exe 来处理:
您甚至可以循环遍历几个数并结合好用的 fsutil.exe 来创建一批大小不同的文件用于测试项目:
尾声
事到如今,您可能已经确信 windows powershell 是管理员的好朋友啦。然而您可能因为有些旧命令带着古怪的名字或参数而不能使用 powershell.exe。我非常鼓励您使用这些技巧来彻底停用 cmd.exe 并永久迁移到 powershell 来作为您主要的 shell。
文章出处:http://www.pstips.net/using-windows-powershell-to-run-old-command-line-tools-and-their-weirdest-parameters.html
上一篇: Js获取当前日期时间及其它操作