字幕整理脚本

从看过的电影、美剧里学英语是一件很棒的事。因为你曾经被带入过那个场景,曾经和主角一同喜怒哀乐。如果能将电影里的中英文对白整理出来,对做笔记和搜索回顾将大有帮助。

我们可以从网上(例如射手网)下载视频的中英文字幕,需要是 .srt 格式的。它实际上是一个文本文件,内容类似如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
13
00:04:42,050 --> 00:04:45,010
{\an2}{\pos(212,240)}第三季 第一集

14
00:01:56,000 --> 00:01:56,990

Hey, Pop.

15
00:01:56,880 --> 00:02:04,020
{\an8}凯文·安德伍德

16
00:01:59,750 --> 00:02:01,510
好久不见啊
Been a while, hasn't it?

我们希望将它整理成这样的格式:

1
2
3
4
5
6
7
8
9
第三季  第一集


Hey, Pop.

凯文·安德伍德

好久不见啊
Been a while, hasn't it?

这个任务可以用 PowerShell + 正则表达式轻松搞定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (!(Test-Path dst)) {
md dst | Out-Null
}

Get-ChildItem src\*.srt | ForEach-Object {
$srcFile = $_
Write-Output "Processing $($srcFile.Name)"
$dstFile = (Join-Path 'dst' $srcFile.BaseName) + '.txt'
Get-Content $srcFile | ForEach-Object {
$line = $_
if ($line -cmatch '\A\d+\z') { return }
if ($line -cmatch '\d\d:\d\d:\d\d,\d\d\d --> \d\d:\d\d:\d\d,\d\d\d') { return }
$line = $line -creplace '\s*\{\\.*?\}\s*', ''
return $line
} | Out-File $dstFile
}

只需要将字幕源文件放在_src_目录下,运行脚本,就可以在_dst_目录下得到期望的文本文件。执行效果如下:

文件目录如下:

您也可以在这里下载完整的脚本。

如何用 PowerShell 撰写心灵鸡汤

关于励志段子

微信上传着一个励志段子,大意是:

如果26个英文字母 A B C D EF G H I J K L M N O P Q R S T U V W X Y Z 分别等于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 17 18 19 20 21 22 23 24 25 26。那么:

  • Knowledge (知识): K+N+O+W+L+E+D+G+E= 11+14+15+23+12+5+4+7+5=96%
  • Workhard (努力工作):W+O+R+K+H+A+R+D= 23+15+18+11+8+1+18+4 =98%
  • Luck(好运) L+U+C+K=12+21+3+11=47%
  • Love(爱情) L+O+V+E=12+15+22+5=54%
  • Money(金钱) M+O+N+E+Y=13+15+14+5+25=72%
  • Leadership(领导能力)L+E+A+D+E+R+S+H+I+P=12+5+1+4+5+18+19+9+16=89%
  • ATTITUDE(心态)A+T+T+I+T+U+D+E=1+20+20+9+20+21+4+5=100%

于是得出结论:用什么样的态度去看待人生,就会得到什么样的人生。

分析

这样的心灵鸡汤是怎样来的呢?我们用 PowerShell 脚本来琢磨一下。

1
2
3
4
5
6
7
8
function Get-Weight([string]$word) {
$word = $word.ToLower()
#Write-Host ([System.Text.Encoding]::ASCII.GetBytes($word) |
# ForEach-Object { $_ - 96 })
return ([System.Text.Encoding]::ASCII.GetBytes($word) |
ForEach-Object { $_ - 96 } |
Measure-Object -Sum).Sum
}

这个函数可以对任意字符串求值,例如以下测试代码将返回 6(abc = 1+2+3):

1
Get-Weight 'abc'

现在可以测试一下段子里用到的几个单词,并对结果进行排序:

1
2
'Knowledge', 'Workhard', 'Luck', 'Love', 'Money', 'Leadership', 'ATTITUDE' |
Sort-Object -Property @{Expression = { Get-Weight $_ }}

结果符合预期:

Luck
Love
Money
Knowledge
Leadership
Workhard
ATTITUDE

如何撰写鸡汤

以上实现了输入任意字符串数组,对它们进行求值和排序。但是如何选出这些单词呢?我们可以找一篇长文,例如从麻省理工找到莎士比亚的《哈姆雷特》全文,将它输进去拆解成单词试试:

1
2
3
4
5
$resp = Invoke-WebRequest 'http://shakespeare.mit.edu/hamlet/full.html'
$fullText = $resp.ParsedHtml.documentElement.innerText
$words = [regex]::Matches($fullText, '\b\w+\b') |
ForEach-Object { $_.Value } |
Sort-Object -Unique

这样几行代码,就可以将《哈姆雷特》全文的所有单词挑出来进行排序,并将结果保存在 $words 变量中。

最后套用我们上面写好的函数即可实现对所有单词求值排序:

1
2
3
4
5
$words |
Sort-Object -Property @{Expression = { Get-Weight $_ }} |
ForEach-Object {
"$_`t$(Get-Weight $_)"
}

结果大概是这样:

word weight
a 1
c 3
d 4
e 5
bad 7
be 7
I 9
letters 99
firmament 99
temperance 100
Writing 100
prosperously 199
unproportioned 200

有了这个长长的表格之后,撰写鸡汤就容易多了。只要按顺序挑出一些单词,设计一下台词即可。

完整的代码如下:

function Get-Weight([string]$word) {
    $word = $word.ToLower()
    #Write-Host ([System.Text.Encoding]::ASCII.GetBytes($word) |
    #    ForEach-Object { $_ - 96 })
    return ([System.Text.Encoding]::ASCII.GetBytes($word) |
        ForEach-Object { $_ - 96 } |
        Measure-Object -Sum).Sum
}

# Test
# Get-Weight 'abc'

if (!$resp) {
    $resp = Invoke-WebRequest 'http://shakespeare.mit.edu/hamlet/full.html'
}

$fullText = $resp.ParsedHtml.documentElement.innerText
$words = [regex]::Matches($fullText, '\b\w+\b') |
    ForEach-Object { $_.Value } |
    Sort-Object -Unique

# The following code will procuce output:
# Luck
# Love
# Money
# Knowledge
# Leadership
# Workhard
# ATTITUDE
'Knowledge', 'Workhard', 'Luck', 'Love', 'Money', 'Leadership', 'ATTITUDE' |
    Sort-Object -Property @{Expression = { Get-Weight $_ }}

$words |
    Sort-Object -Property @{Expression = { Get-Weight $_ }} |
    ForEach-Object {
        "$_`t$(Get-Weight $_)"
    }

后记

完整的代码可以在这里下载。鸡汤的原文请参见《是哪位高人琢磨出的这条微信,太牛了》。顺便发现了原文中的一个计算 bug——Leadership(领导能力)应是L+E+A+D+E+R+S+H+I+P=12+5+1+4+5+18+19+8+9+16=97%,而不是 89%。

怎么样,有没有一点理工男秒杀心灵鸡汤的味道?

用 PowerShell 显示 黑客帝国数码雨动画

请在 PowerShell 控制台中执行本脚本

今天在群里看到一个数码雨的课题,试着实现了一下:

【话痨】powershell传教士(1328486072) 12:58:11
话说有人用bat写出了数码雨,谁也用powershell写一个,我用powershell写了几个,总感觉不对。
【话痨】powershell传教士(1328486072) 12:58:52
有人对命令行数码雨,感兴趣么?

根据传教士的提示,改了一下,避免了闪烁。

实现效果

Matrix

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
## Prepare the screen
$host.UI.RawUI.BackgroundColor = "Black"
$host.UI.RawUI.ForegroundColor = "Green"

$charSet = '0123456789'.ToCharArray()

$width = 75
$height = [Console]::WindowHeight
$maxStringLength = 7
$minStringLength = 2
$maxSpaceLength = 20
$minSpaceLength = 6

$lines = New-Object System.Collections.ArrayList
$symbols = @()

for ($i = 0; $i -lt $width; $i++) {
$symbols += ''
}

function AddLine([string]$line) {
$lines.insert(0, $line)
if ($lines.Count -eq $height) {
$lines.RemoveAt($lines.Count - 1)
}
}

function ShowFrame() {
Write-Host ($lines.ToArray() -join "`n")
}

function TryGenerateSymbol() {
for ($i = 0; $i -lt $width; $i++) {
$column = $symbols[$i]
if ($column -eq '') {
# initial state, generate spaces
$symbols[$i] = New-Object String ' ', (Get-Random -Minimum $minSpaceLength -Maximum $maxSpaceLength)
} elseif ($column -eq ' ') {
# last space
$randomCount = Get-Random -Minimum $minStringLength -Maximum $maxStringLength
$chars = Get-Random -InputObject $charSet -Count $randomCount
$symbols[$i] = $column + ($chars -join '')
} elseif ($column.Length -eq 1) {
# last char
$symbols[$i] = $column + (New-Object String ' ', (Get-Random -Minimum $minSpaceLength -Maximum $maxSpaceLength))
}
}
}

function UpdateFrame() {
TryGenerateSymbol

$line = @()
for ($i = 0; $i -lt $width; $i++) {
$column = $symbols[$i]
$line += $column[0]
$symbols[$i] = $column.Substring(1, $column.Length - 1)
}
$line = $line -join ''
AddLine $line
}

try
{
$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size $width + 1, $height + 1
}
catch {}

try
{
$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size $width + 1, $height + 1
} catch {}

try
{
while($true)
{
if([Console]::KeyAvailable)
{
$key = [Console]::ReadKey()
if(($key.Key -eq 'Escape') -or
($key.Key -eq 'Q') -or
($key.Key -eq 'C'))
{
break
}
}

# Clear-Host

$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0,0

UpdateFrame
ShowFrame

$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates `
0,([Console]::WindowHeight - 1)
Write-Host -NoNewLine 'Q or ESC to Quit'

Start-Sleep -m 100
}
}
finally
{
## Clean up, display exit screen
Clear-Host
"`n"
" Happy Scripting from PowerShell..."
" by Victor.Woo!"
"`n`n`n"
}

您也可以在这里下载 Matrix.ps1

在 PowerShell 中利用正则表达式来解析文本块

需求

给定一段文本,如:

1, abcd [xxxx]
vkjl gas kje asld
gew wef
2, bbb [wefs]
oioias wmfjalkjs
3, ccc [wegas]
kzxlj kjlwiewe ii

要求分割成多段以数字开头的文本块,如:

第一块:

1, abcd [xxxx]
vkjl gas kje asld
gew wef

第二块:

2, bbb [wefs]
oioias wmfjalkjs

第三块:

3, ccc [wegas]
kzxlj kjlwiewe ii

思路

  • 定义我们要东西为 n 个“block”。
  • 每个“block”的特征是:
    • 以数字开头
    • block 之前可能是整段文本的起始也有可能是一个回车符。
    • block 之后可能是一个回车符+下一行的数字也有可能是整段文本的结束。
  • block 之前和之后的回车符是不需要的
  • block 应该尽可能“非贪婪”,遇到下一个符合条件的,算作一个新的 block 开始。

其中,“block 之前和之后的回车符是不需要的”可以用正则表达式的“零宽断言”来解决。

代码

$subject = @'
1, abcd [xxxx]
vkjl gas kje asld
gew wef
2, bbb [wefs]
oioias wmfjalkjs
3, ccc [wegas]
kzxlj kjlwiewe ii
'@

$resultlist = new-object System.Collections.Specialized.StringCollection
$regex = [regex]@'
(?snx)(^|(?<=\n))
(?<block>\d, .*?)
((?=\n\d, )|$)
'@
$match = $regex.Match($subject)
while ($match.Success) {
    $resultlist.Add($match.Groups['block'].Value) | out-null
    $match = $match.NextMatch()
}

$resultlist | ForEach-Object {
    echo $_
    echo ---
}

输出结果

1, abcd [xxxx]
vkjl gas kje asld
gew wef
---
2, bbb [wefs]
oioias wmfjalkjs
---
3, ccc [wegas]
kzxlj kjlwiewe ii
---

用 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] 类来处理。

参考材料:

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

markdown 学习路线

Markdown 是一种轻量级标记语言,创始人为约翰·格鲁伯(John Gruber)。它允许人们“使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档”。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。

markdown

介绍

规范

Markdown 的基本语法较为简单,所以多家衍生出不同的扩展版本。其中由于 GitHub 网站的流行,导致 Markdown 的 GitHub 扩展版本(简称 GFM)较为流行。

以下是基本语法和各个扩展版本的语法文档原始链接:

编辑器

基于 .NET 开发,只用于 Windows,功能较齐全。GFM 风格离线编辑要收费。

MarkdownPad Screenshot

文化

用 PowerShell 批量分割 QQ 聊天记录

纯文本文件有诸多的好处:

  • 通用
  • 易于管理
  • 易于搜索
  • 易于迁移

接下来我们用 PoewrShell 来处理 QQ 的聊天记录。目的是将所有的聊天记录按照“组名/对象名.txt”来分别保存每个好友、每个 QQ 群等的聊天记录。

我现在用的是 QQ 6.1 (11905) 版本。依次打开 QQ / 工具 / 消息管理器,点击右上角的倒三角按钮可以看到“导出全部消息记录”菜单项。我们在接下来的对话框里的保存类型中选择“文本文件(*.txt,不支持导入)”,并用默认的“全部消息记录.txt”文件名保存。保存之后的文件内容大概是如下格式:

消息记录(此消息记录为文本格式,不支持重新导入)

================================================================
消息分组:我的好友
================================================================
消息对象:Victor.Woo
================================================================

2010-01-06 16:57:28 Victor.Woo
http://pic4.nipic.com/20090728/1684061_175750076_2.jpg

2010-05-27 12:29:35 Victor.Woo
6块钱包月55
8000/月
中心端,用户端

================================================================
消息分组:技术.关注
================================================================
消息对象:*PowerShell技术交流
================================================================

2013-06-23 15:52:32 此消彼长,云过有痕<qq_g@163.com>
http://yun.baidu.com/buy/center?tag=4#FAQ02

百度亮了,自己找亮点

2013-06-23 18:42:35 Victor.Woo<victorwoo@gmail.com>
[表情]

观察它的规律:

  • ================================================================ 作为每一段的元数据开始。
  • 接下来依次是消息分组、分隔符、消息对象。
  • ================================================================ 作为元数据的结束。
  • 元数据之后,是正文部分,直到下一个元数据开始。
  • 文件头部还有两行无关内容。
  • 文件尺寸巨大,不适合整体用正则表达式来提取,只能一行一行解析。

我们的目标是生成 我的好友/Victor.Woo.txt技术.关注/.PowerShell技术交流.txt

根据这个规律,我们可以用类似“状态机”的思想来设计 PowerShell 脚本。在遍历源文件的所有行时,用一个 $status 变量来表示当前的状态,各个状态的含义如下:

状态 含义
INIT 初始状态
ENTER_BLOCK 进入一个元数据块
ENTER_GROUP “消息分组”解析完成
ENTER_SPLITTER 元数据中间的分隔符解析完成
ENTER_TARGET “消息对象”解析完成
LEAVE_BLOCK 元数据块解析完成
CONTENT 当前行是正文内容

然后用一个 switch 语句让 $status 变量在这些状态之间来回跳转,就能解析出一个一个独立的消息文件了。完整代码如下:

function Get-Status($status, $textLine, $lineNumber, $block) {
    $splitter = '================================================================'
    switch ($status) {
        'INIT' {
            if ($textLine -eq $splitter) {
                $status = 'ENTER_BLOCK'
            }
        }
        'ENTER_BLOCK' {
            if ($textLine -cmatch '消息分组:(.*)') {
              $block.Group = $matches[1]
                $block.Target = $null
                $status = 'ENTER_GROUP'
                break
            } else {
              Write-Error "[$lineNumber] [$status] $textLine"
                exit
            }
        }
        'ENTER_GROUP' {
            if ($textLine -eq $splitter) {
                $status = 'ENTER_SPLITTER'
                break
            } else {
                Write-Error "[$lineNumber] [$status] $textLine"
                exit
            }
        }
        'ENTER_SPLITTER' {
            if ($textLine -cmatch '消息对象:(.*)') {
              $block.Target = $matches[1]
                $status = 'ENTER_TARGET'
                break
            } else {
              Write-Error "[$lineNumber] [$status] $textLine"
                exit
            }
        }
        'ENTER_TARGET' {
            if ($textLine -eq $splitter) {
                $status = 'LEAVE_BLOCK'
                break
            } else {
                Write-Error "[$lineNumber] [$status] $textLine"
                exit
            }
        }
        'LEAVE_BLOCK' {
            if ($textLine -eq $splitter) {
                $status = 'ENTER_BLOCK'
                break
            } else {
                $status = 'CONTENT'
            }
        }
        'CONTENT' {
            if ($textLine -eq $splitter) {
                $status = 'ENTER_BLOCK'
                break
            } else {
                $status = 'CONTENT'
            }
        }
    }

    return $status
}

$status = 'INIT'
$lineNumber = 0
$block = @{}
$targetPath = $null
cat 全部消息记录.txt -Encoding UTF8 | foreach {
    $textLine = $_
    $lineNumber++
    $status = Get-Status $status $textLine $lineNumber $block
    switch ($status) {
        'LEAVE_BLOCK' {
            if ($block.Target -eq '最近联系人') {
                break
            }
            $dirName = $block.Group.Replace('*', '.')
            if (!(Test-Path $dirName)) {
                md $dirName | Out-Null
            }

            $fileName = $block.Target.Replace('*', '.')

            $targetPath = (Join-Path $dirName $fileName) + '.txt'
            if (Test-Path $targetPath) {
                del $targetPath
            }

            echo $targetPath
        }
        'CONTENT' {
            #echo $textLine
            if ($block.Target -eq '最近联系人') {
                break
            }
            Out-File -InputObject $textLine -Encoding utf8 -LiteralPath $targetPath -Append
        }
    }
}

您也可以在这里下载完成后的版本。

用 PowerShell 输出中文到剪贴板

方法一 通过 clip.exe

用 PowerShell 将字符串输出到剪贴板的最简单方式是:

'abc' | clip.exe

不过直接这么使用的话,如果待输出的字符串是包含中文的,那么剪贴板里的内容会出现“乱码”:

'abc中文def' | clip.exe

剪贴板里的内容变成:

abc??def

这是因为为了兼容旧程序,管道操作缺省将字符串采用 ASCII 编码,因此对于中文字符,被转换成了“??”。解决方案如下:

$OutputEncoding = [Console]::OutputEncoding
'abc中文def' | clip.exe

方法二 通过 WPF 方法

-sta 参数启动 PowerShell 后,执行以下代码:

Add-Type -Assembly PresentationCore
[Windows.Clipboard]::SetText('abc中文def')

PowerShell 2.0 的控制台,缺省设置是 MTAPowerShell 3.0 的控制台,缺省设置是 STA。

关于 -sta 的知识,请参见PowerShell中的 STA和MTA

参考材料:

快速生成树形结构的纯文本

今天帮朋友整理一些材料,需要为这些材料整理一个目录。之前有研究过一些方案,例如:

这些方案有一个共性:麻烦。也就是无法像手头的工具一样拿来就用。于是发掘了一番,发现 tree 这个 dos 时代的命令刚好能满足需要。该命令的帮助如下:

以图形显示驱动器或路径的文件夹结构。

TREE [drive:][path] [/F] [/A]

   /F   显示每个文件夹中文件的名称。
   /A   使用 ASCII 字符,而不使用扩展字符。

我们可以用以下命令将 D:\work 下的结构输出到 output.txt 文本文件:

TREE "D:\work" /F /A > output.txt

然后用记事本之类的文本编辑器对它进行简单的编辑,就可以达到目的。

还可以拓展一下思路:在撰写文章的时候,常常需要描述一个有层次的结构(可以是心得体会之类的,不仅限于描述一系列文件)。此时可以在硬盘里创建一个临时目录,在里面创建一些文件夹和文件,用资源管理器拖拽调整目录结构,然后用上述命令导出一个目录文件,就可以快速地用于文档的撰写了。请不要徒手编辑这样的文本,因为那样很愚蠢,调整起来也相当费功夫。

命令执行效果参考:

卷 os 的文件夹 PATH 列表
卷序列号为 0000002C 000E:BD6F
C:.
|   HaxLogs.log
|   setmockup.log
|   WEVTUTIL.exe
|
+---adt-bundle-windows-x86
|   |   SDK Manager.exe
|   |
|   +---android-ndk-r9
|   |   |   documentation.html
|   |   |   GNUmakefile
...
|   |   |   README.TXT
|   |   |   RELEASE.TXT
|   |   |
|   |   +---build
|   |   |   +---awk
|   |   |   |       check-awk.awk
|   |   |   |       extract-debuggable.awk