PowerShell 技能连载 - 理解顺序过滤

适用于 PowerShell 所有版本

当您在解析基于文本的日志文件时,或是需要过滤其它类型的信息时,往往需要使用 Where-Object 命令。以下是一些常见的场景,演示如何合并过滤器:

# logical AND filter for ALL keywords
Get-Content -Path C:\windows\WindowsUpdate.log |
  Where-Object { $_ -like '*successfully installed*' } |
  Where-Object { $_ -like '*framework*' } |
  Out-GridView

# above example can also be written in one line
# by using the -and operator
# the resulting code is NOT faster, though, just harder to read
Get-Content -Path C:\windows\WindowsUpdate.log |
  Where-Object { ($_ -like '*successfully installed*') -and ($_ -like '*framework*') } |
  Out-GridView

# logical -or (either condition is met) can only be applied in one line
Get-Content -Path C:\windows\WindowsUpdate.log |
  Where-Object { ($_ -like '*successfully installed*') -or ($_ -like '*framework*') } |
  Out-GridView

PowerShell 技能连载 - 过滤 Hotfix 信息

适用于 PowerShell 所有版本

Get-HotFix 是一个用于返回已安装的 hotfix 的 cmdlet。不过它没有可以过滤 hotfix 编号的参数。

通过一个 cmdlet 过滤器,您可以很方便地查看您关注的 hotfix。这个例子只返回编号为“KB25”开头的 hotfix:

Get-HotFix |
  Where-Object {
    $_.HotfixID -like 'KB25*'
  }

请注意 Get-HotFix 有一个 -ComputerName 参数,所以如果您拥有了合适的权限,那么您也可以从远程计算机中获取 hotfix 信息。

PowerShell 技能连载 - 获取关机信息

适用于 PowerShell 所有版本

Windows 在系统事件日志中记录了所有的关机事件。您可以从那儿提取和分析信息。

以下是一个读取适当的事件日志记录、从 ReplacementStrings 数组中读取相关的信息,并以对象数组的方式返回关机信息的函数。

function Get-ShutdownInfo
{

  Get-EventLog -LogName system -InstanceId 2147484722 -Source user32 |
  ForEach-Object {

    $result = 'dummy' | Select-Object -Property ComputerName, TimeWritten, User, Reason, Action, Executable

    $result.TimeWritten = $_.TimeWritten
    $result.User = $_.ReplacementStrings[6]
    $result.Reason = $_.ReplacementStrings[2]
    $result.Action = $_.ReplacementStrings[4]
    $result.Executable = Split-Path -Path $_.ReplacementStrings[0] -Leaf
    $result.ComputerName = $_.MachineName

    $result
  }
}

现在要检查关机问题就容易多了:

PS> Get-ShutdownInfo |  Out-GridView

CDN 资源

常用 CDN 列表

命令行

开放静态文件 CDN 提供的:

npm install -g sfile

详见 staticfile/cli

故障转移代码

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script type="text/javascript">!window.jQuery && document.write('<script type="text/javascript" src="/js/libs/jquery-2.0.3.min.js"><\/script>')</script>

引用多个script

<script type="text/javascript" src="http://www.google.com/jsapi"></script>

引用 jQuery 时:

google.load("jquery","1.3.2");

PowerShell 技能连载 - 复制命令行历史

适用于所有 PowerShell 版本

如果您操作了一阵子 PowerShell,然后突然意识到想保存刚才输入过的命令,那么请试试这行简单的代码:

(Get-History).CommandLine | clip

这段代码将您的所有命令行历史复制到剪贴板中。您可以把它粘贴到任何您喜欢的编辑器中,然后将命令保存到文件中。

如果您将命令粘贴到 PowerShell ISE 编辑器中,那么这些命令将会成为一段 PowerShell 脚本。

用 PowerShell 解析 eD2k 链接

电骡的 eD2k 链接包含了丰富的信息。例如这个:

ed2k://|file|BingPinyinSetup_1.5.24.02.exe|31485072|C8C9282E6112455E624EE82941E5BA00|p=79A822E1788353E0B289D2ADD5DA3BDE:FB9BB40DEDB1D2307E9D734A6416704B:0732B122C4ECF70065B181C92BF72400:437958DF590D764DE1694F91AC085225|h=HLXRQSANEO5MHIVOYNM5FNQOHJG3D5MP|s=http://blog.vichamp.com|s=http://www.baidu.com|/|sources,127.0.0.1:1234,192.168.1.1:8888|/

这给我们的第一感觉是可以用正则表达式来解析。我们观察一下它的规律,发现它是用 | 分割的字符串:

ed2k://
file
BingPinyinSetup_1.5.24.02.exe
31485072
C8C9282E6112455E624EE82941E5BA00
p=79A822E1788353E0B289D2ADD5DA3BDE:FB9BB40DEDB1D2307E9D734A6416704B:0732B122C4ECF70065B181C92BF72400:437958DF590D764DE1694F91AC085225
h=HLXRQSANEO5MHIVOYNM5FNQOHJG3D5MP
s=http://www.abc.com/def.zip
s=http://www.vichamp.com/qq.zip
/
sources,127.0.0.1:1234,192.168.1.1:8888
/

还有一些规律:

  • p= 开始,后面的段都是可选的。
  • p=xxxh=xxxs=xxx看起来像键值对。
  • s= 可以有多个,sources 后面的 IP 和端口可以有多对。

根据这个规律,我们可以很容易地构造出正则表达式,并用 PowerShell 解析它。

function Get-Ed2kLink {
    Param(
        [string]
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage = 'Enter an ed2k:// url')]
        $Link
    )

    $regex = [regex]@'
(?x)
\bed2k://
\|file\|(?<FILE_NAME>[^|]+)
\|(?<FILE_SIZE>\d+)
\|(?<FILE_HASH>[0-9a-fA-F]+)
(?:\|p=(?:(?<HASH_SET>[0-9a-fA-F]+):?)+)?
(?:\|h=(?<ROOT_HASH>[0-9a-zA-Z]+))?
(?:\|s=(?<HTTP_SOURCE>[^|]+))*
\|\/
\|sources(?:,(?<SOURCES_HOST>[0-9a-zA-Z.]+):(?<SOURCES_PORT>\d+))*
|\/\b
'@
    $match = $regex.Match($Link)
    if ($match.Success) {
        $sourcesHost = $match.Groups['SOURCES_HOST'].Captures | Select-Object -ExpandProperty Value
        $sourcesPort = $match.Groups['SOURCES_PORT'].Captures | Select-Object -ExpandProperty Value
        $sources = @()
        for ($i = 0; $i -lt $sourcesHost.Length; $i++) {
            $sources += [PSCustomObject][Ordered]@{
                Host = $sourcesHost[$i]
                Port = $sourcesPort[$i]
            }
        }

        $result = [PSCustomObject][Ordered]@{
            File = $match.Groups['FILE_NAME'].Value;
            FileSize = $match.Groups['FILE_SIZE'].Value;
            FileHash = $match.Groups['FILE_HASH'].Value;
            HashSet = $match.Groups['HASH_SET'].Captures | Select-Object -ExpandProperty Value
            RootHash = $match.Groups['ROOT_HASH'].Value;
            HttpSource = $match.Groups['HTTP_SOURCE'].Captures | Select-Object -ExpandProperty Value
            Sources = $sources;
        }
    } else {
        $result = $null
    }

    return $result
}

Get-Ed2kLink 'ed2k://|file|BingPinyinSetup_1.5.24.02.exe|31485072|C8C9282E6112455E624EE82941E5BA00|p=79A822E1788353E0B289D2ADD5DA3BDE:FB9BB40DEDB1D2307E9D734A6416704B:0732B122C4ECF70065B181C92BF72400:437958DF590D764DE1694F91AC085225|h=HLXRQSANEO5MHIVOYNM5FNQOHJG3D5MP|s=http://www.abc.com/def.zip|s=http://www.vichamp.com/qq.zip|/|sources,127.0.0.1:1234,192.168.1.1:8888|/'

执行结果如下:

File       : BingPinyinSetup_1.5.24.02.exe
FileSize   : 31485072
FileHash   : C8C9282E6112455E624EE82941E5BA00
HashSet    : {79A822E1788353E0B289D2ADD5DA3BDE, FB9BB40DEDB1D2307E9D734A6416704B, 0732B122C4ECF70065B181C92BF72400, 437958DF590D764DE1694F91AC085225}
RootHash   : HLXRQSANEO5MHIVOYNM5FNQOHJG3D5MP
HttpSource : {http://www.abc.com/def.zip, http://www.vichamp.com/qq.zip}
Sources    : {@{Host=127.0.0.1; Port=1234}, @{Host=192.168.1.1; Port=8888}}

注意一下,由于 s=sources 节包含循环体,所以不能直接用 PowerShell 的 -cmatch 表达式和 $Matches 变量,必须用 .NET 的 [regex] 类来处理。

参考材料:

您也可以在这里下载完整的源代码。

PowerShell 技能连载 - 用正则表达式搜索文件

适用于 PowerShell 所有版本

Get-ChildItem 不支持高级的文件过滤。当您使用简单的通配符时,无法利用上正则表达式。

要用上正则表达式,需要增加一个过滤用的 cmdlet 和 -match 操作符。

这个例子将在 Windows 目录中查找所有文件名包含至少 2 位数字,且文件名不超过 8 个字符的文件:

Get-ChildItem -Path $env:windir -Recurse -ErrorAction SilentlyContinue |
  Where-Object { $_.BaseName -match '\d{2}' -and $_.Name.Length -le 8 }

请注意 BaseName 属性的使用。它只返回主文件名(不包含扩展名)。通过这种方式,扩展名中的数字不会被包含在内。

PowerShell 技能连载 - 获取指定扩展名的文件

适用于 PowerShell 所有版本

当您使用 Get-ChildItem 来获取一个文件列表时,您可能会注意到 -Filter 参数有时候会导致返回比你预期的更多的文件。

以下是一个例子。这段代码并不只是返回“.ps1”扩展名的文件,而也会返回“.ps1xml”扩展名的文件:

Get-ChildItem -Path C:\windows -Recurse -ErrorAction SilentlyContinue -Filter *.ps1

要限制只返回您需要的扩展名的文件,请用一个 cmdlet 来过滤结果:

Get-ChildItem -Path C:\windows -Recurse -ErrorAction SilentlyContinue -Filter *.ps1 |
  Where-Object { $_.Extension -eq '.ps1' }

这将只返回您指定的扩展名的文件。

XAMPP 学习路线

XAMPP = Apache + MySQL + PHP + Perl

XAMPP是最流行的PHP开发环境

XAMPP是完全免费且易于安装的Apache发行版,其中包含MySQL、PHP和Perl。XAMPP开放源码包的设置让安装和使用出奇容易。

网站

XAMPP 官方网站
XAMPP - SourceForge

文件区别

  • 安装版(适合小型服务器安装)
    • xampp-win32-*-installer.exe - 有安装向导。
    • xampp-win32-*.zip - 解开是一个 xampp 目录,但可以随后注册服务等。
    • xampp-win32-*.7z - 和 .zip 版相同,压缩后体积更小。
  • 便携版(适合开发测试。不包含 FileZilla FTP 和 Mercury Mail Server,不能安装服务)
    • xampp-portable-win32-*-installer.exe
    • xampp-portable-win32-*.zip
    • xampp-portable-win32-*.7z

快速起步

**切勿自己摸索!**因为不同的版本的步骤有所不同。请阅读 xampp\readme_en.txt 中的 QUICK INSTALLATION 节。篇幅很短,不用担心 :)

潜在陷阱

  • 必须安装(或解压到)根目录下。例如 D:\xampp,或者 E:\xampp。
  • 注意缺省的 80 和 443 端口未被其它程序占用。netstat -ano |find "80"netstat -ano |find "443"
  • 如果用安装版的压缩包(.zip 或 .7z),并需要安装服务,请用提升权限的管理员账户打开 xampp-control.exe 进行安装。
  • 如果要能让别的机器或外网访问,请注意配置防火墙。
  • 通过 xampp-control.exe 启动,可能看不到完整的错误提示。请在命令行下启动 xampp_start.exe,可以看到更详细的错误提示。
  • 如果遇到错误,可以根据 xampp-control.exe 面板上的各个 Logs 按钮找到相应的日志。另外,可以通过 Windows 的事件查看部分日志。

用 XAMPP 搭建反向代理服务器

公网 IP 地址 + 80 端口是稀缺资源。在开发、测试阶段,我们常常需要在一个公网 IP 的 80 端口上,绑定多个 WEB 服务,这些服务可能部署在内网的多台异构服务器上(不同操作系统、不同服务器软件)。

用表格来表达就是:

外网访问 重定向到
http://home.test.com http://127.0.0.1:81
http://img.test.com http://127.0.0.1:82
http://js.test.com http://127.0.0.1:83

在 Linux 下,可以通过 vhost 程序来实现这个需求。在 Windows 下,我们有 XAMPP 和 IIS 两种选择。本文重点介绍 XAMPP 的实现方式。

分别搭建 3 个测试服务器

可以采用这些小工具快速创建测试服务器:

设置 hosts 以便测试

首先要让 3 个域名都指向本机。我们可以直接修改本地 hosts 文件以便测试。这种方式立刻生效,免去申请域名的麻烦。

用提升权限的记事本打开 %windir%\system32\drivers\etc\hosts 文件,加入这段:

127.0.0.1 home.test.com
127.0.0.1 img.test.com
127.0.0.1 js.test.com

这里有个快捷的方法,参见:PowerShell 技能连载 - 编辑“hosts”文件

搭建 XAMPP 环境

请参见 XAMPP 学习路线。只需要其中的 Apache 模块即可。确保 XAMPP 能够正常启动,并能够通过 http://127.0.0.1 访问缺省页面。

设置 XAMPP

编辑 xampp\apache\conf\httpd.conf,将 LoadModule proxy_http_module modules/mod_proxy_http.so 前的 # 号去掉。

编辑 xampp\apache\conf\extra\httpd-vhosts.conf,在尾部添加:

ProxyRequests Off

<Proxy *>
    Order deny,allow
    Allow from all
</Proxy>

<VirtualHost *:80>
    ServerName blog.test.com
    ProxyPass / http://127.0.0.1:81/
    ProxyPassReverse / http://127.0.0.1:81/
</VirtualHost>

<VirtualHost *:80>
    ServerName img.test.com
    ProxyPass / http://127.0.0.1:82/
    ProxyPassReverse / http://127.0.0.1:82/
</VirtualHost>

<VirtualHost *:80>
    ServerName js.test.com
    ProxyPass / http://127.0.0.1:83/
    ProxyPassReverse / http://127.0.0.1:83/
</VirtualHost>

重启 XAMPP 中的 Apache 组件

姊妹篇 - 用 IIS 搭建反向代理服务器

用 IIS 也可以实现相同的功能。

注意有个坑:

%windir%\System32\inetsrv\iis.msc 或通过“这台电脑 - 右键 - 计算机管理” 启动 IIS 管理器,可能看不到 ARR 组件而通过 %windir%\system32\inetsrv\InetMgr.exe 则可以看到。

鸣谢

PowerShell 技术 QQ 群