当前位置: 移动技术网 > IT编程>脚本编程>Shell > PowerShell中运行CMD命令的技巧总结(解决名称冲突和特殊字符等问题)

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

2017年12月12日  | 移动技术网IT编程  | 我要评论
引言 我从老旧的 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

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网