PowerShell入門

コマンド&スクリプト

LinuxやUnixのコマンドが使える人って、結構PowerShellってとっつきにくくないですか?
コマンドレットっていう聴き慣れない用語とか、オブジェクトを処理するとか。
特にテキスト整形だけやりたい人は、回りくどい説明を理解しなきゃいけないのが苦痛だったりします。
LinuxやUnixと同じようなことがどうすればできるのかすぐ知りたい。そんな思いでまとめています。初心者の人にも読めるようにまだまだ見直し中ですが、本記事が、何かの足しになったら幸いです。

Contents

スポンサーリンク

PowerShell 入門編

PowerShell を始めるには

コマンドプロンプトのようなターミナルウィンドウは、powershellコマンドを実行すれば起動できます。

コマンドプロンプトのように真っ黒ではなく青いウィンドウです。

簡単なコマンドを実行してみます。

PS C:\Users\takk> write-output HELLO
HELLO
PS C:\Users\takk>

echoを実行しても同じ出力ができます。

PS C:\Users\takk> echo HELLO
HELLO
PS C:\Users\takk>

PowerShell エイリアス

さきほどの結果が示す通り、write-outputもechoも同じことができるコマンドです。
write-outputのエイリアスがechoです。
echoで良いのになんでwrite-outputなんて長ったらしい名前なのか。逆にこれがPowerShellの良いところでもあります。コマンド名に一貫性があるんです。かつ馴染みのあるコマンド名はエイリアスになっているので、LinuxやUnixのコマンドに慣れた人なら本当はものすごく導入しやすいんです。

エイリアスの設定状況を見てみましょう。get-aliasを使います。

PS C:\Users\takk> get-alias

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin
Alias           cat -> Get-Content
Alias           cd -> Set-Location
Alias           chdir -> Set-Location
Alias           clc -> Clear-Content
Alias           clear -> Clear-Host
Alias           clhy -> Clear-History
Alias           cli -> Clear-Item
Alias           clp -> Clear-ItemProperty
Alias           cls -> Clear-Host
(省略)
PS C:\Users\takk>

エイリアス名を指定して確認するには、引数にエイリアスを指定します。

PS C:\Users\takk> get-alias echo

CommandType     Name                                      ModuleName
-----------     ----                                      ----------
Alias           echo -> Write-Output


PS C:\Users\takk>

echoが、write-outputのエイリアスであることが確認できました。

先ほども述べたとおり、定義されているエイリアスは、Unix/Linuxのコマンドを使ってる人なら、とても馴染みのある名称となっています。例えばGet-ChildItem。エイリアスを知らずに初めてこのコマンドレット名を目の当たりにすると、臆してしまいますが、このコマンドレットのエイリアスはlsです。lsと聞けば安心できますね。

PowerShell 変数、関数

変数名と変数への代入

変数を使います。
PowerShellの変数はPerlのように変数名は$から始まります。
=で代入できます。

PS C:\Users\takk> $a=1
PS C:\Users\takk> echo $a
1
PS C:\Users\takk>

16進数

0xをつけると16進数を表現できます。

PS C:\Users\takk> $a=0x20
PS C:\Users\takk> echo $a
32
PS C:\Users\takk>

指数

指数表記。

PS C:\Users\takk> $a=1.23e3
PS C:\Users\takk> echo $a
1230
PS C:\Users\takk>

配列

配列は、,(カンマ)で区切るだけで使えます。

PS C:\Users\takk> $a=10,20,30
PS C:\Users\takk> echo $a
10
20
30
PS C:\Users\takk> echo $a[1]
20
PS C:\Users\takk>

文字列

文字列は、シングルまたはダブルクォートで囲みます.’文字列’ ”文字列”

PS C:\Users\takk> $a='HELLO'
PS C:\Users\takk> echo $a
HELLO
PS C:\Users\takk> $a="HELLO"
PS C:\Users\takk> echo $a
HELLO
PS C:\Users\takk>

変数名だけ書くと、内容が表示されます。Lengthは文字列長を取得できます。

PS C:\Users\takk> $a='HELLO'
PS C:\Users\takk> $a
HELLO
PS C:\Users\takk> $a.Length
5
PS C:\Users\takk>

Countは1が返ります。

PS C:\Users\takk> $a.Count
1
PS C:\Users\takk>

関数

関数はfunctionで定義します。変数$aと関数aは区別されます。

PS C:\Users\takk> function a(){
>> echo "Hello World"
>> }
>>
PS C:\Users\takk> a
Hello World
PS C:\Users\takk>

PowerShell 制御文

分岐 if文

if文を使ってみます。$aが1のときは、ONEと表示。
それ以外のときは、NOT ONEと表示する関数aを作ります。

PS C:\Users\takk> function a(){
>> if ($a -eq 1){
>> echo "ONE"
>> }else{
>> echo "NOT ONE"
>> }
>> }
>>
PS C:\Users\takk> $a=2
PS C:\Users\takk> a
NOT ONE
PS C:\Users\takk> $a=1
PS C:\Users\takk> a
ONE
PS C:\Users\takk>

分岐判定に使う比較演算子

PowerShellの比較演算子はtestコマンドと合わせてあるので覚えやすいです。

-eq Equal to
-ne Not Equal to
-gt Greater Than
-ge Greater than or Equal to
-lt Less Than
-le Less than or Equal to

使ってみます。

PS C:\Users\takk> 3 -eq 5
False
PS C:\Users\takk> 3 -ne 5
True
PS C:\Users\takk> 3 -gt 5
False
PS C:\Users\takk> 3 -ge 5
False
PS C:\Users\takk> 3 -lt 5
True
PS C:\Users\takk> 3 -le 5
True
PS C:\Users\takk>

真偽が逆にあるように値を変えます。

PS C:\Users\takk> 3 -eq 3
True
PS C:\Users\takk> 3 -ne 3
False
PS C:\Users\takk> 3 -gt 3
False
PS C:\Users\takk> 3 -ge 3
True
PS C:\Users\takk> 3 -lt 3
False
PS C:\Users\takk> 3 -le 3
True
PS C:\Users\takk>

文字列にも使えます。eqにiが付くと大文字小文字無視します。
eqにcがつくと大文字小文字を区別します。

PS C:\Users\takk> "HELLO" -eq "hello"
True
PS C:\Users\takk> "HELLO" -ceq "hello"
False
PS C:\Users\takk> "HELLO" -ieq "hello"
True
PS C:\Users\takk> "HELLO" -ceq "HELLO"
True
PS C:\Users\takk>

真偽逆のパターン。

PS C:\Users\takk> "HELLO" -ne "hello"
False
PS C:\Users\takk> "HELLO" -cne "hello"
True
PS C:\Users\takk> "HELLO" -ine "hello"
False
PS C:\Users\takk> "HELLO" -cne "HELLO"
False
PS C:\Users\takk>

繰り返し for foreach

forとforeachを使います。

forの文法は、

for(初期化式;条件式;加減式){
 処理
}

です。使ってみましょう。

PS C:\Users\takk> for($i=0;$i -lt 3; $i++){echo $i}
0
1
2
PS C:\Users\takk>

コマンドラインとして <や>と混同しないためでしょうか。
<を-ltと書く必要があります。

次は、foreachです。文法は、

foreach(変数 in 配列){
 処理
}

です。

PS C:\Users\takk> $arr = 1,2,3
PS C:\Users\takk> foreach($a in $arr){
>> echo $a
>> }
>>
1
2
3
PS C:\Users\takk>

inに指定する配列の代わりに直接値を記述できます。

PS C:\Users\takk> foreach($a in 1,2,3){echo $a}
1
2
3
PS C:\Users\takk>

配列は、..で数列にすることもできますので、このようにも書けます。

PS C:\Users\takk> foreach($a in 1..3){echo $a}
1
2
3
PS C:\Users\takk>

他にもdoとかwhileとかの繰り返し構文あります。

では、 0~255までを16進で1行16桁で表示してみましょう。

PS C:\Users\takk> foreach($i in 0..255){
>> $s += "{0:X2} " -f $i
>> if($i % 16 -eq 15){$s += "`r`n"}
>> }
>>
PS C:\Users\takk> echo $s
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF

PS C:\Users\takk>

できました。
echoすると毎回改行されてしまいますので、大きな文字列を作成した後、最後に一回でechoしています。

制御ブロック begin process end

PerlやAwkと同じようにブロックが指定できます。
起動時1回だけ実行するbeginブロック。標準入力の各行を処理するprocessブロック。最後に1回だけ実行するendブロックです。
使ってみましょう。

PS C:\Users\takk> function tohex(){
>> begin{$s=""}
>> process{$s += "{0:X2} " -f $_}
>> end{echo $s}
>> }
>>
PS C:\Users\takk> 10,20,30,40,50 | tohex
0A 14 1E 28 32
PS C:\Users\takk>

次は、lsの結果を渡してみましょう。

PS C:\Users\takk\aaa> ls -n
dir_11CFDF63
dir_2065207
dir_2EA62317
dir_3A7F5143
dir_46585CA
dir_59E634FB
dir_5D8B7032
dir_60682448
dir_6604792F
dir_6A254AAD
dir_7E2D24B5
PS C:\Users\takk\aaa>

18桁の右詰表示に加工する関数です。

PS C:\Users\takk\aaa> function print20(){
>> process{"|{0,18}|" -f $_}
>> }
>>
PS C:\Users\takk\aaa> ls -n | print20
|      dir_11CFDF63|
|       dir_2065207|
|      dir_2EA62317|
|      dir_3A7F5143|
|       dir_46585CA|
|      dir_59E634FB|
|      dir_5D8B7032|
|      dir_60682448|
|      dir_6604792F|
|      dir_6A254AAD|
|      dir_7E2D24B5|
PS C:\Users\takk\aaa>

foreach (ForEach-Object)

foreachは、ForEach-Objectのエイリアスです。
さらに詳しく見ていきましょう。

PS C:\Users\takk> get-alias foreach

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           foreach -> ForEach-Object


PS C:\Users\takk>

こんな風に使ってました。

PS C:\Users\takk> foreach($i in 1,2,3){echo $i}
1
2
3
PS C:\Users\takk>

1,2,3はパイプで渡すこともできます。その際、各行の文字列は、$_に格納されています。

PS C:\Users\takk> 1,2,3 | foreach{echo $_}
1
2
3
PS C:\Users\takk>

ForEach-Objectはforeachの他にも%がエイリアスされています。

PS C:\Users\takk> 1,2,3 | %{echo $_}
1
2
3
PS C:\Users\takk>

-Begin -Process -Endブロックが指定できます。

PS C:\Users\takk> 1,2,3 | % -Begin{echo "BEGIN"} -Process{echo $_} -End{echo "END"}
BEGIN
1
2
3
END
PS C:\Users\takk>

そして -Begin -Process -Endは{}を残して省略もできます。

PS C:\Users\takk> 1,2,3 | % {echo "BEGIN"} {echo $_} {echo "END"}
BEGIN
1
2
3
END
PS C:\Users\takk> 1,2,3 | %{echo "BEGIN"}{echo $_}{echo "END"}
BEGIN
1
2
3
END
PS C:\Users\takk>

16進16桁表示はこのように書くこともできます。

PS C:\Users\takk> 0..255 | %{$s=""}{$s += "{0:X2} " -f $_;if($_%16 -eq 15){$s+="`n"}}{echo $s}
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF

PS C:\Users\takk>

文字列操作 置換、分割、位置

文字列置換。

PS C:\Users\takk> $a="HELLO"
PS C:\Users\takk> $a-replace("L","E")
HEEEO
PS C:\Users\takk>

文字列分割。

PS C:\Users\takk> $a="192.168.1.10"
PS C:\Users\takk> $a.Split(".")[1]
168
PS C:\Users\takk>
PS C:\Users\takk> "aa bb cc dd"-split " "
aa
bb
cc
dd
PS C:\Users\takk>
PS C:\Users\takk> $a="192.168.1.10"
PS C:\Users\takk> ($a-Split("\."))[1]
168
PS C:\Users\takk>

文字列位置。最初。

PS C:\Users\takk> $a="HELLO"
PS C:\Users\takk> $a.IndexOf("L")
2
PS C:\Users\takk>

文字列位置。最後。

PS C:\Users\takk> $a="HELLO"
PS C:\Users\takk> $a.LastIndexOf("L")
3
PS C:\Users\takk>

配列操作

PowerShellで分かっていそうで分かっていない配列。おさらいしてみます。
まず連番の生成から。1~5の整数を作ります。

PS C:\Users\takk> $arr=1..5
PS C:\Users\takk> $arr
1
2
3
4
5
PS C:\Users\takk>

負の数も作れます。

PS C:\Users\takk> $arr=-3..2
PS C:\Users\takk> $arr
-3
-2
-1
0
1
2
PS C:\Users\takk>

配列数を取得するには、Countです。

PS C:\Users\takk> $arr.Count
6
PS C:\Users\takk>

カンマで区切って配列を作成。

PS C:\Users\takk> $arr=10,-20,30,-40,50,-60
PS C:\Users\takk> $arr
10
-20
30
-40
50
-60
PS C:\Users\takk>

比較演算子でフィルタリングもできます。

PS C:\Users\takk> $arr -gt 0
10
30
50
PS C:\Users\takk> $arr -lt 0
-20
-40
-60
PS C:\Users\takk>

変数の値自体を変えるには、変数へ再代入するだけです。

PS C:\Users\takk> $arr2 = $arr -gt 0
PS C:\Users\takk> $arr2 = $arr2 -gt 10
PS C:\Users\takk> $arr2
30
50
PS C:\Users\takk>

一致しない値を抽出。

PS C:\Users\takk> $arr -ne 30
10
-20
-40
50
-60
PS C:\Users\takk>

条件を増やして抽出。

PS C:\Users\takk> $arr -ne 30 -ne 50 -ne -40
10
-20
-60
PS C:\Users\takk>

配列を繰り返し。

PS C:\Users\takk> $arr * 2
10
-20
30
-40
50
-60
10
-20
30
-40
50
-60
PS C:\Users\takk>

文字列にも使えます。

PS C:\Users\takk> "HELLO" * 10
HELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLO
PS C:\Users\takk>

奇数を抽出。

PS C:\Users\takk> $arr=1..20
PS C:\Users\takk> ($arr | %{$_*($_%2)}) -ne 0
1
3
5
7
9
11
13
15
17
19
PS C:\Users\takk>

偶数。

PS C:\Users\takk> ($arr | %{$_*(1-$_%2)}) -ne 0
2
4
6
8
10
12
14
16
18
20
PS C:\Users\takk>

PowerShell Get-ChildItem (ls gci dir) ファイル一覧表示

Get-ChildItemを使ってみます。

PS C:\Users\takk> ls


    ディレクトリ: C:\Users\takk


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2017/06/24     19:00            .android
d-r--        2017/06/24     19:00            Contacts
d-r--        2017/06/24     19:01            Desktop
d-r--        2017/06/24     19:00            Documents
d-r--        2017/06/24     19:00            Downloads
d-r--        2017/06/24     19:00            Favorites
d-r--        2017/06/24     19:00            Links
d-r--        2017/06/24     19:00            Music
d-r--        2017/06/24     19:00            Pictures
d-r--        2017/06/24     19:00            Saved Games
d-r--        2017/06/24     19:00            Searches
d-r--        2017/06/24     19:00            Videos


PS C:\Users\takk>

lsコマンドは、元々はUnixで使われていたコマンドで、ただファイル名を表示する仕様が美しかったのですが、PowerShellのlsはなんだか情報が多いですね。
ファイル名のみ表示するオプションもあります。-Nameです。大文字小文字は区別しませんので、ls -nameでよいです。

PS C:\Users\takk> ls -name
.android
Contacts
Desktop
Documents
Downloads
Favorites
Links
Music
Pictures
Saved Games
Searches
Videos
PS C:\Users\takk>

-nameオプションは長くて打つのが面倒だというなら、さらに省略して-nでも良いです。

PS C:\Users\takk> ls -n
.android
Contacts
Desktop
Documents
Downloads
Favorites
Links
Music
Pictures
Saved Games
Searches
Videos
PS C:\Users\takk>

PowerShell Get-Item (gi) ファイル表示、項目表示

Get-ChildItemってコマンドレットがあるってことは、当然Get-Itemってコマンドレットもあり、Childがついてないってことは、指定した項目そのものの情報を表示するってことです。

ディレクトリ構成はこのようになっているとします。

PS C:\Users\taku\Desktop> tree                                                                                          フォルダー パスの一覧
ボリューム シリアル番号は C4E9-35F2 です
C:.
└─a
    └─b
PS C:\Users\taku\Desktop>

ではGet-Itemを使ってみます。引数にディレクトリを指定します。

PS C:\Users\taku\Desktop> gi a

    ディレクトリ: C:\Users\taku\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/03/30     22:48                a


PS C:\Users\taku\Desktop>

どこかでみたことのある情報が出力されました。

この場所で、Get-ContentItemを実行してみます。

PS C:\Users\taku\Desktop> gci

    ディレクトリ: C:\Users\taku\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/03/30     22:48                a


PS C:\Users\taku\Desktop>

まったく同じですね。

Get-Itemでプロパティを取得する

Get-FileやGet-Directoryではなく、わざわざGet-Itemというコマンドレット名なのには理由があります。
単にファイル名やディレクトリ名を取得するだけではないからです。
プロパティを取得してみましょう。
更新日時を表示。

PS C:\Users\taku\Desktop> (get-item a).LastAccessTime
2020年3月31日 20:51:54


PS C:\Users\taku\Desktop>

親ディレクトリを表示。

PS C:\Users\taku\Desktop> (get-item a).Parent
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-r---       2020/03/30     22:48                Desktop


PS C:\Users\taku\Desktop>

Get-Itemの戻り値がオブジェクトなので、プロパティを取得するには、.(ドット)を使ってプロパティ名を指定してやればいいだけです。

どのようなプロパティがあるかというと、以下のようにGet-Memberを使えばわかります。

PS C:\Users\taku\Desktop> get-item a | get-member

   TypeName: System.IO.DirectoryInfo

Name                      MemberType     Definition
----                      ----------     ----------
LinkType                  CodeProperty   System.String LinkType{get=GetLinkType;}
Mode                      CodeProperty   System.String Mode{get=Mode;}
Target                    CodeProperty   System.Collections.Generic.IEnumerable`1[[System.String, mscorlib, Version=...
Create                    Method         void Create(), void Create(System.Security.AccessControl.DirectorySecurity ...
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateSubdirectory        Method         System.IO.DirectoryInfo CreateSubdirectory(string path), System.IO.Director...
Delete                    Method         void Delete(), void Delete(bool recursive)
EnumerateDirectories      Method         System.Collections.Generic.IEnumerable[System.IO.DirectoryInfo] EnumerateDi...
EnumerateFiles            Method         System.Collections.Generic.IEnumerable[System.IO.FileInfo] EnumerateFiles()...
EnumerateFileSystemInfos  Method         System.Collections.Generic.IEnumerable[System.IO.FileSystemInfo] EnumerateF...
Equals                    Method         bool Equals(System.Object obj)
GetAccessControl          Method         System.Security.AccessControl.DirectorySecurity GetAccessControl(), System....
GetDirectories            Method         System.IO.DirectoryInfo[] GetDirectories(), System.IO.DirectoryInfo[] GetDi...
GetFiles                  Method         System.IO.FileInfo[] GetFiles(string searchPattern), System.IO.FileInfo[] G...
GetFileSystemInfos        Method         System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern), System...
GetHashCode               Method         int GetHashCode()
GetLifetimeService        Method         System.Object GetLifetimeService()
GetObjectData             Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Sys...
GetType                   Method         type GetType()
InitializeLifetimeService Method         System.Object InitializeLifetimeService()
MoveTo                    Method         void MoveTo(string destDirName)
Refresh                   Method         void Refresh()
SetAccessControl          Method         void SetAccessControl(System.Security.AccessControl.DirectorySecurity direc...
ToString                  Method         string ToString()
PSChildName               NoteProperty   string PSChildName=a
PSDrive                   NoteProperty   PSDriveInfo PSDrive=C
PSIsContainer             NoteProperty   bool PSIsContainer=True
PSParentPath              NoteProperty   string PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\taku\Des...
PSPath                    NoteProperty   string PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Users\taku\Desktop\a
PSProvider                NoteProperty   ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
Attributes                Property       System.IO.FileAttributes Attributes {get;set;}
CreationTime              Property       datetime CreationTime {get;set;}
CreationTimeUtc           Property       datetime CreationTimeUtc {get;set;}
Exists                    Property       bool Exists {get;}
Extension                 Property       string Extension {get;}
FullName                  Property       string FullName {get;}
LastAccessTime            Property       datetime LastAccessTime {get;set;}
LastAccessTimeUtc         Property       datetime LastAccessTimeUtc {get;set;}
LastWriteTime             Property       datetime LastWriteTime {get;set;}
LastWriteTimeUtc          Property       datetime LastWriteTimeUtc {get;set;}
Name                      Property       string Name {get;}
Parent                    Property       System.IO.DirectoryInfo Parent {get;}
Root                      Property       System.IO.DirectoryInfo Root {get;}
BaseName                  ScriptProperty System.Object BaseName {get=$this.Name;}


PS C:\Users\taku\Desktop>

PowerShell New-Item (mkdir) ディレクトリ作成

ディレクトリを作ってみましょう。mkdirが使えます。New-Itemコマンドレットのエイリアスです。

PS C:\Users\takk> mkdir aaa


    ディレクトリ: C:\Users\takk


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2017/06/26     20:25            aaa


PS C:\Users\takk>

多階層のディレクトリでも一回で作成することができます。

PS C:\Users\takk\aaa> ls
PS C:\Users\takk\aaa> mkdir dir1/dir2/dir3 | out-null
PS C:\Users\takk\aaa> ls -n -r
dir1
dir1\dir2
dir1\dir2\dir3
PS C:\Users\takk\aaa>

PowerShell Set-Location (cd) カレントディレクトリ変更

ディレクトリを切り替えるには、cdです。 (コマンドレットは、Set-Location)

PS C:\Users\takk> cd aaa
PS C:\Users\takk\aaa>

当然ディレクトリは空です。

PS C:\Users\takk\aaa> ls
PS C:\Users\takk\aaa>

ファイルを作ってみます。Linuxと同じくリダイレクトできます。

PS C:\Users\takk\aaa> echo HELLO > file1

ファイルが作成されたか確認します。

PS C:\Users\takk\aaa> ls -n
file1
PS C:\Users\takk\aaa>

PowerShell Get-Content (cat) テキスト内容表示

中身を見てみましょう。catが使えます。(コマンドレットは、Get-Content)

PS C:\Users\takk\aaa> cat file1
HELLO
PS C:\Users\takk\aaa>

PowerShell Move-Item (mv) ファイル名変更

ファイルをリネームするには、mvを使います。

PS C:\Users\takk\aaa> mv file1 file2
PS C:\Users\takk\aaa> ls -n
file2
PS C:\Users\takk\aaa>

PowerShell Remove-Item (rm) ファイル削除

ファイルの削除は、rmです。(コマンドレットは、Remove-Item)

PS C:\Users\takk\aaa> rm file2
PS C:\Users\takk\aaa> ls
PS C:\Users\takk\aaa>

PowerShell Get-Random 乱数

乱数を取得します。

コマンドレットは、Get-Randomです。

PS C:\Users\takk> get-random
2045325134
PS C:\Users\takk> get-random 5
2
PS C:\Users\takk>

引数を指定すると、0~(引数に指定した数-1)の乱数を取得できます。

PS C:\Users\takk> $s=""
PS C:\Users\takk> foreach($i in 0..255){
>> $r=get-random 256
>> $s += "{0:X2} " -f $r
>> if($i % 16 -eq 15){$s += "`n"}
>> }
>>
PS C:\Users\takk> echo $s
64 88 5F B6 32 B3 A9 1E 9B B8 39 68 FD 9E D6 F6
6D D0 E6 EF 2E 0B 67 B0 D7 ED 03 EF EA 21 9C 2B
2D 1E 1E DB 9D AF 00 54 D3 2A B7 10 F7 E9 C3 CE
5E CE BA AA 2C 4B 30 58 20 AF DE 45 B0 BA 34 7A
1C 0E 3A DF 7F FB 59 BD D0 91 1B 04 53 56 B9 EE
2A 34 91 1B 66 F1 FE E2 ED C5 BA ED D1 BB A4 19
F5 3D F4 E8 AF E4 4E 63 75 FD DA 9A 2F 2C 05 C9
F6 F0 1A 7B 28 18 13 2A 0F 58 F2 B9 40 6B EC 14
ED 02 0E 15 62 D0 3F 45 E5 2D A6 69 17 64 B2 C1
C0 F1 F6 E0 A0 28 F0 DD 2A C9 D9 56 F2 94 23 09
10 C5 AD 2C 1D EF 67 26 B1 D5 96 FB 72 A9 13 AA
A5 30 F9 4E 75 0B 74 C4 12 31 EA 98 F6 E8 53 50
0A 9D 59 51 B7 86 A4 D0 8A D0 2F CA 92 F5 6B 81
B6 2E B1 C1 9B D4 94 05 50 E8 1A EB 05 CE 2F C8
81 45 F0 D4 50 C1 F2 1F 8C 28 C4 A4 DB AA 32 1D
C5 69 E1 C7 36 91 B5 35 08 53 00 CF 6B B8 CB BB

PS C:\Users\takk>

PowerShell コマンド置換 コマンド実行結果を使う

コマンド置換は、bashのように$()で囲みます。

PS C:\Users\takk> "{0:X}" -f $(get-random)
246955EC
PS C:\Users\takk>

コマンド置換はネストもできます。

PS C:\Users\takk> mkdir aaa | out-null
PS C:\Users\takk> cd aaa
PS C:\Users\takk\aaa> foreach($i in 0..10){mkdir $("dir_{0:X}" -f $(get-random)) | out-null}
PS C:\Users\takk\aaa> ls


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2017/07/01     11:20            dir_11CFDF63
d----        2017/07/01     11:20            dir_2065207
d----        2017/07/01     11:20            dir_2EA62317
d----        2017/07/01     11:20            dir_3A7F5143
d----        2017/07/01     11:20            dir_46585CA
d----        2017/07/01     11:20            dir_59E634FB
d----        2017/07/01     11:20            dir_5D8B7032
d----        2017/07/01     11:20            dir_60682448
d----        2017/07/01     11:20            dir_6604792F
d----        2017/07/01     11:20            dir_6A254AAD
d----        2017/07/01     11:20            dir_7E2D24B5


PS C:\Users\takk\aaa>

PowerShell Get-FileHash ファイルのハッシュ値 MD5 SHA1 SHA384等

PowerShellでファイルのハッシュ値を求めてみます。このようなファイルがあるとき、

PS C:\Users\takk\aaa> ls -name -re -i *.txt
12\74\06\63\23\file_1.txt
14\53\23\53\43\file_2.txt
14\92\16\14\68\file_3.txt
20\20\97\04\40\file_5.txt
57\93\43\76\7\file_6.txt
86\03\02\65\2\file_4.txt
PS C:\Users\takk\aaa>

Get-FileHashを使います。デフォルトではSHA256です。
各ファイルのSHA256の一覧を表示してみましょう。(lsはGet-ChildItemのエイリアス)

PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA256          D0BD5F8C7875E14A074D98511EED9C3D3BF720F0BDFD008D16052563D4EA94B8       C:\Users\takk\aaa\12\74\06\63...
SHA256          3345CCC61C3BBED900FE98F6431B0E1CDF0BE5EDF627733294FF988F8C43022E       C:\Users\takk\aaa\14\53\23\53...
SHA256          67C5758FEF35C0F828B76D20F10858DB3C42259C9641C4DB89FC5B455584E932       C:\Users\takk\aaa\14\92\16\14...
SHA256          B1CC89066A64CEC02B2D0163C88B4613BB936F5B7DBEF54E1A596B56D3F43D30       C:\Users\takk\aaa\20\20\97\04...
SHA256          2363F9D309044538D5F6958A2D4F05A14E19A02A05E92E16165F2FAF721D5B1F       C:\Users\takk\aaa\57\93\43\76...
SHA256          008E87DE25C15AB9A45324626E2258AF65A499D99C6569002760C6B5185C87A7       C:\Users\takk\aaa\86\03\02\65...

別のアルゴリズムを指定するには、-Algorithmオプションを使います。(-Aは略)ファイルのMD5を求めてみます。

PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A MD5 $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
MD5             218F5179EBAB0BB65A054150FDF65580                                       C:\Users\takk\aaa\12\74\06\63...
MD5             2D39D9EBC71AF0B26E22094BA9E115E0                                       C:\Users\takk\aaa\14\53\23\53...
MD5             EEE6D8EFB87B0791D94D2DD59312A5D9                                       C:\Users\takk\aaa\14\92\16\14...
MD5             616EBF0371CD7B162FD6BB7C941ED8A1                                       C:\Users\takk\aaa\20\20\97\04...
MD5             7317684E8336F2B29F414B5E9AC4FE31                                       C:\Users\takk\aaa\57\93\43\76...
MD5             CFFF13625862792EA22A94437D8C20A8                                       C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa>

他にもSHA1、SHA384、SHA512、MACTripleDES、RIPEMD160が指定できます。

PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A SHA1 $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA1            D50AE4AFDEF5D8DDADE7ECE88184D07FA1ED96F2                               C:\Users\takk\aaa\12\74\06\63...
SHA1            7A1E424D949FD1FCED8582F7141F5A4926D06E5B                               C:\Users\takk\aaa\14\53\23\53...
SHA1            474C63E9FAB49BF8BCC9FCC947BC2B11B36EEBF6                               C:\Users\takk\aaa\14\92\16\14...
SHA1            FA8157FC68C63FB41B2465E011E01D9AD2AB5671                               C:\Users\takk\aaa\20\20\97\04...
SHA1            C2B24C5E23DC9B0C573F8E4E55780C8A9AE1B2E9                               C:\Users\takk\aaa\57\93\43\76...
SHA1            554B62A479ABE742C2E79CB3CCEC17F162BD6029                               C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A SHA384 $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA384          FDD204CCADA4615995B8C7CA39B10F5325D7FE18A4A85440373D7C2A83528C0C6A1... C:\Users\takk\aaa\12\74\06\63...
SHA384          FB2CE751A95120AC311C2C962240E804787E75831C4F5E1A3468FAA11BF69399A4B... C:\Users\takk\aaa\14\53\23\53...
SHA384          44875C31E0B8984CE357A0F8D8587DC3DFD5497FD96DF885A864AC645448D184473... C:\Users\takk\aaa\14\92\16\14...
SHA384          C2853017C46398CD63643C07FFC100790940996B05ED3E27F0F55E0197174F64EFC... C:\Users\takk\aaa\20\20\97\04...
SHA384          511B76D407CDC7D7291C332F0265CBE7528D4F0846072CE3D118A2B7302B0CFFFAE... C:\Users\takk\aaa\57\93\43\76...
SHA384          3EC69551AB36BD2C669DEA202E357F51E44A013EA881FC3D3A5F561583F60D19571... C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A SHA512 $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA512          A76783397DF47097AA4F26ED89F768476EA8BD40E8B1DFC01440BC5BC303738DAA3... C:\Users\takk\aaa\12\74\06\63...
SHA512          CCD17D9A7D5B6B305C73D034CD12C973696F173DEEA4E4F3D39859F30EB27912903... C:\Users\takk\aaa\14\53\23\53...
SHA512          A143A2FA355CE38E7D20C92A7E7B3F0121639539063E2328BF198F98534585F6E3D... C:\Users\takk\aaa\14\92\16\14...
SHA512          B0D0A0AD01CBF2AB99F678A61059F3ABEEE120B2E6F6B47F617F202A2EC6E151171... C:\Users\takk\aaa\20\20\97\04...
SHA512          FDAB0ED01617023A24B47958E143ECDE99845AEAEB92B463955CAEC896AA372BF77... C:\Users\takk\aaa\57\93\43\76...
SHA512          181D119A7924009FC102096E83FFD81C17E1A51CC981ADB4C7CE0D11EA244F98942... C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A MACTripleDES $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
MACTRIPLEDES    FF8B3F61EA3009E6                                                       C:\Users\takk\aaa\12\74\06\63...
MACTRIPLEDES    25242A5795E6C6CF                                                       C:\Users\takk\aaa\14\53\23\53...
MACTRIPLEDES    A7065FAED6BCB0AC                                                       C:\Users\takk\aaa\14\92\16\14...
MACTRIPLEDES    A64D73AF42505439                                                       C:\Users\takk\aaa\20\20\97\04...
MACTRIPLEDES    E838F837F8CD47F0                                                       C:\Users\takk\aaa\57\93\43\76...
MACTRIPLEDES    A7E63E09109AF589                                                       C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa> ls -name -re -i *.txt | %{get-filehash -A RIPEMD160 $_}

Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
RIPEMD160       ECD4C2025F40781E1692221EF17F8B2C9C8BB19E                               C:\Users\takk\aaa\12\74\06\63...
RIPEMD160       36C0B12558F054486377E489ECC0373A8E239A8C                               C:\Users\takk\aaa\14\53\23\53...
RIPEMD160       F5C610EF7F1F1D31849EE4F00CA19E8D8E53D46B                               C:\Users\takk\aaa\14\92\16\14...
RIPEMD160       ECC87B539406FDE866CB47415B7096D85075C1D6                               C:\Users\takk\aaa\20\20\97\04...
RIPEMD160       C2A8BF874288DFD01275F70E5335B60EC5CB5FA6                               C:\Users\takk\aaa\57\93\43\76...
RIPEMD160       BD5D69D4BBF99F9A8B63B868A51C95BF76A01CE9                               C:\Users\takk\aaa\86\03\02\65...


PS C:\Users\takk\aaa>

PowerShell filter フィルタ

フィルタです。オブジェクトをフィルタする場合は、%(ForEach-Object)を使いますが、

PS C:\Users\takk\aaa> 1..10 | %{$_*$_}
1
4
9
16
25
36
49
64
81
100
PS C:\Users\takk\aaa>

これを関数化するには、filterを使います。

PS C:\Users\takk\aaa> filter squre(){
>> $_*$_
>> }
>>
PS C:\Users\takk\aaa> 1..10 | squre
1
4
9
16
25
36
49
64
81
100
PS C:\Users\takk\aaa>

始めと終わりの処理をしたい時は、%を使う場合は、{}を3つ置きます。

PS C:\Users\takk\aaa> 1..10 | %{$sum=0}{$sum+=$_}{$sum}
55
PS C:\Users\takk\aaa>

この3つの{}は、単にBegin/Process/Endの指定を省略しているだけです。

PS C:\Users\takk\aaa> 1..10 | % -Begin{$sum=0} -Process{$sum+=$_} -End{$sum}
55
PS C:\Users\takk\aaa>

ですので、filterを使う場合は、Begin/Process/Endブロックそれぞれの中に必要な処理を書けばよいです。

PS C:\Users\takk\aaa> filter sum(){
>> begin{$sum=0}
>> process{$sum += $_}
>> end{$sum}
>> }
>>
PS C:\Users\takk\aaa> 1..10 | sum
55
PS C:\Users\takk\aaa>

PowerShell Where-Object

Where-Objectを使ってみます。省略形は、?です。

ForEach-Objectを使ってWhere-Objectを表現すると、

PS C:\Users\takk\aaa> 1..10 | %{if($_ % 2){$_}}
1
3
5
7
9
PS C:\Users\takk\aaa>

となります。

Where-Objectを使うと少しだけ短いコマンドラインとなります。

PS C:\Users\takk\aaa> 1..10 | ?{$_ % 2}
1
3
5
7
9
PS C:\Users\takk\aaa>

ファイル一覧に対して、Where-Objectを使ってみましょう。
実験ファイルを適当に作成します。

PS C:\Users\takk\aaa> 1..10 | %{$fname="file$_.txt";"HELLO" > $fname}

このようにファイルが10個できました。

PS C:\Users\takk\aaa> gci


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2017/07/30     23:27         16 file1.txt
-a---        2017/07/30     23:27         16 file10.txt
-a---        2017/07/30     23:27         16 file2.txt
-a---        2017/07/30     23:27         16 file3.txt
-a---        2017/07/30     23:27         16 file4.txt
-a---        2017/07/30     23:27         16 file5.txt
-a---        2017/07/30     23:27         16 file6.txt
-a---        2017/07/30     23:27         16 file7.txt
-a---        2017/07/30     23:27         16 file8.txt
-a---        2017/07/30     23:27         16 file9.txt

-likeを使って、10という文字列が含まれている行のみ抽出します。

PS C:\Users\takk\aaa> gci | ?{$_ -like "*10*"}


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2017/07/30     23:27         16 file10.txt


PS C:\Users\takk\aaa>

正規表現を使っての抽出には、-matchを使います。

PS C:\Users\takk\aaa> gci | ?{$_ -match "[135]"}


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2017/07/30     23:27         16 file1.txt
-a---        2017/07/30     23:27         16 file10.txt
-a---        2017/07/30     23:27         16 file3.txt
-a---        2017/07/30     23:27         16 file5.txt


PS C:\Users\takk\aaa>

PowerShell .NETを使う

PowerShellから.NET FrameWorkを使ってみます。

スタティックメソッドの呼び出し方法は、
[クラス]::メソッド()
です。

PS C:\Users\takk> [System.Console]::WriteLine("HELLO")
HELLO
PS C:\Users\takk>

PowerShellのWrite-OutPutだと改行されてしまうので、改行したくない場合は、.NETを使えば良いですね。

PS C:\Users\takk> [System.Console]::Write("HELLO1\nHELLO2")
HELLO1\nHELLO2PS C:\Users\takk>

変数に格納して実行することもできます。

PS C:\Users\takk> $con=[System.Console]
PS C:\Users\takk> $con::WriteLine("HELLO")
HELLO
PS C:\Users\takk>
PS C:\Users\takk> $math=[Math]
PS C:\Users\takk> $math::Sqrt(121)
11
PS C:\Users\takk>

インスタンスメソッドの呼び出しは、::の代わりに.(ドット)を使います。
RandomのNextメソッド。

PS C:\Users\takk> $r=New-Object System.Random
PS C:\Users\takk> $r.Next(10)
1
PS C:\Users\takk> $r.Next(10)
4
PS C:\Users\takk> $r.Next(10)
1
PS C:\Users\takk>

ArrayListのAddメソッド。

PS C:\Users\takk> $list=New-Object System.Collections.ArrayList
PS C:\Users\takk> $list.Add(10)
0
PS C:\Users\takk> $list.Add(20)
1
PS C:\Users\takk> $list.Add(30)
2
PS C:\Users\takk> $list
10
20
30
PS C:\Users\takk>

別アセンブリで、デフォルトで使えないクラスは、ロードします。

PS C:\Users\takk> [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

GAC    Version        Location
---    -------        --------
True   v4.0.30319     C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089...


PS C:\Users\takk> $f=New-Object System.Windows.Forms.Form
PS C:\Users\takk> $f.ShowDialog()

Add-Typeの場合。

PS C:\Users\takk> $f=new-object System.Windows.Forms.Form
new-object : 型 [System.Windows.Forms.Form] が見つかりません。この型を含むアセンブリが読み込まれていることを確認してく
ださい。
発生場所 行:1 文字:4
+ $f=new-object System.Windows.Forms.Form
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

PS C:\Users\takk> Add-Type -AssemblyName System.Windows.Forms
PS C:\Users\takk> $f=new-object System.Windows.Forms.Form
PS C:\Users\takk>

PowerShell 基数変換 16進数 10進数

PS C:\Users\takk> [Convert]::ToInt64("0x64", 16)
100
PS C:\Users\takk>

PowerShell out-nullで出力を捨てる

PowerShellでディレクトリを作成しようとすると、ディレクトリ作成だけでなく、親切にもたくさんのメッセージを表示してくれます。

PS C:\Users\takk\aaa> mkdir a


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        2017/06/28     21:19            a


PS C:\Users\takk\aaa>

これはこれで良いのですが、とてもかさばります。
メッセージを表示しないようにするには、out-nullへパイプで渡します。

PS C:\Users\takk\aaa> mkdir b | out-null
PS C:\Users\takk\aaa> ls -n
a
b
PS C:\Users\takk\aaa>

PowerShell 再起的ファイル一覧

for文を使って深階層のディレクトリを作ってみます。

PS C:\Users\takk\aaa> for($i=0; $i -lt 10; $i++){
>> mkdir $i | out-null
>> cd $i
>> }
>>
PS C:\Users\takk\aaa\0\1\2\3\4\5\6\7\8\9>

ディレクトリ作成と同時にcdしてますので、一番深いディレクトリにまで来てしまいました。
cd ..で、一つ上のディレクトリに切り替わります。

PS C:\Users\takk\aaa\0\1\2\3\4\5\6\7\8\9> cd ..
PS C:\Users\takk\aaa\0\1\2\3\4\5\6\7\8>

面倒なので、ホームディレクトリへ切り替えます。

PS C:\Users\takk\aaa\0\1\2\3\4\5\6\7\8> cd ~
PS C:\Users\takk>

findのように階層を追ってすべて表示する場合は、-Recurseオプションを使います。-rでよいです。-nは-Nameの略で名前のみです。

PS C:\Users\takk\aaa> ls -n -r
0
a
b
0\1
0\1\2
0\1\2\3
0\1\2\3\4
0\1\2\3\4\5
0\1\2\3\4\5\6
0\1\2\3\4\5\6\7
0\1\2\3\4\5\6\7\8
0\1\2\3\4\5\6\7\8\9
PS C:\Users\takk\aaa>

ls -n -rで、ディレクトリだけでなく、ファイルも一覧に表示されます。

PS C:\Users\takk\aaa> echo HELLO > 0\1\2\aa.txt
PS C:\Users\takk\aaa> ls -n -r
0
a
b
0\1
0\1\2
0\1\2\3
0\1\2\aa.txt
0\1\2\3\4
0\1\2\3\4\5
0\1\2\3\4\5\6
0\1\2\3\4\5\6\7
0\1\2\3\4\5\6\7\8
0\1\2\3\4\5\6\7\8\9
PS C:\Users\takk\aaa>

ディレクトリのみに限定する場合は、-Directoryオプション(-di)を使います。

PS C:\Users\takk\aaa> ls -n -r -di
0
a
b
0\1
0\1\2
0\1\2\3
0\1\2\3\4
0\1\2\3\4\5
0\1\2\3\4\5\6
0\1\2\3\4\5\6\7
0\1\2\3\4\5\6\7\8
0\1\2\3\4\5\6\7\8\9
PS C:\Users\takk\aaa>

PowerShell パス区切り文字の変換

ディレクトリを用意して、

PS C:\Users\takk\aaa> ls
PS C:\Users\takk\aaa> mkdir dir1/dir2/dir3 | out-null
PS C:\Users\takk\aaa> ls -n -r
dir1
dir1\dir2
dir1\dir2\dir3
PS C:\Users\takk\aaa>

このディレクトリパスの\を/に置換してみます。

PS C:\Users\takk\aaa> ls -n -r | %{$_.Replace("\","/")}
dir1
dir1/dir2
dir1/dir2/dir3
PS C:\Users\takk\aaa>

正規表現を使う場合。

PS C:\Users\takk\aaa> ls -n -r | %{$_ -Replace("(dir.)","[$&]")}
[dir1]
[dir1]\[dir2]
[dir1]\[dir2]\[dir3]
PS C:\Users\takk\aaa>

ランダムに階層の深いディレクトリを作ってファイルを保存してみます。

PS C:\Users\takk\aaa> 1..5 | %{$r=get-random; $dir=$r -replace("(...)","$&\\");mkdir $dir | out-null;echo HELLO > $dir/f
ile.txt}
PS C:\Users\takk\aaa> ls -n -r
113
182
193
640
890
113\938
113\938\662
113\938\662\3
113\938\662\3\file.txt
182\911
182\911\275
182\911\275\1
182\911\275\1\file.txt
193\133
193\133\520
193\133\520\file.txt
640\446
640\446\641
640\446\641\file.txt
890\224
890\224\140
890\224\140\file.txt
PS C:\Users\takk\aaa>

PowerShell バイナリファイル読み込み

バイナリファイルを読み込みます。

まずは元になるバイナリの生成から。

PS C:\Users\takk\aaa> echo 123 > a.bin
PS C:\Users\takk\aaa> ls a.bin


    ディレクトリ: C:\Users\takk\aaa


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2017/07/04     22:04         12 a.bin


PS C:\Users\takk\aaa>

生成したバイナリをcat(コマンドレットはget-content)で読み込みます。

PS C:\Users\takk\aaa> cat -encoding byte a.bin
255
254
49
0
50
0
51
0
13
0
10
0
PS C:\Users\takk\aaa>

-encodingは、-enにbyteはbyに省略できます。

PS C:\Users\takk\aaa> cat -en by a.bin
255
254
49
0
50
0
51
0
13
0
10
0
PS C:\Users\takk\aaa>

それにしても、123という文字列をリダイレクトしただけなのに、データ数が多い気がします。

16進数で横に並べてみましょう。

PS C:\Users\takk\aaa> cat -en by a.bin | %{$s=""}{$s+="{0:x2} " -f $_}{echo $s}
ff fe 31 00 32 00 33 00 0d 00 0a 00
PS C:\Users\takk\aaa>

PowerShellのecho(Write-Output)は、BOM付きUTFでテキストファイルを作るようです。

コマンドプロンプトのechoを利用しましょう。

PS C:\Users\takk\aaa> cmd /c "echo 123 > b.bin"
PS C:\Users\takk\aaa> cat -en by b.bin | %{$s=""}{$s+="{0:x2} " -f $_}{echo $s}
31 32 33 20 0d 0a
PS C:\Users\takk\aaa>

意図したとおりにバイナリができました。

PowerShell バイナリファイル書き込み

PowerShellの変数には型があります。

[型]変数名 = 値

PS C:\Users\takk\aaa> [byte]$a = 255
PS C:\Users\takk\aaa> $a
255

byte型は、0~255なので、255より大きい数字を入れるとエラーとなります。

PS C:\Users\takk\aaa> [byte]$a = 256
値 "256" を型 "System.Byte" に変換できません。エラー: "符号なしバイト型に対して値が大きすぎるか、または小さすぎます。"
発生場所 行:1 文字:1
+ [byte]$a = 256
+ ~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

PS C:\Users\takk\aaa>

配列の型を指定する場合は、このように指定します。

[byte[]]$a = 1,2,3

では、byte配列を使って、バイナリファイルを作ってみましょう。

PS C:\Users\takk\aaa> [byte[]]$bin=0..255
PS C:\Users\takk\aaa> sc -value $bin -encoding byte a.bin
PS C:\Users\takk\aaa>

意図した通りにバイナリファイルが作成されたか確認するため、
ファイルダンプ関数を作ります。

PS C:\Users\takk\aaa> function hexdump($file){
	$i=0
	$s=""
	$bin=cat -en by $file
	foreach($v in $bin){
		$s += "{0:x2} " -f $v
		if($i % 16 -eq 15){
			$s+= "`n"
		}
		$i ++
	}
	echo $s
}

PS C:\Users\takk\aaa>

ではa.binの内容を確認してみましょう。

PS C:\Users\takk\aaa> hexdump a.bin
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff

PS C:\Users\takk\aaa>

ランダムなバイナリを作ってみます。

PS C:\Users\takk\aaa> [byte[]]$rbin = 0..255 | %{get-random -Max 256}

ランダムな配列$rbinができたので、これをsc(Set-Content)でバイナリ書き込みします。

PS C:\Users\takk\aaa> sc -value $rbin -en by a.bin
PS C:\Users\takk\aaa> hexdump a.bin
b3 4c 9f dd 12 da 67 74 e7 cd 13 f1 88 68 15 cb
12 a4 f4 ce 79 3f 49 63 e1 ba 25 c7 f7 36 22 ff
2c ee 3c 93 72 35 54 c1 02 25 00 af 70 fb 92 0a
1c d7 71 35 9e d9 3a 74 02 3c fc 57 b5 9f 7c b0
ab 14 c4 9a 2c 81 58 dd 4f 32 cb 53 3f 9a f2 e5
28 1a ab 1f c5 ed 61 53 b4 c7 90 36 39 fc 0c 63
a9 4f 04 5b 36 f7 dd 9a 7f 94 e5 6b 0d e7 35 68
49 16 2f 9b f4 5c eb bd b3 70 e6 64 f1 22 a3 52
26 68 aa ef 95 96 ae 30 3c 10 a0 31 07 f5 46 cd
92 27 ec 22 cd 70 6f 4c 64 46 a8 c6 11 79 a9 5c
f0 92 45 a4 3c 45 d2 b2 67 fe 5e e3 bc b9 b6 be
2a 19 d2 c9 35 81 85 b5 f9 5d 8a 4f ee e8 1e c3
67 44 41 74 af a1 90 4c 55 39 bb cf 12 8b 90 ab
8f ea 9b f8 a7 e0 26 6e 0d 90 ab df bb 83 c4 7e
df 9a 7b 78 77 42 0e 88 88 85 74 fb c6 b5 a2 6d
cd df c0 03 4d 25 1c 87 1d d2 66 1f f5 b1 91 d1

PS C:\Users\takk\aaa>

PowerShell cmdと連携

PowerShellとcmd(コマンドプロンプト)の連携をします。

PowerShellは、繰り返し処理が簡潔に書けます。
1~10の連番の生成は、5文字書くだけです。

C:\Users\takk> 1..10

しかしcmd(コマンドプロンプト)では、同じようには書けません。

C:\Users\takk> 1..10


この通り、エラーとなります。

cmdから1~10の連番を作るには、forコマンドを使います。

C:\Users\takk> for /l %i in (1,1,10);do @echo %i


このようにforコマンドは使い方が難しいです。代わりにコマンドプロンプトからPowerShellを呼び出ししてみましょう。powershellのコマンドを実行して結果を得るには、-Cオプションを使います。

C:\Users\takk>powershell -C 1..10

PowerShell 応用編

PowerShell 複数ファイルの置換

ForEach-Objectを使って、複数のファイルを置換してみます。
まずは例によってランダムなファイル生成から。

PS C:\Users\takk\aaa> 1..6 | %{$n=1}{$r=random;$dir=$r -replace("(....)","$&\\");mkdir $dir | out-null;1..4 | % {echo $_"   "$r >> $dir/file_$r.txt}}
PS C:\Users\takk\aaa>

このようなフォルダとファイルができました。

PS C:\Users\takk\aaa> ls -s -n -File
1009\7783\03\file_1009778303.txt
1140\3858\4\file_114038584.txt
1236\9624\05\file_1236962405.txt
1374\5229\22\file_1374522922.txt
8623\1718\5\file_862317185.txt
9398\6818\file_93986818.txt
PS C:\Users\takk\aaa>

現在の各ファイルの内容は、このようになっています。

PS C:\Users\takk\aaa> ls -s -n -File | %{echo ===$_===;cat $_}
===1009\7783\03\file_1009778303.txt===
1   1009778303
2   1009778303
3   1009778303
4   1009778303
===1140\3858\4\file_114038584.txt===
1   114038584
2   114038584
3   114038584
4   114038584
===1236\9624\05\file_1236962405.txt===
1   1236962405
2   1236962405
3   1236962405
4   1236962405
===1374\5229\22\file_1374522922.txt===
1   1374522922
2   1374522922
3   1374522922
4   1374522922
===8623\1718\5\file_862317185.txt===
1   862317185
2   862317185
3   862317185
4   862317185
===9398\6818\file_93986818.txt===
1   93986818
2   93986818
3   93986818
4   93986818
PS C:\Users\takk\aaa>

では置換します。元のファイルは残したまま置換します。
二行目の末尾に印をつけます。

PS C:\Users\takk\aaa> ls -s -n -File | %{$fname=$_+".newfile";cat $_ | %{ $_ -creplace "^(2.*)$","$& <---" >> $fname}}
PS C:\Users\takk\aaa>

置換前のファイルに加えて、同じフォルダに、置換後のファイルができましたね。

PS C:\Users\takk\aaa> ls -s -n -File
1009\7783\03\file_1009778303.txt
1009\7783\03\file_1009778303.txt.newfile
1140\3858\4\file_114038584.txt
1140\3858\4\file_114038584.txt.newfile
1236\9624\05\file_1236962405.txt
1236\9624\05\file_1236962405.txt.newfile
1374\5229\22\file_1374522922.txt
1374\5229\22\file_1374522922.txt.newfile
8623\1718\5\file_862317185.txt
8623\1718\5\file_862317185.txt.newfile
9398\6818\file_93986818.txt
9398\6818\file_93986818.txt.newfile
PS C:\Users\takk\aaa>

newfileで絞り込みます。

PS C:\Users\takk\aaa> ls -n -s -Filter *.newfile
1009\7783\03\file_1009778303.txt.newfile
1140\3858\4\file_114038584.txt.newfile
1236\9624\05\file_1236962405.txt.newfile
1374\5229\22\file_1374522922.txt.newfile
8623\1718\5\file_862317185.txt.newfile
9398\6818\file_93986818.txt.newfile
PS C:\Users\takk\aaa>

各ファイルの内容はこのようになっています。2行目に印がつきました。

PS C:\Users\takk\aaa> ls -s -n -Filter *.newfile | %{echo ===$_===;cat $_}
===1009\7783\03\file_1009778303.txt.newfile===
1   1009778303
2   1009778303 <---
3   1009778303
4   1009778303
===1140\3858\4\file_114038584.txt.newfile===
1   114038584
2   114038584 <---
3   114038584
4   114038584
===1236\9624\05\file_1236962405.txt.newfile===
1   1236962405
2   1236962405 <---
3   1236962405
4   1236962405
===1374\5229\22\file_1374522922.txt.newfile===
1   1374522922
2   1374522922 <---
3   1374522922
4   1374522922
===8623\1718\5\file_862317185.txt.newfile===
1   862317185
2   862317185 <---
3   862317185
4   862317185
===9398\6818\file_93986818.txt.newfile===
1   93986818
2   93986818 <---
3   93986818
4   93986818
PS C:\Users\takk\aaa>

PowerShell 置換後ファイルを別フォルダへ

置換後のファイルを別フォルダに格納したいと思います。
dir1が置換前フォルダ
dir2が置換後フォルダ
とします。

では、置換前フォルダのdir1を作ります。中はランダムにフォルダとファイルにします。

PS C:\Users\takk\aaa> 1..6 | %{$n=1}{$r=random;$dir="dir1\\"+$r -replace("(....)","$&\\");mkdir $dir | out-null;1..4 | % {echo $_"    "$r >> $dir/file_$r.txt}}
PS C:\Users\takk\aaa> ls -n -re -file
dir1\11\8467\8195\file_1184678195.txt
dir1\16\1271\2013\file_1612712013.txt
dir1\17\6129\8812\file_1761298812.txt
dir1\20\6212\8419\file_2062128419.txt
dir1\45\1915\907\file_451915907.txt
dir1\70\7329\220\file_707329220.txt
PS C:\Users\takk\aaa>

次にdir1のフォルダ構成だけコピーしたdir2を作ります。

PS C:\Users\takk\aaa> xcopy /t /e dir1 dir2
dir2 は受け側のファイル名ですか、
またはディレクトリ名ですか
(F= ファイル、D= ディレクトリ)? d
PS C:\Users\takk\aaa>

フォルダツリーがコピーできたか確認しましょう。

PS C:\Users\takk\aaa> tree dir1
フォルダー パスの一覧
ボリューム シリアル番号は 000000AF C288:F709 です
C:\USERS\TAKK\AAA\DIR1
├─11
│  └─8467
│      └─8195
├─16
│  └─1271
│      └─2013
├─17
│  └─6129
│      └─8812
├─20
│  └─6212
│      └─8419
├─45
│  └─1915
│      └─907
└─70
    └─7329
        └─220
PS C:\Users\takk\aaa> tree dir2
フォルダー パスの一覧
ボリューム シリアル番号は 0000000F C288:F709 です
C:\USERS\TAKK\AAA\DIR2
├─11
│  └─8467
│      └─8195
├─16
│  └─1271
│      └─2013
├─17
│  └─6129
│      └─8812
├─20
│  └─6212
│      └─8419
├─45
│  └─1915
│      └─907
└─70
    └─7329
        └─220
PS C:\Users\takk\aaa>

今、dir1フォルダにある各ファイルはこのような内容となっています。

PS C:\Users\takk\aaa> ls dir1 -n -s -file | %{echo ===dir1\$_===;cat dir1\$_}
===dir1\11\8467\8195\file_1184678195.txt===
1    1184678195
2    1184678195
3    1184678195
4    1184678195
===dir1\16\1271\2013\file_1612712013.txt===
1    1612712013
2    1612712013
3    1612712013
4    1612712013
===dir1\17\6129\8812\file_1761298812.txt===
1    1761298812
2    1761298812
3    1761298812
4    1761298812
===dir1\20\6212\8419\file_2062128419.txt===
1    2062128419
2    2062128419
3    2062128419
4    2062128419
===dir1\45\1915\907\file_451915907.txt===
1    451915907
2    451915907
3    451915907
4    451915907
===dir1\70\7329\220\file_707329220.txt===
1    707329220
2    707329220
3    707329220
4    707329220
PS C:\Users\takk\aaa>

では行末4文字をAAAAに置換してみましょう。置換後のファイルはdir2に置きます。

PS C:\Users\takk\aaa> ls dir1 -n -s -file | %{$old="dir1\\"+$_;$new="dir2\\"+$_;cat $old | %{$_ -creplace "....$","AAAA" >> $new}}
PS C:\Users\takk\aaa>

置換後ファイルを確認します。

PS C:\Users\takk\aaa> ls dir2 -n -re -file | %{echo ===dir2\$_===;cat dir2\$_}
===dir2\11\8467\8195\file_1184678195.txt===
1    118467AAAA
2    118467AAAA
3    118467AAAA
4    118467AAAA
===dir2\16\1271\2013\file_1612712013.txt===
1    161271AAAA
2    161271AAAA
3    161271AAAA
4    161271AAAA
===dir2\17\6129\8812\file_1761298812.txt===
1    176129AAAA
2    176129AAAA
3    176129AAAA
4    176129AAAA
===dir2\20\6212\8419\file_2062128419.txt===
1    206212AAAA
2    206212AAAA
3    206212AAAA
4    206212AAAA
===dir2\45\1915\907\file_451915907.txt===
1    45191AAAA
2    45191AAAA
3    45191AAAA
4    45191AAAA
===dir2\70\7329\220\file_707329220.txt===
1    70732AAAA
2    70732AAAA
3    70732AAAA
4    70732AAAA
PS C:\Users\takk\aaa>

PowerShell 指定ファイルの一覧取得

linuxでやるとfind -name file_4.*、というような一覧の取得を、PowerShellでもやってみます。

まずは実験用フォルダの作成から。

PS C:\Users\takk\aaa> 1..6 | %{$n=1}{$r=random;$dir=$r -replace("(..)","$&\\");mkdir $dir | out-null;1..4 | %{echo $_"    "$r >> $dir/file_$n.txt};$n++}

このようにフォルダとファイルができました。

PS C:\Users\takk\aaa> ls -name -re
12
14
20
57
86
12\74
12\74\06
12\74\06\63
12\74\06\63\23
12\74\06\63\23\file_1.txt
14\53
14\92
14\53\23
14\53\23\53
14\53\23\53\43
14\53\23\53\43\file_2.txt
14\92\16
14\92\16\14
14\92\16\14\68
14\92\16\14\68\file_3.txt
20\20
20\20\97
20\20\97\04
20\20\97\04\40
20\20\97\04\40\file_5.txt
57\93
57\93\43
57\93\43\76
57\93\43\76\7
57\93\43\76\7\file_6.txt
86\03
86\03\02
86\03\02\65
86\03\02\65\2
86\03\02\65\2\file_4.txt
PS C:\Users\takk\aaa>

file_1.txtとfile_2.txt、file_4*のパスを取得します。
-includeオプションを指定して、,(カンマ)で区切ればよいです。
-includeは-iで省略できます。

PS C:\Users\takk\aaa> ls -name -re -i file_1.txt,file_2.txt,file_4.*
12\74\06\63\23\file_1.txt
14\53\23\53\43\file_2.txt
86\03\02\65\2\file_4.txt
PS C:\Users\takk\aaa>

フルパスの取得は、dirを使えばよいです。

PS C:\Users\takk\aaa> cmd /c "dir /b /s file_1.txt file_2.txt file_4.*"
C:\Users\takk\aaa\12\74\06\63\23\file_1.txt
C:\Users\takk\aaa\14\53\23\53\43\file_2.txt
C:\Users\takk\aaa\86\03\02\65\2\file_4.txt
PS C:\Users\takk\aaa>

PowerShell 重複してない内容のファイルを抽出

2つのファイル群を作って、内容が一致するものがどれぐらいあるか確認してみます。

ファイルの内容は1~5の数字の組み合わせで作ります。

PS C:\Users\takk\aaa> function 5num(){
>> 1..5 | %{random -min 1 -max 5}
>> }
>>
PS C:\Users\takk\aaa> 5num
4
1
3
1
4
PS C:\Users\takk\aaa>

まず一つ目のファイル群としてdir1を作ってファイルを100個作ります。

PS C:\Users\takk\aaa\dir1> 1..100 | % {$n=1}{5num > file_$n; $n++}
PS C:\Users\takk\aaa\dir1>

ファイルが100個生成されます。

PS C:\Users\takk\aaa\dir1> (ls -name)[0..9]
file_1
file_10
file_100
file_11
file_12
file_13
file_14
file_15
file_16
file_17
PS C:\Users\takk\aaa\dir1>

ファイルの内容はそれぞれ異なります。

PS C:\Users\takk\aaa\dir1> (ls -name | %{echo ===$_===;cat $_})[0..9]
===file_1===
1
2
2
2
2
===file_10===
1
3
4
PS C:\Users\takk\aaa\dir1>

これをもう一つ作ります。

PS C:\Users\takk\aaa\dir2> 1..100 | % {$n=1}{5num > file_$n; $n++}
PS C:\Users\takk\aaa\dir2>

各フォルダにあるファイルのMD5を確認してみましょう。10ファイル分のみ表示しています。
dir1から。

PS C:\Users\takk\aaa> (ls dir1 -file | %{get-filehash -A MD5 dir1/$_})[0..9]

Algorithm       Hash                                        Path
---------       ----                                        ----
MD5             349FE18B709737FF903B301A8D5809AB            C:\Users\takk\aaa\dir1\file_1
MD5             4C710F3E92557BABA923F47C866DEA05            C:\Users\takk\aaa\dir1\file_10
MD5             93172F647FBDEF3634C9242A8053C35D            C:\Users\takk\aaa\dir1\file_100
MD5             B882146EF4FBBBC0DDC759FBEB34B69C            C:\Users\takk\aaa\dir1\file_11
MD5             C9467816275EAE1A54DE45FE6DDB02AE            C:\Users\takk\aaa\dir1\file_12
MD5             AE2933E92ECB3CAEC642A74546833B2A            C:\Users\takk\aaa\dir1\file_13
MD5             4DF661A8583B2FB04E9E3E400EC02ADA            C:\Users\takk\aaa\dir1\file_14
MD5             BC7F41F1B446E09C464F954EA5D86CCA            C:\Users\takk\aaa\dir1\file_15
MD5             7F101CC43794720C7EAA678DC3F3BAB6            C:\Users\takk\aaa\dir1\file_16
MD5             5A60F517FA7C678B158C8CC21CAB27C0            C:\Users\takk\aaa\dir1\file_17


PS C:\Users\takk\aaa>
PS C:\Users\takk\aaa> (ls dir2 -file | %{get-filehash -A MD5 dir2/$_})[0..9]

Algorithm       Hash                                        Path
---------       ----                                        ----
MD5             323D55B920AEA935F72B45210C8F1DDE            C:\Users\takk\aaa\dir2\file_1
MD5             D081F8BFA19B4E3D4B6C024FD3D47B12            C:\Users\takk\aaa\dir2\file_10
MD5             C3E2624D47BBDA6E0AB9516DA993B8F1            C:\Users\takk\aaa\dir2\file_100
MD5             18AE851073AC7A8E1BD9DB693CE4869C            C:\Users\takk\aaa\dir2\file_11
MD5             698A011B551CBD80B7D03703CAA214FD            C:\Users\takk\aaa\dir2\file_12
MD5             F7F795DB9BC48816F20970AF0A98C92D            C:\Users\takk\aaa\dir2\file_13
MD5             11BEC7147D833D0AE3F5A6C3919CAD82            C:\Users\takk\aaa\dir2\file_14
MD5             9D3274BCB6A4FFB288AF03AB7F876A11            C:\Users\takk\aaa\dir2\file_15
MD5             A77636F22FE1124EC21A4F6501D8C540            C:\Users\takk\aaa\dir2\file_16
MD5             81B90CE24BE723830422C977BB6DA7BD            C:\Users\takk\aaa\dir2\file_17


PS C:\Users\takk\aaa>

当然総数は200となります。

PS C:\Users\takk\aaa> (ls -name -re -file | %{(get-filehash -A MD5 $_).hash}).Count
200
PS C:\Users\takk\aaa>

重複していない数を調べます。

PS C:\Users\takk\aaa> (ls -name -re -file | %{(get-filehash -A MD5 $_).hash} | sort | get-unique).Count
183
PS C:\Users\takk\aaa>

183個あったので、重複しているMD5は17個となります。
奇数個あるので、つまり2つずつのセットにはできないですね。

ファイルも合わせて確認するとわかりますが、

PS C:\Users\takk\aaa> ls -name -re -file | %{"{0:s} {0:s}" -f (get-filehash -A MD5 $_).hash ,$_} | sort
C9467816275EAE1A54DE45FE6DDB02AE dir1\file_12
C9467816275EAE1A54DE45FE6DDB02AE dir1\file_52

同じdir1フォルダに一致する内容のファイルがありました。

PowerShell Compare-Object

catを使います。PowerShellのcatはGet-Contentコマンドレットのエイリアスですが、このcatとdiffを使ってファイルの比較をしたいと思います。
ちなみにdiff(Compare-Object)だけしか使わないとこのように、ファイル名に比較しかしません。

PS C:\Users\takk\aaa> diff file1.txt file2.txt

InputObject                                                 SideIndicator
-----------                                                 -------------
file2.txt                                                   =>
file1.txt                                                   <=


PS C:\Users\takk\aaa>

では2つファイルを用意します。

PS C:\Users\takk\aaa> 10001..10010 > file1.txt
PS C:\Users\takk\aaa> copy file1.txt file2.txt
PS C:\Users\takk\aaa>

差分を作りたいので、notepad等でファイルを適当に編集します。

PS C:\Users\takk\aaa> notepad file2.txt
PS C:\Users\takk\aaa>

このように編集しました。

PS C:\Users\takk\aaa> cat file2.txt
10001
10002
10003
10004
10305
10006
10407
10008
10509
10010
PS C:\Users\takk\aaa>

各ファイルをcatして内容を変数に格納します。

PS C:\Users\takk\aaa> $a=cat file1.txt
PS C:\Users\takk\aaa> $b=cat file2.txt
PS C:\Users\takk\aaa>

後は、変数をdiff(Compare-Object)すれば良いです。

PS C:\Users\takk\aaa> diff $a $b

InputObject                                                 SideIndicator
-----------                                                 -------------
10305                                                       =>
10407                                                       =>
10509                                                       =>
10005                                                       <=
10007                                                       <=
10009                                                       <=


PS C:\Users\takk\aaa>

diff(Compare-Object)は、2つのセットの差分を取るだけなので、順序のあるテキストは比較できません。

$d1には1~4の数字、$d2には順序を変えただけの1~4を格納して比較してみます。

PS C:\Users\takk\aaa> $d1=1,2,3,4
PS C:\Users\takk\aaa> $d2=1,3,4,2
PS C:\Users\takk\aaa> diff $d1 $d2
PS C:\Users\takk\aaa>

この通り、差分が出ません。あくまでもセット(集合)の比較です。

PowerShell cut代替

PowerShellでcutコマンドと似たようなことができないか確認してみます。

実験用のデータを生成します。タブ区切りフィールド3列、10レコードのデータです。

PS C:\Users\takk\aaa> $data=1..10 | %{"$_`t"+$(random)+"`tAAAA"}
PS C:\Users\takk\aaa> $data
1       1578837591      AAAA
2       222539531       AAAA
3       1974766125      AAAA
4       475015556       AAAA
5       647225503       AAAA
6       1860466963      AAAA
7       476117047       AAAA
8       1962103142      AAAA
9       1377184796      AAAA
10      1308106584      AAAA
PS C:\Users\takk\aaa>

まずは、cut -f、タブ区切りのフィールド抽出から。
以下のようにSplitを使って、文字列を分割して使うだけです。

PS C:\Users\takk\aaa> $arr="aaa,bbb,ccc".Split(",")
PS C:\Users\takk\aaa> $arr[1]
bbb
PS C:\Users\takk\aaa>

ではSplitを使って、実験データの1列目、3列目のみを抽出してみます。

PS C:\Users\takk\aaa> $data | %{$a,$b,$c=$_.Split("`t"); "$a`t$c"}
1       AAAA
2       AAAA
3       AAAA
4       AAAA
5       AAAA
6       AAAA
7       AAAA
8       AAAA
9       AAAA
10      AAAA
PS C:\Users\takk\aaa>

2列目の乱数値の和を計算してみます。

PS C:\Users\takk\aaa> $data | %{$sum=0}{$a,$b,$c=$_.Split("`t");$sum=$sum+$b}{$sum}
11882362838
PS C:\Users\takk\aaa>

cut -bはバイト位置を指定して抽出ができますが、これをPowerShellで実現するためには、StringをChar[]でキャストすれば、配列で指定できるので扱いやすくなります。

PS C:\Users\takk\aaa> $arr="HELLO"
PS C:\Users\takk\aaa> $c=[char[]]$arr
PS C:\Users\takk\aaa> $c[2]
L
PS C:\Users\takk\aaa>

まあ、cut -bを使うときは、範囲で指定すると思いますから、Substring使った方が早そうです。

PS C:\Users\takk\aaa> $data | % {$_.substring(0,4)}
1       15
2       22
3       19
4       47
5       64
6       18
7       47
8       19
9       13
10      1
PS C:\Users\takk\aaa>

PowerShell join代替

Unix/Linuxのjoinコマンドは指定列の同一項目を結合してくれるコマンドですが、これを、PowerShellでもやってみたいと思います。
まずjoinする元のファイルを適当に作ります。

PS C:\Users\takk\aaa> 1..10 | %{ "{0:d1}`t{1:d1}" -f $_ , $(random)} > file1
PS C:\Users\takk\aaa> 3,4,5 | %{ "{0:d1}`t{1:d1}" -f $_ , $(random)} > file2

それぞれの内容です。

PS C:\Users\takk\aaa> cat file1
1       497585595
2       1980261099
3       182124860
4       1085555943
5       1382725001
6       381820766
7       500921000
8       361022721
9       131614824
10      1843791556
PS C:\Users\takk\aaa> cat file2
3       1421951741
4       1254637545
5       1604066056
PS C:\Users\takk\aaa>

1列目の情報でjoinしてみましょう。連想配列を使います。

PS C:\Users\takk\aaa> cat file2 | %{$hash=@{}}{$a,$b=$_.split("`t");$hash.add($a,$b)}{ cat file1 | %{$a,$b=$_.split("`t");"$_`t"+$hash["$a"]}}
1       497585595
2       1980261099
3       182124860       1421951741
4       1085555943      1254637545
5       1382725001      1604066056
6       381820766
7       500921000
8       361022721
9       131614824
10      1843791556
PS C:\Users\takk\aaa>

このままだと使いにくいので、関数にします。

PS C:\Users\takk\aaa> function join($file1,$file2){
>> cat $file2 | %{$hash=@{}}{$a,$b=$_.split("`t");$hash.add($a,$b)}{
>> cat $file1 | %{$a,$b=$_.split("`t");"$_`t"+$hash["$a"]}}
>> }
>>
PS C:\Users\takk\aaa>

名前をjoinにしました。

join ファイル名 ファイル名
で実行すると、1列目の文字列が一致する行を結合します。

PS C:\Users\takk\aaa> join file1 file2
1       497585595
2       1980261099
3       182124860       1421951741
4       1085555943      1254637545
5       1382725001      1604066056
6       381820766
7       500921000
8       361022721
9       131614824
10      1843791556
PS C:\Users\takk\aaa> join file2 file1
3       1421951741      182124860
4       1254637545      1085555943
5       1604066056      1382725001
PS C:\Users\takk\aaa>

別のファイルを作って試してみましょう。

PS C:\Users\takk\aaa> 3,5,7 | %{ "$_`tBBBB`tCCCC"} > file3
PS C:\Users\takk\aaa> join file1 file3
1       497585595
2       1980261099
3       182124860       BBBB CCCC
4       1085555943
5       1382725001      BBBB CCCC
6       381820766
7       500921000       BBBB CCCC
8       361022721
9       131614824
10      1843791556
PS C:\Users\takk\aaa> join file1 file3

PowerShell ArrayListとHashSet

.NETのArrayListとHashSetを使います。

配列の使い方はこうでした。

PS C:\Users\takk> $arr=1..10
PS C:\Users\takk> $arr
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk>

ArrayListへ置き換えるには、new-objectを使います。

PS C:\Users\takk> $lis = new-object System.Collections.ArrayList(,$arr)
PS C:\Users\takk> $lis
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk>

要素5番目に100を追加しましょう。

PS C:\Users\takk> $lis.Insert(5,100)
PS C:\Users\takk> $lis
1
2
3
4
5
100
6
7
8
9
10

HashSetの置き換えも同様です。object型です。

PS C:\Users\takk> $hashset=new-object System.Collections.Generic.HashSet[object](,$arr)
PS C:\Users\takk> $hashset
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk>

HashSetに重複した値を追加しようとすると、失敗します。

PS C:\Users\takk> $hashset.add(5)
False
PS C:\Users\takk> $hashset
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk>

差分を抽出してみましょう。
元の集合から、1,3,5,7,9を引いてみます。

PS C:\Users\takk> $hashset.ExceptWith(@(1,3,5,7,9))
PS C:\Users\takk> $hashset
2
4
6
8
10
PS C:\Users\takk>

PowerShell head代替

PowerShellでheadっぽいことをやってみます。
実験ファイルの生成から。

PS C:\Users\takk\aaa> 1..5 | %{$fname="file$_.txt";1..10 | %{ $s="$_ "+$(random);$s >> $fname}}

このようなファイルができました。

PS C:\Users\takk\aaa> gci -name
file1.txt
file2.txt
file3.txt
file4.txt
file5.txt
PS C:\Users\takk\aaa>

file1についてファイルの内容を全部表示してみます。

PS C:\Users\takk\aaa> gc file1.txt
1 1237559471
2 1512586903
3 549321715
4 1944462360
5 1504720877
6 582363922
7 1745017158
8 1286318147
9 907364139
10 2120535931
PS C:\Users\takk\aaa>

これを頭から5行だけ表示するには、スライスを使えばよいです。

PS C:\Users\takk\aaa> (gc file1.txt)[0..4]
1 1237559471
2 1512586903
3 549321715
4 1944462360
5 1504720877
PS C:\Users\takk\aaa>

全ファイル頭5行を表示してみます。

PS C:\Users\takk\aaa> gci -name -re -i *.txt | %{(gc $_)[0..4]}
1 1237559471
2 1512586903
3 549321715
4 1944462360
5 1504720877
1 1674928609
2 736087708
3 550300367
4 1711440630
5 173433268
1 1402413772
2 140134983
3 1398882876
4 2121339685
5 646999364
1 1018242756
2 1245974822
3 30996024
4 422514801
5 1563882282
1 1960316396
2 385430188
3 1153795493
4 1257254738
5 1755023305
PS C:\Users\takk\aaa>

PowerShell tail代替

PowerShellでtailと同じようなことをやって見ます。

まずは実験用ファイルを作成。

PS C:\Users\takk\aaa> 1..5 | %{$fname="file$_.txt";1..10 | %{$r=random; "$_ $r" >> $fname}}
PS C:\Users\takk\aaa>

各ファイルのtail -3です。

PS C:\Users\takk\aaa> gci -name | %{"===$_===";(gc $_)[-3..-1]}
===file1.txt===
8 327987977
9 331443027
10 1762377467
===file2.txt===
8 2018881294
9 1181652202
10 1522374748
===file3.txt===
8 335203631
9 1128505680
10 1292207698
===file4.txt===
8 479540247
9 529618067
10 1633330444
===file5.txt===
8 1983671095
9 522046106
10 2118231412
PS C:\Users\takk\aaa>

とまあ、tailの代替として配列アクセスで簡単に書けてしまうのですが、tailやheadといったコマンドって、そのコマンド名だからこそいい、と思っています。
ですので、headはhead、tailはtailコマンドとして使いたいです。

ということで関数化してみます。関数といってもフィルタとして定義するのでfunctionの代わりにfilterを使います。
まずはheadから。

PS C:\Users\takk\aaa> filter head(){
>> begin{$cnt=5}
>> process{$cnt--;if($cnt -ge 0){ $_ }}
>> }
>>
PS C:\Users\takk\aaa> 1..10
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk\aaa> 1..10 | head
1
2
3
4
5
PS C:\Users\takk\aaa>

次はtail。すべて読み込んでからでないと末尾が取得できないので面倒です。

PS C:\Users\takk\aaa> filter tail(){
>> begin{$arr=@()}
>> process{$arr += $_}
>> end{$arr[-5..-1]}
>> }
>>
PS C:\Users\takk\aaa> 1..10
1
2
3
4
5
6
7
8
9
10
PS C:\Users\takk\aaa> 1..10 | tail
6
7
8
9
10
PS C:\Users\takk\aaa>

できました。

PowerShell wc代替

wcの-Lオプションで、ファイル中の最大桁数を求めることができますが、PowerShellでもやってみます。

実験用としてランダムな桁数のファイルを生成してみます。PowerShellでは文字列 * 数字で、繰り返し文字列が生成されるので、これを利用します。

PS C:\Users\takk\aaa> "A"*10
AAAAAAAAAA
PS C:\Users\takk\aaa>
function test($fname){
  1..10 | %{
    $r=$(random) % 50
    $s = "+"*$r
    $s >> $fname
  }
}
PS C:\Users\takk\aaa> 1..10 | %{ test "file$_.txt"}

実験用ファイルが10個できました。

PS C:\Users\takk\aaa> ls -name
file1.txt
file10.txt
file2.txt
file3.txt
file4.txt
file5.txt
file6.txt
file7.txt
file8.txt
file9.txt
PS C:\Users\takk\aaa>

中身を確認してみましょう。

PS C:\Users\takk\aaa> gc file1.txt
+++
++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++
+++++++++++++
+++++++++++++++++++++
+
++++++++++++++++++++++++++++++++++++++++
PS C:\Users\takk\aaa>

では最大桁数を求める関数を作ってみます。

function wc-L($fname){
  $colmax = 0
  gc $fname | %{
    $col = $_.Length
    if($colmax -lt $col){
      $colmax = $col
    }
  }
  $colmax
}

全ファイルをwc-Lにかければ、各ファイルの最大桁数が求められます。

PS C:\Users\takk\aaa> gci -name | % { "$_ "+$(wc-L $_) }
file1.txt 48
file10.txt 47
file2.txt 40
file3.txt 39
file4.txt 47
file5.txt 42
file6.txt 48
file7.txt 49
file8.txt 43
file9.txt 42
PS C:\Users\takk\aaa>

PowerShell paste代替

テキストを左右に並べるpasteコマンドと同じようなことを、PowerShellでやってみます。

PS C:\Users\takk\aaa> function paste($file1,$file2){
>>   $a=gc $file1
>>   $b=gc $file2
>>
>>   $diffs = $a.Count - $b.Count
>>
>>   if($diffs -ge 0){
>>     $rows=$diffs
>>       foreach ($m in 1..$rows){
>>       $b += ""
>>     }
>>   }else{
>>     $rows = 0-$diffs
>>     foreach ($m in 1..$rows){
>>       $a += ""
>>     }
>>   }
>>
>>   $row=0
>>   foreach($line in $a){
>>     "{0,-20} {1,-20}" -f $line,$b[$row]
>>     $row++
>>   }
>> }
>>
PS C:\Users\takk\aaa>
PS C:\Users\takk\aaa> paste file1.txt file2.txt
1 1661730330         1 610265905
2 1571784754         2 1622555713
3 499865609          3 1346965944
4 1736300608         4 1186718975
5 1321082403         5 1442458485
6 1625347902         6 124016226
7 628097727          7 1040899495
8 1692476762         8 1978768203
                     9 1012849703
                     10 1153033523
PS C:\Users\takk\aaa> paste file2.txt file1.txt
1 610265905          1 1661730330
2 1622555713         2 1571784754
3 1346965944         3 499865609
4 1186718975         4 1736300608
5 1442458485         5 1321082403
6 124016226          6 1625347902
7 1040899495         7 628097727
8 1978768203         8 1692476762
9 1012849703
10 1153033523
PS C:\Users\takk\aaa> paste file2.txt file3.txt
1 610265905          1 838864522
2 1622555713         2 1522354711
3 1346965944         3 1489263227
4 1186718975         4 1695805405
5 1442458485         5 850068224
6 124016226          6 1005360951
7 1040899495         7 1631362594
8 1978768203         8 321183274
9 1012849703         9 1578353004
10 1153033523        10 1338697134
PS C:\Users\takk\aaa>

PowerShell 必要なファイルだけTreeコピー

フォルダ内の特定ファイルを、コピーしてみましょう。
元のフォルダの構成です。

PS C:\Users\takk\aaa> tree /f
フォルダー パスの一覧
ボリューム シリアル番号は 0000005E C288:F709 です
C:.
└─dir1
    ├─1295
    │  └─1272
    │      └─13
    │              file6.txt
    │
    ├─1598
    │  └─7997
    │      └─42
    │              file3.txt
    │
    ├─1689
    │  └─0633
    │      └─86
    │              file4.txt
    │
    ├─1955
    │  └─2146
    │      └─92
    │              file7.txt
    │
    ├─2310
    │  └─6227
    │      └─6
    │              file1.txt
    │
    ├─2602
    │  └─6181
    │      └─6
    │              file5.txt
    │
    ├─4748
    │  └─0337
    │          file10.txt
    │
    ├─4829
    │  └─4788
    │      └─5
    │              file9.txt
    │
    ├─6104
    │  └─6524
    │      └─2
    │              file8.txt
    │
    └─6827
        └─0945
            └─3
                    file2.txt

PS C:\Users\takk\aaa>

特定のファイルがあるディレクトリのみ抽出してみます。

PS C:\Users\takk\aaa> gci dir1 -name -re -i file[135].txt | % { $_ -replace "[^\\]*$","" }
1598\7997\42\
2310\6227\6\
2602\6181\6\
PS C:\Users\takk\aaa>

抽出したディレクトリをコピーします。

PS C:\Users\takk\aaa> gci dir1 -name -re -i file[135].txt | % { $needs=$_ -replace "[^\\]*$","";mkdir "dir2\$needs" | ou
t-null }
PS C:\Users\takk\aaa>

コピーできたようです。

PS C:\Users\takk\aaa> gci dir2 -name -re
1598
2310
2602
1598\7997
1598\7997\42
2310\6227
2310\6227\6
2602\6181
2602\6181\6
PS C:\Users\takk\aaa>

削除してもう一度。

PS C:\Users\takk\aaa> rm dir2 -re
PS C:\Users\takk\aaa>

ディレクトリをコピー後、ファイルもコピーします。

PS C:\Users\takk\aaa> gci dir1 -name -re -i file[135].txt | % { $needs=$_ -replace "[^\\]*$","";mkdir "dir2\$needs" | ou
t-null;copy "dir1\$_" "dir2\$_" }

コピーできたか確認しましょう。

PS C:\Users\takk\aaa> gci dir2 -name -re
1598
2310
2602
1598\7997
1598\7997\42
1598\7997\42\file3.txt
2310\6227
2310\6227\6
2310\6227\6\file1.txt
2602\6181
2602\6181\6
2602\6181\6\file5.txt
PS C:\Users\takk\aaa>

Compare-Objectでパスが一致しているか確認します。

PS C:\Users\takk\aaa> $dir1=gci dir1 -name -re -file
PS C:\Users\takk\aaa> $dir2=gci dir2 -name -re -file
PS C:\Users\takk\aaa> diff $dir1 $dir2

InputObject                                                 SideIndicator
-----------                                                 -------------
1295\1272\13\file6.txt                                      <=
1689\0633\86\file4.txt                                      <=
1955\2146\92\file7.txt                                      <=
4748\0337\file10.txt                                        <=
4829\4788\5\file9.txt                                       <=
6104\6524\2\file8.txt                                       <=
6827\0945\3\file2.txt                                       <=


PS C:\Users\takk\aaa>

合ってそうです。

ではコピー処理を関数化します。

function fcopy($srcdir, $destdir,$pattern){
	gci $srcdir -name -re -i $pattern | % {
		$needs=$_ -replace "[^\\]*$","";mkdir -force "$destdir\$needs" | out-null
		copy "$srcdir\$_" "$destdir\$_"
	}
}

fcopyという関数名にしました。使ってみましょう。

PS C:\Users\takk\aaa> dir -name
dir1

dir1しかないことを確認して、fcopyを実行します。

PS C:\Users\takk\aaa> fcopy dir1 dir2 file[1-5].txt
PS C:\Users\takk\aaa> dir -name
dir1
dir2
PS C:\Users\takk\aaa>

dir2ができました。中身を確認します。

PS C:\Users\takk\aaa> gci dir2 -name -re -file
1598\7997\42\file3.txt
1689\0633\86\file4.txt
2310\6227\6\file1.txt
2602\6181\6\file5.txt
6827\0945\3\file2.txt
PS C:\Users\takk\aaa>

file1.txt~5.txtまでコピーされていますね。 dir1と比較して確認します。

PS C:\Users\takk\aaa> $dir1=gci dir1 -name -re -file
PS C:\Users\takk\aaa> $dir2=gci dir2 -name -re -file
PS C:\Users\takk\aaa> diff $dir1 $dir2

InputObject                                                 SideIndicator
-----------                                                 -------------
1295\1272\13\file6.txt                                      <=
1955\2146\92\file7.txt                                      <=
4748\0337\file10.txt                                        <=
4829\4788\5\file9.txt                                       <=
6104\6524\2\file8.txt                                       <=


PS C:\Users\takk\aaa>

目的のファイルの差は出なかったので合ってますね。

PowerShell GUIのテンプレを作っておく

PowerShellでGUIを使うためのテンプレです。めったにGUIを使わない私ですが、たまに他人へ配布する際、コマンドで渡すと不親切なので、GUI化して渡したりします。
その時に、いちいちGUIのためにプログラミングしていると時間がかかってしまうので、先にテンプレを用意しておきます。

このようなテンプレートを作っておいて、

[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$f=New-Object System.Windows.Forms.Form
$f.Text="PowerShell GUI"

$label=New-Object System.Windows.Forms.Label
$label.Location=New-Object System.Drawing.Point(5,30)
$label.Size=New-Object System.Drawing.Size(70,20)
$label.Text="LABEL"

$textbox1=New-Object System.Windows.Forms.TextBox
$textbox1.Location=New-Object System.Drawing.Point(75,30)
$textbox1.Size=New-Object System.Drawing.Size(110,20)
$textbox1.Text="DEFAULT TEXT"

$button=New-Object System.Windows.Forms.Button
$button.Location=New-Object System.Drawing.Point(210,30)
$button.Size=New-Object System.Drawing.Size(70,20)
$button.Text="PUSH"
$button.Add_Click({$textbox2.Text+=$textbox1.Text+"`r`n"})

$textbox2=New-Object System.Windows.Forms.TextBox
$textbox2.Location=New-Object System.Drawing.Point(5,60)
$textbox2.Size=New-Object System.Drawing.Size(295,200)
$textbox2.MultiLine=$true

$f.Controls.Add($label)
$f.Controls.Add($textbox1)
$f.Controls.Add($button)
$f.Controls.Add($textbox2)

$f.ShowDialog()

配布時にその時の気分でカスタマイズして渡します。

ボタンをクリックする度に、小さいテキストボックスに入力した内容が、大きなテキストボックスに追記されます。

PowerShell 日付時刻の計算

PowerShellのGet-Dateコマンドでも時刻までわかるようになっています。ということで、今回は、Get-Dateを使います。

PowerShell 現在の日付時刻を取得する

PS C:\Users\takk> get-date

2017年8月18日 19:48:03


Get-DateのAddMonthsメソッドで、日付の加算ができます。

PS C:\Users\takk> (get-date).addMonths(1)

2017年9月18日 19:48:25


PS C:\Users\takk>

一カ月後の9月18日になってはいますが、さきほどは19:48:03だったのに、
今回は、19:48:25になっています。時間は刻々と過ぎていきますから仕方ありませんね。
一度変数に格納してからメソッドを使ってみましょう。

PS C:\Users\takk> $a=get-date
PS C:\Users\takk> $a

2017年8月18日 19:48:42


PS C:\Users\takk> $a.addMonths(1)

2017年9月18日 19:48:42


PS C:\Users\takk>

PowerShell 任意の日付時刻で計算

現在の日付時刻ではなく、自由に決めたい場合は、Get-Dateの引数に日付や時刻を指定します。
では日付の指定をしてみます。

PS C:\Users\takk> get-date "2010/1/1"

2010年1月1日 0:00:00


PS C:\Users\takk>

次は日付と時刻。

PS C:\Users\takk> get-date "2010/1/1 10:00:00"

2010年1月1日 10:00:00


PS C:\Users\takk>

同じように変数に格納して、日付時刻を計算するメソッドを使うことができます。

PS C:\Users\takk> $a=get-date "2010/1/1 10:00:00"
PS C:\Users\takk> $a.addmonths(3)

2010年4月1日 10:00:00


PS C:\Users\takk>
PS C:\Users\takk> $a.adddays(100)

2010年4月11日 10:00:00


PS C:\Users\takk>

PowerShell 指定した2つの日付時刻の差分

差を求めるのは単純に引き算するだけです。

PS C:\Users\takk> $b=$a.adddays(100)
PS C:\Users\takk> $b

2010年4月11日 10:00:00


PS C:\Users\takk>

結果はいろんな単位で表示されます。これは結構便利です。

PS C:\Users\takk> $b - $a


Days              : 100
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 86400000000000
TotalDays         : 100
TotalHours        : 2400
TotalMinutes      : 144000
TotalSeconds      : 8640000
TotalMilliseconds : 8640000000



PS C:\Users\takk>

PowerShell ExcelファイルはPDFにしてからコンビニへ

Excel、PDF変換です。もちろんExcelの操作だけでPDFにできますね。PDF形式を選ぶだけです。

簡単にPDFファイルが作成されました。

Excelファイルの方がサイズは小さいようです。

では、PowerShellでも試してみましょう。

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$wkbk = $excel.Workbooks.Open("C:/Users/takk/Desktop/Book1.xlsx")
$sheet = $wkbk.Worksheets.Item(1)
$sheet.ExportAsFixedFormat(0, "C:/Users/takk/Desktop/Book2.pdf")

手動でPDF保存したファイルBook1.pdfとスクリプトで保存したファイルBook2.pdfはほぼサイズが同じでした。

Book1.xlsxはExcel開っきぱなしになるので、閉じるのでれば、
$wkbk.Close($false)
$excel.Quit()
を実行すればよいです。

PowerShell UbuntuでPowerShellを使う

UbuntuでPowerShellを使ってみます。
(LinuxでもPowerShellが使えることをつい昨日知りました)

PowerShellをUbuntuにインストール

PowerShellは、githubにレポジトリ(https://github.com/PowerShell)が公開されています。

githubレポジトリのpowershellをクリックします。

画面をREADMEが現れるまでスクロールさせます。今回はUbuntu 16.04にインストールするので、Ubuntu 16.04 のdebボタンを右クリックしてURLをコピーしておきます。

wgetがインストールされてなければ用意しておきます。

root@ubu16:~# apt-get install wget

インストールしたwgetを使って、PowerShellのgitレポジトリからdebファイルを取得します。さきほどURLをコピーしましたので、wgetのみ入力して、URLはペーストすればよいです。

root@ubu16:~# wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.5/powershell_6.0.0-beta.5-1ubuntu1.16.04.1_amd64.deb

インストール方法は、README.mdに書いてあるとおり。以下のようにコマンドを実行すればよいです。

root@ubu16:~# dpkg -i powershell_6.0.0-beta.5-1ubuntu1.16.04.1_amd64.deb
root@ubu16:~# apt-get install -f

PowerShellをUbuntuで使う

では使ってみましょう。

takk@ubu16:~$ powershell
PowerShell v6.0.0-beta.5
Copyright (C) Microsoft Corporation. All rights reserved.

PS /home/takk> 1..10 | %{random}
1480426006
1120022630
815938875
1939085862
1470613838
1781857308
1396863669
1899772878
701530902
1871527399
PS /home/takk>

感動です。

コマンドの連携もできそうです。

PS /home/takk> 1..5 | xargs
1 2 3 4 5
PS /home/takk>

LinuxでPowerShell 1..5とseq 5は異なるのか

bashだけでは長くなるコマンドも、PowerShellを使うと簡潔に書けるメリットがあります。普段私がよく使うseqコマンドを、PowerShellで表現するとどのような違いがあるでしょうか、確認してみます。

PowerShellで数列を生成

とても簡単にかけてしまいます。2掛けると、各要素が2倍になります。

PS /home/takk> 1..5
1
2
3
4
5
PS /home/takk> 1..5 | %{$_ * 2}
2
4
6
8
10
PS /home/takk>

seqコマンドで数列を生成

こちらは、各要素に2を書けると同じ数字が2つ並ぶだけです。

PS /home/takk> seq 5
1
2
3
4
5
PS /home/takk> seq 5 | %{$_ * 2}
11
22
33
44
55
PS /home/takk>

なぜ結果が違うのでしょうか。一度変数に格納してみましょう。

それぞれの変数の型

変数に格納するのも、バッククォート不要です。PowerShell便利だなあと思います。

PS /home/takk> $a=1..5
PS /home/takk> $b=seq 5
PS /home/takk>

これで格納できました。

念のため、それぞれを実行してみます。

PS /home/takk> $a
1
2
3
4
5
PS /home/takk> $b
1
2
3
4
5
PS /home/takk>

間違いないですね。

次に各変数の型を調べます。

PS /home/takk> $a.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS /home/takk> $b.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS /home/takk>

両方変数ともオブジェクトですので、メソッドが使えます。GetType()で型が取得できます。ともにArrayってことです。ここまでは一致してますね。

各要素の型

では各要素の型をGetType()で取得してみましょう。

PS /home/takk> $a | %{$_.gettype()}

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType
True     True     Int32                                    System.ValueType


PS /home/takk> $b | %{$_.gettype()}

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object
True     True     String                                   System.Object
True     True     String                                   System.Object
True     True     String                                   System.Object
True     True     String                                   System.Object


PS /home/takk>

1..5の方は、Int32。seq 5の方は、Stringが返ってきました。
seq 5の戻り値は、元々標準出力を使ってますので、文字列(String)になるのは当たり前のように思えます。seq 5を数値として計算したい場合は、以下のようにキャストしてから使用します。

PS /home/takk> seq 5 | %{[Int32]$_ *2}
2
4
6
8
10
PS /home/takk>

Unix/Linuxの基本的なコマンドが、PowerShellによって脅かされそうな気がします。

BashかPowerShellか

LinuxでPowerShellが使えるなんて知ると、BashかPowerShellどちらを常用するシェルとすべきか迷います。今回はよく使う操作を行い、比べてみます。

ファイルの行数を求める

Bashの場合(wcを使う場合)

takk@ubu16:~/aaa$ wc -l < /usr/share/dict/words
99171
takk@ubu16:~/aaa$

PowerShellの場合

takk@ubu16:~/aaa$ powershell
PowerShell v6.0.0-beta.5
Copyright (C) Microsoft Corporation. All rights reserved.

PS /home/takk/aaa> (get-content /usr/share/dict/words).count
99171
PS /home/takk/aaa>

指定行のみ抽出する

Bashの場合(sedを使う場合)

takk@ubu16:~/aaa$ sed -ne 20000p < /usr/share/dict/words
armlet
takk@ubu16:~/aaa$

PowerShellの場合

PS /home/takk/aaa> (get-content /usr/share/dict/words)[19999]
armlet
PS /home/takk/aaa>

ファイル先頭の表示

Bashの場合(headを使う場合)

takk@ubu16:~/aaa$ head /usr/share/dict/words
A
A's
AA's
AB's
ABM's
AC's
ACTH's
AI's
AIDS's
AM's
takk@ubu16:~/aaa$

PowerShellの場合

PS /home/takk/aaa> (get-content /usr/share/dict/words)[0..9]
A
A's
AA's
AB's
ABM's
AC's
ACTH's
AI's
AIDS's
AM's
PS /home/takk/aaa>

ファイル末尾の表示

Bashの場合(sedを使う場合)

takk@ubu16:~/aaa$ tail /usr/share/dict/words
elan's
emigre
emigre's
emigres
epee
epee's
epees
etude
etude's
etudes
takk@ubu16:~/aaa$

PowerShellの場合

PS /home/takk/aaa> (get-content /usr/share/dict/words)[-10..-1]
elan's
emigre
emigre's
emigres
epee
epee's
epees
etude
etude's
etudes
PS /home/takk/aaa>

この比較結果だけだと、PowerShellのメリットといえば、周辺コマンドがいらないことぐらいですね。

文字列部分抽出

BashかPowerShellどちらを常用するか悩みの続きで、今回は文字列の抽出を比較します。

何番目の文字取得

Bash

takk@ubu16:~/aaa$ a=HELLO
takk@ubu16:~/aaa$ echo ${a:0:1}
H
takk@ubu16:~/aaa$

Bashのこの表現好きですが、分かり難いですねえ。

PowerShell

PS /home/takk/aaa> $a="HELLO"
PS /home/takk/aaa> $a[0]
H
PS /home/takk/aaa>

直観で使える気がします。

部分文字列取得

Bash

takk@ubu16:~/aaa$ a=HELLO
takk@ubu16:~/aaa$ echo ${a:1:3}
ELL
takk@ubu16:~/aaa$

Bashにだけ慣れてしまえば、これはこれで使えるようになります。

PowerShell

PS /home/takk/aaa> $a="HELLO"
PS /home/takk/aaa> $a.substring(1,3)
ELL
PS /home/takk/aaa>

やはり、直観で使えますね。

文字列トリム

Bash

takk@ubu16:~$ a="   HELLO   "
takk@ubu16:~$ echo $a
HELLO
takk@ubu16:~$

Bashではechoするだけで空白削除してくれます。
空白入りの元の文字列を表示するには、””で囲みます。

takk@ubu16:~$ echo "$a"
   HELLO
takk@ubu16:~$

PowerShell

PS /home/takk> $a="   HELLO   "
PS /home/takk> $a
   HELLO
PS /home/takk> $a.Trim()
HELLO
PS /home/takk>

PowerShellはトリムするためのメソッドがあります。
どちらが便利かというと、慣れ、でしょうか。

文字列置換

BashとPowerShellの比較の文字列置換です。

文字列の長さ

Bash

takk@ubu16:~/aaa$ echo ${#a}
5
takk@ubu16:~/aaa$

PowerShell

PS /home/takk/aaa> $a="HELLO"
PS /home/takk/aaa> $a.Length
5
PS /home/takk/aaa>

文字列の置換

Bash

takk@ubu16:~$ a=HELLO
takk@ubu16:~$ echo ${a//L/E}
HEEEO
takk@ubu16:~$

PowerShell

PS /home/takk> $a="HELLO"
PS /home/takk> $a-replace("L","E")
HEEEO
PS /home/takk>

文字列の分割

Bash

takk@ubu16:~$ a=192.168.1.10
takk@ubu16:~$ b=(${a//./ })
takk@ubu16:~$ echo ${b[1]}
168
takk@ubu16:~$

PowerShell

PS /home/takk> $a="192.168.1.10"
PS /home/takk> $a.Split(".")[1]
168
PS /home/takk>

文字のインデックス(最初)

Bash

takk@ubu16:~$ grep -bo L <<< $a | sed -ne "1s/^\(.*\):.*$/\1/p"
2
takk@ubu16:~$

無理やりです。

PowerShell

PS /home/takk> $a="HELLO"
PS /home/takk> $a.IndexOf("L")
2
PS /home/takk>

文字のインデックス(最後)

Bash

takk@ubu16:~$ grep -bo L <<< $a | tac | sed -ne "1s/^\(.*\):.*$/\1/p"
3
takk@ubu16:~$

tacをてこ入れしただけです。
grepはこのように文字列中のインデックスをすべて表示してくれます。

takk@ubu16:~$ grep -bo L <<< $a
2:L
3:L
takk@ubu16:~$

PowerShell

PS /home/takk> $a="HELLO"
PS /home/takk> $a.LastIndexOf("L")
3
PS /home/takk>

Bashだと文字列の中まで踏み込もうと思うと、別のコマンドの力を借りるしかないですが、PowerShellはメソッドがあるでの楽チンです。

コマンド履歴

BashとPowerShellの比較で、ヒストリーです。

コマンド履歴の表示

Bash
Bashのコマンド履歴はご存じhistoryコマンドです。

takk@ubu16:~$ echo {1..5}
1 2 3 4 5
takk@ubu16:~$ echo {6..10}
6 7 8 9 10
takk@ubu16:~$ echo {11..15}
11 12 13 14 15
takk@ubu16:~$ history
    1  echo {1..5}
    2  echo {6..10}
    3  echo {11..15}
    4  history
takk@ubu16:~$

PowerShell
PowerShellは、hだけでコマンド履歴が表示されます。

PS /home/takk> 1..5
1
2
3
4
5
PS /home/takk> 6..10
6
7
8
9
10
PS /home/takk> 11..15
11
12
13
14
15
PS /home/takk> h

  Id CommandLine
  -- -----------
   1 1..5
   2 6..10
   3 11..15


PS /home/takk>

コマンド履歴のクリア

Bash

takk@ubu16:~$ history -c
takk@ubu16:~$ history
    1  history
takk@ubu16:~$

PowerShell

PS /home/takk> clhy
PS /home/takk> h

  Id CommandLine
  -- -----------
  11 clhy


PS /home/takk>

前回コマンドの再実行

Bash

takk@ubu16:~$ echo 1 2 3 4 5
1 2 3 4 5
takk@ubu16:~$ !!
echo 1 2 3 4 5
1 2 3 4 5
takk@ubu16:~$

PowerShell

PS /home/takk> echo 1 2 3 4 5
1
2
3
4
5
PS /home/takk> r
echo 1 2 3 4 5
1
2
3
4
5
PS /home/takk>

前回コマンドの最初の引数(トークン先頭)

Bash

takk@ubu16:~$ echo 1 2 3 4 5
1 2 3 4 5
takk@ubu16:~$ echo !^
echo 1
1
takk@ubu16:~$

PowerShell

PS /home/takk> echo $^
echo
PS /home/takk>

PowerShellではトークンの最初なので、コマンド自身を含めてしまいます。

前回コマンドの最後の引数(トークン末尾)

Bash

takk@ubu16:~$ echo 1 2 3 4 5
1 2 3 4 5
takk@ubu16:~$ echo !$
echo 5
5
takk@ubu16:~$

PowerShell

PS /home/takk> echo 1 2 3 4 5
1
2
3
4
5
PS /home/takk> echo $$
5
PS /home/takk>

ヒストリーはBashに分がありそうです。

重複検出(uniq)

uniqコマンドについて、BashとPowerShellを比べます。

0~2のランダムな整数を10個並べたテキストファイルを用意します。

takk@ubu16:~/aaa$ for i in {1..10};do expr $RANDOM % 3;done > src.txt
takk@ubu16:~/aaa$

このような内容になりました。

takk@ubu16:~/aaa$ cat src.txt
2
1
0
0
2
2
0
1
1
0
takk@ubu16:~/aaa$

Bashの場合
uniqコマンドの引数にテキストファイルを指定するとこのような結果になります。

takk@ubu16:~/aaa$ uniq src.txt
2
1
0
2
0
1
0
takk@ubu16:~/aaa$

uniqコマンドは前の行が今の行と一致しているかどうかしか見ないので、
最初にsortしておいてから使います。

takk@ubu16:~/aaa$ sort src.txt | uniq
0
1
2
takk@ubu16:~/aaa$

PowerShellの場合
PowerShellの場合も同じで、sortしないとこのようになります。

PS /home/takk/aaa> get-content src.txt | get-unique
2
1
0
2
0
1
0
PS /home/takk/aaa>

Bashと同じようにsortが必要ですが、コマンドレットがやたら長いです。

PS /home/takk/aaa> get-content src.txt | sort-object | get-unique
0
1
2
PS /home/takk/aaa>

Get-Content、Sort-Object、Get-Uniqueのエイリアスはそれぞれ、
gc、sort、guですが、
sortが/usr/bin/sortと名前がかぶるので、いっそLinux側のコマンドをこのように使ってしまった方がよいです。

PS /home/takk/aaa> cat src.txt | sort | gu
0
1
2
PS /home/takk/aaa>

ここまできたら、uniqを無理やりPowerShellで実現する必要なない気もします。uniqしたい場合は、Bashに分がありそうです。

乱数は%RANDOM%よりもPowerShellが楽

Windowsでランダムです。

Windowsプロンプトで、%RANDOM%で簡単に乱数表示できてしまいますが、

C:\Users\takk>echo %RANDOM%
20306

C:\Users\takk>

これをたくさん実行しようとしてforで囲んでも、

C:\Users\takk>for /l %i in (1,1,10);do @echo %RANDOM%
10092
10092
10092
10092
10092
10092
10092
10092
10092
10092

C:\Users\takk>

毎回同じ乱数が表示されて使えません。

そもそもPowerShellがあるのに、わざわざ%RANDOM%を使う必要もないかと思います。

C:\Users\takk>powershell "1..10 | % {random}"
1872896388
1446547913
2066583986
514197345
1982365520
1917412136
498497519
946024933
454851733
822820932

C:\Users\takk>

Get-Contentで[]を含むファイル名が使えない

今までまったく気づきませんでしたが、ファイル名に[]を含むファイル名の場合、Get-Contentが使えない場合があるようです。

前回作ったファイルで確認してみます。

C:\Users\takk\tmp>powershell "gci -r -n -include *.*"
14\06\78\26\71\file_4.c
14\55\90\38\9\file_[5].c
19\51\76\09\09\tmp.bin
20\65\59\82\30\tmp.bin
51\55\94\62\2\tmp.bin
61\41\04\04\8\file_[6].c

C:\Users\takk\tmp>

[]が含まれるファイル名のファイルは、 file_[5].cとfile_[6].cです。

Get-Content(cat)してみます。

C:\Users\takk\tmp>powershell "gci -r -n -include *.* | %{echo $_;cat $_ }"
14\06\78\26\71\file_4.c
1     1406782671
2     1406782671
3     1406782671
4     1406782671
14\55\90\38\9\file_[5].c
cat : 指定されたパス 14\55\90\38\9\file_[5].c にオブジェクトが存在しないか、-In
clude または -Exclude パラメーターによってフィルターされています。
発生場所 行:1 文字:36
+ gci -r -n -include *.* | %{echo $_;cat $_ }
+                                    ~~~~~~
    + CategoryInfo          : ObjectNotFound: (System.String[]:String[]) [Get-
   Content], Exception
    + FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetCo
   ntentCommand

19\51\76\09\09\tmp.bin
1     1951760909
2     1951760909
3     1951760909
4     1951760909
20\65\59\82\30\tmp.bin
1     2065598230
2     2065598230
3     2065598230
4     2065598230
51\55\94\62\2\tmp.bin
1     515594622
2     515594622
3     515594622
4     515594622
61\41\04\04\8\file_[6].c
cat : 指定されたパス 61\41\04\04\8\file_[6].c にオブジェクトが存在しないか、-In
clude または -Exclude パラメーターによってフィルターされています。
発生場所 行:1 文字:36
+ gci -r -n -include *.* | %{echo $_;cat $_ }
+                                    ~~~~~~
    + CategoryInfo          : ObjectNotFound: (System.String[]:String[]) [Get-
   Content], Exception
    + FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetCo
   ntentCommand


C:\Users\takk\tmp>

file_[5].cとfile_[6].cだけエラーとなりました。

回避するには、[]を`でエスケープするか、Get-Contentのオプション-LiteralPathを使えば良いです。

C:\Users\takk\tmp>powershell "gci -r -n -include *.* | %{echo $_;cat -literalpat
h $_ }"
14\06\78\26\71\file_4.c
1     1406782671
2     1406782671
3     1406782671
4     1406782671
14\55\90\38\9\file_[5].c
1     145590389
2     145590389
3     145590389
4     145590389
19\51\76\09\09\tmp.bin
1     1951760909
2     1951760909
3     1951760909
4     1951760909
20\65\59\82\30\tmp.bin
1     2065598230
2     2065598230
3     2065598230
4     2065598230
51\55\94\62\2\tmp.bin
1     515594622
2     515594622
3     515594622
4     515594622
61\41\04\04\8\file_[6].c
1     614104048
2     614104048
3     614104048
4     614104048

C:\Users\takk\tmp>

きれいに表示されました。

PowerShellでstatもどきを作る

タイムスタンプは、オブジェクト(ファイル)が持っているプロパティで確認できます。
作成日時なら、CreationTime
最終アクセス日時なら、LastAccessTime
最終更新日時なら、LastWriteTime
を、以下のように表示させて値を見ることができます。

C:\Users\takk\tmp>echo HELLO > bb.txt

C:\Users\takk\tmp>powershell "(gp bb.txt).lastwritetime"

2018年6月16日 14:24:44



C:\Users\takk\tmp>

上記の時間は、dir /twと一致するでしょうか。

C:\Users\takk\tmp>dir /tw
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は B402-CA61 です

 C:\Users\takk\tmp のディレクトリ

2018/06/16  14:24    <DIR>          .
2018/06/16  14:24    <DIR>          ..
2018/06/16  14:24                 8 bb.txt
               1 個のファイル                   8 バイト
               2 個のディレクトリ  136,361,189,376 バイトの空き領域

C:\Users\takk\tmp>

同じ時間になりました。問題ないですね。

さて、各タイムスタンプをまとめて表示するための関数を作りましょう。
毎回powershellコマンドを実行するのも面倒なので、PowerShellに入ります。

C:\Users\takk\tmp>powershell
Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Users\takk\tmp>

各タイムスタンプを並べて表示する関数。

function stat($f){
(gp $f).creationtime
(gp $f).lastaccesstime
(gp $f).lastwritetime
}

上をPowerShellのコンソールに貼り付けると、関数が定義されます。
実行すると、

PS C:\Users\takk\tmp> function stat($f){
>> (gp $f).creationtime
>> (gp $f).lastaccesstime
>> (gp $f).lastwritetime
>> }
>>
PS C:\Users\takk\tmp> stat bb.txt

2018年6月16日 14:24:44
2018年6月16日 14:24:44
2018年6月16日 14:24:44


PS C:\Users\takk\tmp>

各タイムスタンプが表示されました。
見出しがないので、どの日時が何の日時なのか分かりにくいですね。
見出しをつけます。

function stat($f){
"作成         {0}" -f (gp $f).creationtime
"最終アクセス {0}" -f (gp $f).lastaccesstime
"最終更新     {0}" -f (gp $f).lastwritetime
}
PS C:\Users\takk\tmp> function stat($f){
>> "作成         {0}" -f (gp $f).creationtime
>> "最終アクセス {0}" -f (gp $f).lastaccesstime
>> "最終更新     {0}" -f (gp $f).lastwritetime
>> }
>>
PS C:\Users\takk\tmp> stat bb.txt
作成         2018/06/16 14:24:44
最終アクセス 2018/06/16 14:24:44
最終更新     2018/06/16 14:24:44
PS C:\Users\takk\tmp>

分かりやすくなりました。

さてLinuxのstatコマンドは、このような表示でした。

takk@deb9:~/tmp$ stat a
  File: a
  Size: 13              Blocks: 8          IO Block: 4096   通常ファイル
Device: 801h/2049d      Inode: 546991      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    takk)   Gid: ( 1000/    takk)
Access: 2018-06-14 20:48:37.280000000 +0900
Modify: 2018-06-14 20:47:55.256000000 +0900
Change: 2018-06-14 20:47:55.256000000 +0900
 Birth: -
takk@deb9:~/tmp$

今回作るstatもどきも、タイムスタンプだけでは寂しいので、ファイル名と、サイズぐらいは表示するように修正します。

ここで、オブジェクト(ファイル)に、どのようなプロパティが存在するか確認してみます。
get-memberで、各メンバが表示できますが、このうちのMemberTypeがPropertyとなっている項目がプロパティです。

PS C:\Users\takk\tmp> gp bb.txt | get-member

   TypeName: System.IO.FileInfo

Name                      MemberType     Definition
----                      ----------     ----------
Mode                      CodeProperty   System.String Mode{get=Mode;}
AppendText                Method         System.IO.StreamWriter AppendText()
CopyTo                    Method         System.IO.FileInfo CopyTo(string de...
Create                    Method         System.IO.FileStream Create()
CreateObjRef              Method         System.Runtime.Remoting.ObjRef Crea...
CreateText                Method         System.IO.StreamWriter CreateText()
Decrypt                   Method         void Decrypt()
Delete                    Method         void Delete()
Encrypt                   Method         void Encrypt()
Equals                    Method         bool Equals(System.Object obj)
GetAccessControl          Method         System.Security.AccessControl.FileS...
GetHashCode               Method         int GetHashCode()
GetLifetimeService        Method         System.Object GetLifetimeService()
GetObjectData             Method         void GetObjectData(System.Runtime.S...
GetType                   Method         type GetType()
InitializeLifetimeService Method         System.Object InitializeLifetimeSer...
MoveTo                    Method         void MoveTo(string destFileName)
Open                      Method         System.IO.FileStream Open(System.IO...
OpenRead                  Method         System.IO.FileStream OpenRead()
OpenText                  Method         System.IO.StreamReader OpenText()
OpenWrite                 Method         System.IO.FileStream OpenWrite()
Refresh                   Method         void Refresh()
Replace                   Method         System.IO.FileInfo Replace(string d...
SetAccessControl          Method         void SetAccessControl(System.Securi...
ToString                  Method         string ToString()
PSChildName               NoteProperty   System.String PSChildName=aa.txt
PSDrive                   NoteProperty   System.Management.Automation.PSDriv...
PSParentPath              NoteProperty   System.String PSParentPath=Microsof...
PSPath                    NoteProperty   System.String PSPath=Microsoft.Powe...
PSProvider                NoteProperty   System.Management.Automation.Provid...
Attributes                Property       System.IO.FileAttributes Attributes...
CreationTime              Property       datetime CreationTime {get;set;}
CreationTimeUtc           Property       datetime CreationTimeUtc {get;set;}
Directory                 Property       System.IO.DirectoryInfo Directory {...
DirectoryName             Property       string DirectoryName {get;}
Exists                    Property       bool Exists {get;}
Extension                 Property       string Extension {get;}
FullName                  Property       string FullName {get;}
IsReadOnly                Property       bool IsReadOnly {get;set;}
LastAccessTime            Property       datetime LastAccessTime {get;set;}
LastAccessTimeUtc         Property       datetime LastAccessTimeUtc {get;set;}
LastWriteTime             Property       datetime LastWriteTime {get;set;}
LastWriteTimeUtc          Property       datetime LastWriteTimeUtc {get;set;}
Length                    Property       long Length {get;}
Name                      Property       string Name {get;}
BaseName                  ScriptProperty System.Object BaseName {get=if ($th...
VersionInfo               ScriptProperty System.Object VersionInfo {get=[Sys...


PS C:\Users\takk\tmp>

さきほどのLastAccessTime等もありますね。

statもどきに追加したいのは、ファイル名と、サイズなので、該当するプロパティが、
Name、Lengthになります。

では、関数を作ります。

function stat($f){
"ファイル名   {0}" -f (gp $f).name
"サイズ       {0}" -f (gp $f).length
"作成         {0}" -f (gp $f).creationtime
"最終アクセス {0}" -f (gp $f).lastaccesstime
"最終更新     {0}" -f (gp $f).lastwritetime
}

貼り付けて、実行してみましょう。

PS C:\Users\takk\tmp> function stat($f){
>> "ファイル名   {0}" -f (gp $f).name
>> "サイズ       {0}" -f (gp $f).length
>> "作成         {0}" -f (gp $f).creationtime
>> "最終アクセス {0}" -f (gp $f).lastaccesstime
>> "最終更新     {0}" -f (gp $f).lastwritetime
>> }
>>
PS C:\Users\takk\tmp>
PS C:\Users\takk\tmp> stat bb.txt
ファイル名   bb.txt
サイズ       8
作成         2018/06/16 14:24:44
最終アクセス 2018/06/16 14:24:44
最終更新     2018/06/16 14:24:44
PS C:\Users\takk\tmp>

完成です。

コメント

タイトルとURLをコピーしました