欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

PowerShell中运行CMD命令的技巧总结(解决名称冲突和特殊字符等问题)

程序员文章站 2022-04-09 14:46:25
引言 我从老旧的 cmd.exe 命令行换到优秀的 powsershell.exe 已经有一段时间啦。您可能知道新的 windows powershell 可以运行任何旧...

引言

我从老旧的 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 别名。

复制代码 代码如下:
ps c:\> sc.exe 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

方案 1b:使用 cmd /c

另一个办法是把您的命令用引号括起让 cmd.exe 来运行。但这样做没啥效率,仅仅为了执行您的命令就得运行一个 cmd.exe 实例。

复制代码 代码如下:
ps c:\> cmd /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

方案 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 里晦涩选项:

复制代码 代码如下:
get-service lan* | % { $_.name; sc.exe sdshow $_.name }

或者您可以使用 powershell 的 get-item(别名 dir)过滤文件子集传递给 icacls.exe 来处理:
复制代码 代码如下:
dir c:\test -recurse | ? {$_.length -ge 1mb} | % { icacls.exe $_.fullname /grant administrator:`(f`) }

您甚至可以循环遍历几个数并结合好用的 fsutil.exe 来创建一批大小不同的文件用于测试项目:
复制代码 代码如下:
1..100 | % { fsutil.exe file createnew c:\test\file$_.txt ($_*10kb)


尾声

事到如今,您可能已经确信 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