PowerShell入門

コマンド&スクリプト

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

対象バージョンは以下です。

Contents

スポンサーリンク

PowerShell を始めるには

コマンドプロンプトのようなターミナルウィンドウは、
1. Winキー+R
2. 「ファイル名を指定して実行」で「powershell」と入力してEnterキー
で起動できます。

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

(起動時にバージョンアップを促すメッセージが表示されますが、本記事の対象バージョンが異なるのでそのまま使用します。)

簡単なコマンドを実行してみましょう。
write-output HELLO

write-outputの代わりに、echoと入力しても同じ結果になります。
echo HELLO

PowerShell エイリアス

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

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

エイリアス多すぎでした。

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

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

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

エイリアスの作成

新規エイリアスを作成するには、New-Aliasコマンドレットを使います。
Get-ChildItemのエイリアスには、dir、gci、lsがありますが、

これにLLも増やしてみましょう。

エイリアスにLLが増えていますね。

LLを実行してみます。大文字小文字どちらでも解釈されます。

PS C:\Users\taku\Desktop> ll -n
folder1
file1.txt
PS C:\Users\taku\Desktop>

エイリアスの削除

エイリアスの削除は、Remove-Aliasではなく、Remove-Itemを使います。

PowerShell 変数、関数

変数名と変数への代入

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

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

変数名だけ記述しても値の確認ができます。

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

定数、定数の削除

定数は、Set-Variable(エイリアス sv)の-OptionでReadOnlyを指定して定義します。Set-Variableの変数名を指定する-Nameオプションは省略できます。

PS C:\Users\taku> sv a -val 10 -op readonly
PS C:\Users\taku>
PS C:\Users\taku> $a
10
PS C:\Users\taku> $a=3
変数 a は読み取り専用または定数であるため、上書きできません。
発生場所 行:1 文字:1
+ $a=3
+ ~~~~
    + CategoryInfo          : WriteError: (a:String) [], SessionStateUnauthorizedAccessException
    + FullyQualifiedErrorId : VariableNotWritable

PS C:\Users\taku>

-Option ReadOnlyで定数にするとRemove-Item(エイリアス rv)で削除する場合は-Forceが必要になります。

PS C:\Users\taku> rv a -force
PS C:\Users\taku> $a
PS C:\Users\taku>

-Forceを使用しなかった場合はこのようにメッセージが表示されます。

PS C:\Users\taku> rv a
rv : 変数 a は定数または読み取り専用であるため、削除できません。変数が読み取り専用である場合は、Force オプションを指定
して操作を再試行してください。
発生場所 行:1 文字:1
+ rv a
+ ~~~~
    + CategoryInfo          : WriteError: (a:String) [Remove-Variable], SessionStateUnauthorizedAccessException
    + FullyQualifiedErrorId : VariableNotRemovable,Microsoft.PowerShell.Commands.RemoveVariableCommand

PS C:\Users\taku>

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>

Automatic変数

自動的に値が格納される変数です。

$^

$^には、前回実行した1番目の文字列が格納されます。

PS C:\Users\taku> echo AA BB CC DD
AA
BB
CC
DD
PS C:\Users\taku> echo $^
echo
PS C:\Users\taku>

$$

$$は、前回実行したコマンドの最後の文字列が格納されます。

PS C:\Users\taku> echo AA BB CC DD
AA
BB
CC
DD
PS C:\Users\taku> echo $$
DD
PS C:\Users\taku>

$HOME

$HOMEには、ホームディレクトリが格納されています。

PS C:\Users\taku> echo $HOME
C:\Users\taku
PS C:\Users\taku>

$_

$_は、現在のオブジェクトです。

PS C:\Users\taku> 1..5 | % { echo $_ }
1
2
3
4
5
PS C:\Users\taku>

$?

$?は、前回のコマンドの実行結果(TrueかFalse)が格納されます。
Trueのケース

PS C:\Users\taku> 1 + 3
4
PS C:\Users\taku> echo $?
True
PS C:\Users\taku>

Falseのケース

PS C:\Users\taku> 1 / 0
0 で除算しようとしました。
発生場所 行:1 文字:1
+ 1 / 0
+ ~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

PS C:\Users\taku> echo $?
False
PS C:\Users\taku>

$NULL $True $False

$NULLはNULLです。

PS C:\Users\taku> echo $NULL
PS C:\Users\taku>

$Trueは、True

PS C:\Users\taku> echo $true
True
PS C:\Users\taku>

$Falseは、これもそのまんま、False

PS C:\Users\taku> echo $false
False
PS C:\Users\taku>

$Args

$Argsには、引数が格納されます。

PS C:\Users\taku> function AA{
>> echo $args
>> }
PS C:\Users\taku> AA 1 2 3
1
2
3
PS C:\Users\taku>

0始まりです。

PS C:\Users\taku> function AA{
>> echo $args[1]
>> }
PS C:\Users\taku> AA 1 2 3
2
PS C:\Users\taku>

プロンプト変更

プロンプトを変更するには、prompt関数を定義します。

PS C:\Users\taku> function prompt {
>> "PS takk >"
>> }
PS takk >

PS1ファイルが実行できないとき

ps1という拡張子を付けたテキストファイルにPowerShellのスクリプトを記述しておけば、実行できるようになりますが、
以下のように実行に失敗する場合、

Get-ExecutionPolicyを確認してみてください。
以下のようにRestricted(スクリプト実行不可)になっていたり、AllSigned(すべてのps1に要署名)になっている場合は、実行できません。

管理者でPowerShellを実行して実行ポリシーを変更します。

PowerShellが起動したら、Set-ExecutionPolicy RemoteSigned実行

「はい」を選択します。

別途PowerShellを起動してGet-ExecutionPolicyを実行すると、RemoteSignedが確認できます。

即席でps1ファイルを作って実行してみてください。
実行するにはパスを明確にしないといけません。.¥がファイル名の頭に必要です。

PowerShell 制御

分岐 if

if(式){
TRUE処理
}else{
FALSE処理
}

他言語と同じく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桁で表示してみましょう。

foreach($i in 0..255){
  $s += "{0:X2} " -f $i
  if($i % 16 -eq 15){$s += "`r`n"}
}


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桁表示はこのように書くこともできます。

0..255 | %{$s=""}{$s += "{0:X2} " -f $_;if($_%16 -eq 15){$s+="`n"}}{echo $s}

文字列操作 置換、分割、長さ、位置取得、パディング他

文字列置換。Replaceメソッドの場合。

PS C:\Users\taku> $s  = "ABCDEFG"
PS C:\Users\taku> $s.replace("CDE","*****")
AB*****FG
PS C:\Users\taku>

Replaceメソッドは正規表現が使えません。

PS C:\Users\taku> $s.replace("A.*E","*****")
ABCDEFG
PS C:\Users\taku>

正規表現を使う場合は、-Replaceを使います。

PS C:\Users\taku> $s  = "ABCDEFG"
PS C:\Users\taku> $s.replace("A.*E","*****")
*****FG
PS C:\Users\taku>
PS C:\Users\takk> $s="HELLO"
PS C:\Users\takk> $s-replace("L","E")
HEEEO
PS C:\Users\takk>

文字列分割。
Splitメソッドの引数にセパレータを指定します。

PS C:\Users\takk> $a="192.168.1.10"
PS C:\Users\takk> $a.Split(".")[1]
168
PS C:\Users\takk>

-Splitの場合。

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>

関連記事はコチラ
ファイルパスと行番号から該当行抽出(PowerShell)

文字列長の取得。Lengthを使います。

PS C:\Users\taku> $s  = "ABCDEFG"
PS C:\Users\taku> $s.length
7
PS C:\Users\taku>

混同しやすいCountですが、配列要素数なので、この場合は1になります。

PS C:\Users\taku> $s.count
1
PS C:\Users\taku>

特定文字列を含んでいるか。

PS C:\Users\taku> $s  = "ABCDEFG"
PS C:\Users\taku> $s.contains("CD")
True
PS C:\Users\taku> $s.contains("CBD")
False
PS C:\Users\taku>

正規表現は使えません。

PS C:\Users\taku> $s.contains("C.*D")
False
PS C:\Users\taku>

文字列内の文字列の位置を取得するには、IndexOf、LastIndexOfメソッドを使います。

PS C:\Users\taku> $s = "ABCD ABCD ABCD"
PS C:\Users\taku> $s.indexof("AB")
0
PS C:\Users\taku> $s.indexof("AB",0)
0
PS C:\Users\taku> $s.indexof("AB",1)
5
PS C:\Users\taku> $s.indexof("AB",5)
5
PS C:\Users\taku> $s.indexof("AB",6)
10
PS C:\Users\taku> $s.lastindexof("AB")
10
PS C:\Users\taku>

PadLeft、PadRightは、文字列のパディングするメソッドです。

PS C:\Users\taku> $s="ABCD"
PS C:\Users\taku> $s.padleft(10)
      ABCD
PS C:\Users\taku> $s.padleft(10,"*")
******ABCD
PS C:\Users\taku> $s.padleft(3,"*")
ABCD
PS C:\Users\taku>
PS C:\Users\taku> $s.padright(10)
ABCD
PS C:\Users\taku> $s.padright(10,"*")
ABCD******
PS C:\Users\taku> $s.padright(3,"*")
ABCD
PS C:\Users\taku>

配列操作

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>

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

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

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

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

0..10 |% {mkdir $("dir_{0:X}" -f $(get-random)) | out-null}

フィルタ 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>

出力を捨てる 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と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 各コマンドレット

Get-ChildItem (ls gci dir) 一覧表示

Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [<CommonParameters>]
Get-ChildItem [[-Filter] <string>] [<CommonParameters>]

指定ディレクトリのファイルとディレクトリ一覧を表示します。

ls


lsコマンドは、元々はUnixで使われていたコマンドで、ただファイル名を表示する仕様が美しかったのですが、PowerShellのlsはなんだか情報が多いですね。

-Name 名前のみ

Get-ChildItemに-Nameを指定すれば、ファイル名、ディレクトリ名のみ表示することができます。
大文字小文字は区別しませんので、ls -nameでよいです。

ls -name


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

ls -n

-Recurse 再起的に取得

Linux(Unix)のfindコマンドのようにディレクトリ階層を追ってすべて表示する場合は、-Recurseオプションを使います。-rでよいです。-n(-Name)も併用します。
ディレクトリだけでなく、ファイルも一覧に表示されます。

ls -n -r

-Directory ディレクトリのみ

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

ls -n -r -di

-File ファイルのみ

ファイルのみの絞り込みは、-Fileオプションでできますが、-F等に省略不可です。

ls -n -r -File

Get-Item (gi) ファイル表示

Get-Item [-Path] <string[]> [<CommonParameters>]
Get-Item [<CommonParameters>]

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

このディレクトリ構成で確認します。

tree                                                                                          

Get-Item(gi)でディレクトリを指定。

gi dir1


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

一方、Get-ContentItem(ls)でディレクトリを指定すると、

ls dir1


Get-Item(gi)が、ディレクトリの表側が確認できたことに対して、Get-ChildItem(ls)では、ディレクトリの内側が確認できました。

Get-Itemでプロパティ取得

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

更新日時を表示。

(gi dir1).LastAccessTime


親ディレクトリを表示。

(gi dir1).Parent


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

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

gi dir1 | get-member

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

New-Item [-Path] <string[]> [<CommonParameters>]
New-Item [[-Path] <string[]>] [<CommonParameters>]

ディレクトリを作ってみましょう。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>

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

Set-Location [[-Path] <string>] [<CommonParameters>]
Set-Location [<CommonParameters>]

cdでカレントディレクトリを変更できます。

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

ls(Get-ChildItem)でディレクトリ内にファイルがあるか確認してみます。

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>

親ディレクトリへ

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>

ホームディレクトリへ

ホームディレクトリへ戻るには、cdに指定するパスを~にします。

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

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

Get-Content [-Path] <string[]> [<CommonParameters>]
Get-Content [<CommonParameters>]

本コマンドでファイルの内容が確認できます。

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

関連記事はコチラ
PowerShellでファイルダンプコマンドを作る

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

Move-Item [-Path] <string[]> [[-Destination] <string>] [<CommonParameters>]
Move-Item [[-Destination] <string>] [<CommonParameters>]

mvでファイル名が変更できます。

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

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

Remove-Item [-Path] <string[]> [<CommonParameters>]
Remove-Item [<CommonParameters>]

ファイルの削除コマンドです。

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

Join-Path パスの結合

Join-Path [-Path] <string[]> [ChildPath] <string> [<CommonParameters>]

実際にあるパスじゃなくてもよいです。

PS C:\Users\taku> join-path c:\aa bb.txt
c:\aa\bb.txt
PS C:\Users\taku>

Convert-Path 絶対パスへ変換

Convert-Path [-Path] <string[]> [<CommonParameters>]
Convert-Path [<CommonParameters>]

相対パス文字列を絶対パスに変換します。
以下ではa.txtは相対パスで表示されています。

PS C:\Users\taku\tmp> ls -n
a.txt
PS C:\Users\taku\tmp>

これをconvert-pathにかけると、絶対パスに変換されます。

PS C:\Users\taku\tmp> ls -n | convert-path
C:\Users\taku\tmp\a.txt
PS C:\Users\taku\tmp>

Get-Random 乱数

Get-Random [[-Maximum] <Object>] [<CommonParameters>]
Get-Random [-InputObject] <Object[]> [<CommonParameters>]

乱数が取得できるコマンドです。

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

Compress-Archive ZIP圧縮

Compress-Archive [-Path] <string[]> [-DestinationPath] <string> [<CommonParameters>]
Compress-Archive [-DestinationPath] <string> [<CommonParameters>]

ファイルをZIP圧縮するコマンドです。

適当にファイル作成しておいて、

PS C:\Users\taku\Desktop> cmd /c echo HELLO> test.txt
PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:23              7 test.txt


PS C:\Users\taku\Desktop>

7バイトのファイルができました。これを圧縮します。

ZIP圧縮するにはCompress-Archiveコマンドレットを使います。
-Pathオプション 圧縮するファイル名
-DestnationPath ZIPファイル名
を指定して実行します。

PS C:\Users\taku\Desktop> compress-archive -path test.txt -destinationpath a.zip
PS C:\Users\taku\Desktop> ls

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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:38            123 a.zip
-a----       2020/04/03     11:23              7 test.txt


PS C:\Users\taku\Desktop>

test.txtは7バイトなのに、圧縮したa.zipは123バイトになってしまいました。
ちょっと例が悪かったですね。もう少しサイズの大きいファイルにしてみます。

PS C:\Users\taku\Desktop> ls
PS C:\Users\taku\Desktop> 1..1000>test.txt
PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:54           9788 test.txt


PS C:\Users\taku\Desktop> compress-archive test.txt -dest a.zip                                    PS C:\Users\taku\Desktop> ls
PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:54           1940 a.zip
-a----       2020/04/03     11:54           9788 test.txt


PS C:\Users\taku\Desktop>

test.txtは9788バイト、圧縮したa.zipは1940バイトになりました。

Expand-Archive ZIP解凍

Expand-Archive [-Path] <string> [[-DestinationPath] <string>] [<CommonParameters>]
Expand-Archive [[-DestinationPath] <string>] [<CommonParameters>]

ZIPファイルを解凍するには、Expand-Archiveを使います。

-Pathオプション 解凍対象のZIPファイル(省略できます)
-DestinationPath 解凍先フォルダ

Compress-Archiveで作成したZIPファイルを解凍してみましょう。

-a----       2020/04/03     11:54           1940 a.zip
-a----       2020/04/03     11:54           9788 test.txt

test.txtが圧縮前のファイルで、a.zipが圧縮したファイルです。
a.zipをnewフォルダに解凍します。

PS C:\Users\taku\Desktop> expand-archive a.zip -dest new
PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/04/03     12:03                new
-a----       2020/04/03     11:54           1940 a.zip
-a----       2020/04/03     11:54           9788 test.txt


PS C:\Users\taku\Desktop>

newの中身をみてみます。

PS C:\Users\taku\Desktop> ls new


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:54           9788 test.txt


PS C:\Users\taku\Desktop>

解凍されてますね。

次はカレントディレクトリへ解凍してみたいと思います。
次は元々あったtest.txtファイルを消した状態にしておきます。

PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:54           1940 a.zip


PS C:\Users\taku\Desktop>

-DestinationPathには、.(ドット)を指定します。

PS C:\Users\taku\Desktop> expand-archive a.zip -dest .
PS C:\Users\taku\Desktop> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/04/03     11:54           1940 a.zip
-a----       2020/04/03     11:54           9788 test.txt


PS C:\Users\taku\Desktop>

カレントディレクトリにtest.txtが解凍されました。

Select-String (sls) 文字列検索

Select-String [-Pattern] <string[]> [-Path] <string[]> [<CommonParameters>]
Select-String [-Pattern] <string[]> [<CommonParameters>]

このコマンドレットでgrepができます。エイリアスはslsです。
まずは検索確認用データを作っておきます。

PS C:\Users\taku\Desktop> $r=1..5 | %{ get-random }
PS C:\Users\taku\Desktop> $r
77073845
1633293137
2001234929
1166330771
1126975556
PS C:\Users\taku\Desktop>

では文字列の検索をしてみましょう。引数で指定した文字列がある行を表示してくれます。

PS C:\Users\taku\Desktop> $r | sls 00

2001234929


PS C:\Users\taku\Desktop>

正規表現も使えます。”で囲って指定しましょう。

PS C:\Users\taku\Desktop> $r | sls '6.*3'

1633293137
1166330771


PS C:\Users\taku\Desktop>

Get-FileHash ハッシュ値取得

Get-FileHash [-Path] <string[]> [<CommonParameters>]
Get-FileHash [<CommonParameters>]

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>

その他のアルゴリズムも指定できます。

-Algorithm SHA1

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>

-Algorithm SHA384

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>

-Algorithm SHA512

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>

-Algorithm MACTripleDES

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>

-Algorithm RIPEMD160

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>

Get-Date 日付時刻の取得、計算

Get-Date [[-Date] <datetime>] [<CommonParameters>]

現在日時の取得や、計算が行えます。

現在日時を取得する

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>

任意の日時で計算

現在日時ではなく、自由に決めたい場合は、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>

指定した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>

Test-Path パス存在チェック

Test-Path [-Path] <string[]> [<CommonParameters>]
Test-Path [<CommonParameters>]

PS C:\Users\taku\tmp> ls


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


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/05/21     21:17             16 a.txt


PS C:\Users\taku\tmp> test-path a.txt
True
PS C:\Users\taku\tmp> test-path b.txt
False
PS C:\Users\taku\tmp>

Where-Object

Where-Object [-Property] <string> [[-Value] <Object>] [<CommonParameters>]
Where-Object [-FilterScript] <scriptblock> [<CommonParameters>]

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 応用編

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 パス区切り文字の変換

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

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 複数ファイルの置換

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 (diff) ファイル比較

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 その他

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 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>

完成です。

付録 エイリアス一覧

バージョン5.1でのget-aliasで表示された一覧です。

% -> ForEach-Object
? -> Where-Object
ac -> Add-Content
asnp -> Add-PSSnapin
cat -> Get-Content
cd -> Set-Location
CFS -> ConvertFrom-String
chdir -> Set-Location
clc -> Clear-Content
clear -> Clear-Host
clhy -> Clear-History
cli -> Clear-Item
clp -> Clear-ItemProperty
cls -> Clear-Host
clv -> Clear-Variable
cnsn -> Connect-PSSession
compare -> Compare-Object
copy -> Copy-Item
cp -> Copy-Item
cpi -> Copy-Item
cpp -> Copy-ItemProperty
curl -> Invoke-WebRequest
cvpa -> Convert-Path
dbp -> Disable-PSBreakpoint
del -> Remove-Item
diff -> Compare-Object
dir -> Get-ChildItem
dnsn -> Disconnect-PSSession
ebp -> Enable-PSBreakpoint
echo -> Write-Output
epal -> Export-Alias
epcsv -> Export-Csv
epsn -> Export-PSSession
erase -> Remove-Item
etsn -> Enter-PSSession
exsn -> Exit-PSSession
fc -> Format-Custom
fhx -> Format-Hex
fl -> Format-List
foreach -> ForEach-Object
ft -> Format-Table
fw -> Format-Wide
gal -> Get-Alias
gbp -> Get-PSBreakpoint
gc -> Get-Content
gci -> Get-ChildItem
gcm -> Get-Command
gcs -> Get-PSCallStack
gdr -> Get-PSDrive
ghy -> Get-History
gi -> Get-Item
gjb -> Get-Job
gl -> Get-Location
gm -> Get-Member
gmo -> Get-Module
gp -> Get-ItemProperty
gps -> Get-Process
gpv -> Get-ItemPropertyValue
group -> Group-Object
gsn -> Get-PSSession
gsnp -> Get-PSSnapin
gsv -> Get-Service
gu -> Get-Unique
gv -> Get-Variable
gwmi -> Get-WmiObject
h -> Get-History
history -> Get-History
icm -> Invoke-Command
iex -> Invoke-Expression
ihy -> Invoke-History
ii -> Invoke-Item
ipal -> Import-Alias
ipcsv -> Import-Csv
ipmo -> Import-Module
ipsn -> Import-PSSession
irm -> Invoke-RestMethod
ise -> powershell_ise.exe
iwmi -> Invoke-WMIMethod
iwr -> Invoke-WebRequest
kill -> Stop-Process
lp -> Out-Printer
ls -> Get-ChildItem
man -> help
md -> mkdir
measure -> Measure-Object
mi -> Move-Item
mount -> New-PSDrive
move -> Move-Item
mp -> Move-ItemProperty
mv -> Move-Item
nal -> New-Alias
ndr -> New-PSDrive
ni -> New-Item
nmo -> New-Module
npssc -> New-PSSessionConfigurationFile
nsn -> New-PSSession
nv -> New-Variable
ogv -> Out-GridView
oh -> Out-Host
popd -> Pop-Location
ps -> Get-Process
pushd -> Push-Location
pwd -> Get-Location
r -> Invoke-History
rbp -> Remove-PSBreakpoint
rcjb -> Receive-Job
rcsn -> Receive-PSSession
rd -> Remove-Item
rdr -> Remove-PSDrive
ren -> Rename-Item
ri -> Remove-Item
rjb -> Remove-Job
rm -> Remove-Item
rmdir -> Remove-Item
rmo -> Remove-Module
rni -> Rename-Item
rnp -> Rename-ItemProperty
rp -> Remove-ItemProperty
rsn -> Remove-PSSession
rsnp -> Remove-PSSnapin
rujb -> Resume-Job
rv -> Remove-Variable
rvpa -> Resolve-Path
rwmi -> Remove-WMIObject
sajb -> Start-Job
sal -> Set-Alias
saps -> Start-Process
sasv -> Start-Service
sbp -> Set-PSBreakpoint
sc -> Set-Content
select -> Select-Object
set -> Set-Variable
shcm -> Show-Command
si -> Set-Item
sl -> Set-Location
sleep -> Start-Sleep
sls -> Select-String
sort -> Sort-Object
sp -> Set-ItemProperty
spjb -> Stop-Job
spps -> Stop-Process
spsv -> Stop-Service
start -> Start-Process
sujb -> Suspend-Job
sv -> Set-Variable
swmi -> Set-WMIInstance
tee -> Tee-Object
trcm -> Trace-Command
type -> Get-Content
wget -> Invoke-WebRequest
where -> Where-Object
wjb -> Wait-Job
write -> Write-Output

コメント

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