我正在尝试在Windows 7中创建一个批处理文件,以帮助我删除Skype广告。
这是我到目前为止所得到的:
@echo off
echo Editing '%WINDIR%\System32\Drivers\Etc\Hosts' file
echo 127.0.0.1 apps.skype.com >> %WINDIR%\System32\Drivers\Etc\Hosts
echo 127.0.0.1 g.msn.com >> %WINDIR%\System32\Drivers\Etc\Hosts
echo Editing '%APPDATA%\Skype' config
powershell -Command "(Get-ChildItem %APPDATA%\Skype -Filter "config.xml" -Recurse) | ForEach-Object { $_ -replace '<AdvertPlaceholder>1</AdvertPlaceholder>', '<AdvertPlaceholder>0</AdvertPlaceholder>' } | Set-Content $_"
ipconfig /flushdns
pause
powershell命令会抛出一个错误:
Editing 'C:\Users\user\AppData\Roaming\Skype' config
Set-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:218
+ (Get-ChildItem C:\Users\user\AppData\Roaming\Skype -Filter config.xml -Recurse) | ForEach-Object { $_ -replace '<AdvertPlaceholder>1</AdvertPlaceholder>
', '<AdvertPlaceholder>0</AdvertPlaceholder>' } | Set-Content <<<< $_
+ CategoryInfo : InvalidData: (:) [Set-Content], ParameterBinding
ValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SetContentCommand
我做错了什么?如何将内容设置为Get-ChildItem找到的文件?如果你有另一种方法,我也想听听。非常感谢。
编辑:
以下是我最终使用的内容:
@echo off
echo Editing '%WINDIR%\System32\Drivers\Etc\Hosts' file
echo 127.0.0.1 apps.skype.com >> %WINDIR%\System32\Drivers\Etc\Hosts
echo 127.0.0.1 g.msn.com >> %WINDIR%\System32\Drivers\Etc\Hosts
echo Editing '%APPDATA%\Skype' config
powershell -noprofile -c "$files = Get-ChildItem $env:AppData\Skype -Recurse config.xml; foreach($file in $files) { (Get-Content $file.fullname) -replace '<(AdvertPlaceholder)>1</\1>', '<$1>0</$1>' | Set-Content $file.fullname }"
ipconfig /flushdns
echo Done. Please restart Skype for changes to take effect.
pause
在这篇文章发表时确认工作。
答案 0 :(得分:2)
你有几个问题,我认为你正在走错路。 Nathan Tuggy had one part right,Set-Content
获取管道输入并将其发送到文件。在大多数情况下,需要使用-Path
(或位置)指定其路径。之前的另一个问题是您实际上并未阅读您找到的文件。您当前的代码正在尝试操作文件名本身,这不是您想要的。
更接近你正在寻找的东西是:
powershell -Command "$file = Get-ChildItem %APPDATA%\Skype -Filter 'config.xml' -Recurse | Select -First 1 -ExpandProperty FullName; (Get-Content $file) | ForEach-Object { $_.replace('<AdvertPlaceholder>1</AdvertPlaceholder>', '<AdvertPlaceholder>0</AdvertPlaceholder>')} | Set-Content $file"
我还没有完全按批次测试,因为我只是想试着告诉你哪里出错了。
获取文件名,假设该目录结构中只有一个config.xml文件。使用-First 1
确保只返回一个文件名。然后将该文件名提供给Get-Content以获取该文件。然后在循环中处理文件以替换所需的文本。还使用了.Replace()
而不是-replace
,因为后者支持正则表达式而你不是。
我认为,等待测试,它会解决您的问题。
但是,您不是以最有效和最可靠的方式编辑XML文件。您也可以在单个PowerShell脚本中拥有所有这些逻辑。让我们看一下:
# Append some loopback entries into the host file. Make sure you are running as admin else this would fail.
"apps.skype.com","g.msn.com" | Foreach-Object{"127.0.0.1`t$_"} | Add-Content "$env:windir\System32\Drivers\Etc\Hosts"
# Beware that multiple executions of this would keep adding more entries as we do not check the existing ones.
# Locate and read the config.xml document. Use object notation to find the node we need to change to 0 then save the file.
$configPath = Get-Item "$($env:appdata)\Skype\*\config.xml" | Select-Object -ExpandProperty FullName
$xml = [xml](Get-Content $configPath)
$xml.config.UI.General.AdvertPlaceholder = "0"
$xml.Save($configPath)
如果您需要帮助运行脚本here is a good post to get you started。
我认为只有config.xml存在。如果您想编辑所有配置文件,那么很少需要更改。这次虽然我们需要确保您要编辑的值退出。由于config.xml不是唯一的名称。
# Locate and read the config.xml documents. Use object notation to find the node we need to change to 0 then save the file.
$configPaths = Get-ChildItem "$($env:appdata)\Skype" -Filter "config.xml" -Recurse | Select-Object -ExpandProperty FullName
# Cycle each file found
$configPaths | ForEach-Object{
$xml = [xml](Get-Content $_)
if($xml.config.UI.General.AdvertPlaceholder){
# Check to be sure this value actually is present before we try to change it.
$xml.config.UI.General.AdvertPlaceholder = "0"
# Only need to save if change was made.
$xml.Save($_)
}
}
答案 1 :(得分:2)
首先,我建议在PowerShell中执行 all ,这最终会简化问题:
处理2个非常不同的环境的复杂性,调用带有powershell -command
的PowerShell命令和包含要执行的命令的命令字符串引入了引用复杂性,这可能很难解决。
其次,您的问题是您的管道尝试同时执行 2个不同的事情:
您的尝试混淆了两个任务:它传入文件对象(在字符串上下文中解析为路径),然后错误地将它们视为内容,然后神奇地期望修改后的内容返回到文件对象(路径),告诉Set-Content
要写回哪些文件。
虽然 可以使用单个管道执行此操作,但它实际上是不可读的,因此我建议分别执行这两项任务:
注意:我知道您的Get-ChildItem
命令实际上只会产生 1 文件,但可能它会产生 multiple < / em>那些,所以我试着解决这个问题
此外,我避免使用cmd.exe
变量(%AppDate%
),而只使用PowerShell($env:AppData
)。
另外,请注意在下面的正则表达式中使用反向引用(\1
),这样就无需重复AdvertPlaceholder
,也无需使用{{ 1}}使用替换表达式来引用正则表达式中的第一个捕获组($1
)
如果我们现在只专注于PowerShell,我们会得到:
AdvertPlacholder
注意:
就字符编码而言,$files = Get-ChildItem $env:AppData\Skype -Recurse config.xml
foreach($file in $files) {
(Get-Content $file.fullname) -replace '<(AdvertPlaceholder)>1</\1>', '<$1>0</$1>' |
Set-Content $file.fullname
}
默认为系统的默认代码页。如有疑问,请使用显式编码,例如Set-Content
。
以上假设有问题的XML元素位于单行。如果不是,事情会变得更复杂:虽然您可以使用-Encode UTF8
(PowerShell v3 +)一次读取整个输入文件,但Get-Content -Raw
会添加 extra 输出的换行符;另外,您必须调整传递给上面Set-Content
的正则表达式以考虑换行空格。
-replace
类型(完全限定名称:[xml]
)使用正确的XML解析,显示了更多强大的处理值替换的方法。 如果您仍想从批处理文件中调用上述PowerShell命令,请按以下步骤操作:
请注意,需要使用单行文字传递给System.Xml.XmlDocument
,这会使整个事情变得不可读;此外,添加powershell.exe -command
通常是一个好主意,以便禁止加载配置文件,该文件仅用于交互式使用。
-noprofile