hakeの日記

Windows環境でプログラミングの勉強をしています。

PowerShell - 正規表現

match演算子

変数$Matchesでマッチした文字列を取得できる。要素0がマッチした文字列、要素1以降が部分マッチ(キャプチャ)された文字列になる。
ただし最初にマッチしたものしか得られないみたい。

PS C:\> "abcde" -match "(.)(.)"
True
PS C:\> $Matches

Name            Value
----            -----
2               b
1               a
0               ab

PS C:\> $Matches[0]
ab

 
配列からマッチした要素を抜き出すこともできる様です。

PS C:\> @("one", "two", "three", "four", "five") -match "t"
two
three

 

regexオブジェクト

正規表現オブジェクトを使用するとマッチした全てが取得できる。

PS C:\> $re = New-Object regex("(.)(.)")     # $re = [regex]"(.)(.)"でも可
PS C:\> $m = $re.Matches("abcde")
PS C:\> $m

Groups   : {ab, a, b}
Success  : True
Captures : {ab}
Index    : 0
Length   : 2
Value    : ab

Groups   : {cd, c, d}
Success  : True
Captures : {cd}
Index    : 2
Length   : 2
Value    : cd

PS C:\> $m[1].Groups[1]

Success  : True
Captures : {c}
Index    : 2
Length   : 1
Value    : c

PS C:\> $m[1].Groups[1].Value
c
後方参照
PS C:\> $re = new-object regex( "(\d)(\d)(\d)(\d)" )
PS C:\> $re.replace("1234", "`$4`$3`$2`$1")
4321

PowerShell - 制御構造(条件分岐)

if

if(条件){
    文
} elseif(条件){
    文
} else {
    文
}

switch

switch($v){
  1          {文; break}
  2          {文; break}
  ($_ -ge 3) {文; break}
  default    {文; break}
}

# 正規表現 (-matchでないことに注意)
$s = "abcd"
switch -Regex($s){
    "^a"       {Write-Host 1;break} # aで始まる
    "bc"       {Write-Host 2;break} # bcを含む
    default    {Write-Host 3;break}
}

# ワイルドカード (-likeでないことに注意)
switch -Wildcard($s){
    "a*"       {Write-Host 1;break} # aで始まる
    "bc"       {Write-Host 2;break} # bcを含む
    default    {Write-Host 3;break}
}

PowerShell - 制御構造(ループ)

for

for($i = 0; $i -lt 5; $i++){
    Write-Host $i
}

0
1
2
3
4

foreach

foreach($i in (0..4)){
    Write-Host $i
}

0
1
2
3
4

while / do while

$i = 0
while($i -lt 5){
    Write-Host $i
    $i++
}

0
1
2
3
4


$i = 0
do {
    Write-Host $i
    $i++
}while($i -lt 5)

0
1
2
3
4

break/continue

for($i = 0; $i -lt 10; $i++){
    if($j -eq 2){break}
    Write-Host $i
}

0
1


for($i = 0; $i -lt 10; $i++){
    if($i -eq 2){continue}
    Write-Host $i
}

0
1
3
4
5
6
7
8
9

多重ループからの脱出

Write-Host "before loop"

:exit_all
for($i = 0; $i -lt 10; $i++){
    for($j = 0; $j -lt 10; $j++){
        Write-Host $i, $j
        if($j -eq 2){break exit_all}
    }
}
Write-Host "after loop"


before loop
0 0
0 1
0 2
after loop

ラベルをforループの下に移動したら、"after loop"が非表示になった。
ラベルが付いている文の次へジャンプするということ?

PowerShell - データと変数の型

文字と数値が混ざった式は、左の型に合わせる? でにキャスト?すれば変換できるっぽい

PS C:\> 1 + "1"
2
PS C:\> "1" + 1
11
PS C:\> [long]"1" + 1
2

変数の型

最初に使用するときに型の宣言をすれば、その型の変数になる。
使用をやめるときはRemove-Variable "$を付けないの変数名"

PS C:\> [long]$i = 1
PS C:\> $i = "abc"
値 "abc" を型 "System.Int64" に変換できません。エラー: "入力文字列の形式が正しくありません。"
発生場所 行:1 文字:1
+ $i = "abc"
+ ~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

PS C:\> Remove-Variable i
PS C:\> $i = "abc"
PS C:\> $i
abc

短縮形が登録されている型と正式型(Ver. 5.1の場合)

続きを読む

PowerShell - ファイル/フォルダリストの取得

バージョン 5.1

PS C:\> Get-ChildItem -File

PS C:\>Get-ChildItem -Directory

バージョン 2.0

PSIsContainerプロパティ?がTrueならフォルダ、FalseならファイルなのでWhere-Objectで抽出する。
バージョン5.1でも使用可能。

PS C:\> Get-ChildItem | Where-Object{!$_.PSIsContainer}  # ファイル

PS C:\> Get-ChildItem | Where-Object{$_.PSIsContainer}   # フォルダ

PowerShell - テキストファイルの読み書き

書き込みはOut-Fileを使用する。文字列はパイプで渡すか、-InputObjectでオブジェクトを渡す。-Appendで追加書き込み。
読み込みはGet-Contentを使用する。各行毎の文字列の配列として変数に読み込まれる。ただし1行のみのファイルはStringになるため、GetType()のNameプロパティで確認が必要となる。

$file = ".\test.txt"

# ファイルへの書き込み
"1行目” | Out-File $file -Encoding default
"2行目” | Out-File $file -Encoding default -Append # 追加書込モード
Out-File $file -Encoding default -Append -InputObject "3行目”



# ファイル読み込み(複数行)
$txt = Get-Content $file
$txt
$txt.GetType()
# IsPublic IsSerial Name        BaseType
# -------- -------- ----        --------
# True     True     Object[]    System.Array 

$txt[1] # 2行目


# ファイル読み込み(1行)
$txt = Get-Content $file
$txt.GetType()
#IsPublic IsSerial Name                                     BaseType              
#-------- -------- ----                                     --------              
#True     True     String                                   System.Object

1行ずつの読み取り

.NETのStreamReaderを使用する。
StreamReaderは、System.IO.StreamReaderなのだけれども、Systemは省略できるっぽいです。ReadLineメソッドで1行ずつ読みとる動作を$nullになるまで繰り返す。


$path = Get-Location
$file = "test.txt"
$enc = [Text.Encoding]::Default # Shift_JIS
$reader = New-Object IO.StreamReader(($path.Path + "\" + $file), $enc)

while ( ($line = $reader.ReadLine()) -ne $null){
    Write-Host $line
}

$reader.close()

PowerShell - 配列

代入

PS C:\> $ary = 1,2,3,4,5
PS C:\> $ary = 1 .. 5

PS C:\> $ary = @() #空配列

PS C:\> $ary = @(1,2,3,4,5)
PS C:\> $ary = "abcdefg" -split "" # 1文字ずつに分割(前後に空文字の要素が入る)
PS C:\> $ary = "abcdefg" -split "\B" # 1文字ずつに分割(前後に空文字の要素が入らない)

要素アクセス

PS C:\> $a = 1..5
PS C:\> $a[2] = 300
PS C:\> $a
1
2
300
4
5

PS C:\> $a[0]
1
PS C:\> $a[0,2,4]
1
300
5
PS C:\> $a[0..3]
1
2
300
4
PS C:\> $a[4..0] # 逆順に並べ替え
5
4
300
2
1

要素追加

PS C:\> $a = 1..5
PS C:\> $a += 100 # 末尾に追加
PS C:\> $a
1
2
3
4
5
100
PS C:\> $a = @(-100) + $a # 先頭に追加
PS C:\> $a
-100
1
2
3
4
5
100

配列のコピー

PS C:\> $a = 0..4
PS C:\> $b = $a # $bは$aと同じオブジェクトへの参照
PS C:\> $a[0] = 100
PS C:\> $b
100
1
2
3
4
PS C:\> $a.GetHashCode()
28959488
PS C:\> $b.GetHashCode()
28959488

PS C:\> $a = 0..4
PS C:\> $b = $a.Clone() # 別オブジェクトとしてコピー
PS C:\> $a[0] = 100
PS C:\> $b
0
1
2
3
4
PS C:\> $a.GetHashCode()
38658037
PS C:\> $b.GetHashCode()
45724662

その他

PS C:\> $a.Count # 要素数
7
PS C:\> $a.Contains(-100) # 要素に存在するか
True
PS C:\> -100 -in $a # 要素に存在するか(V3以降)
True