前提
- macOSやLinuxにガチガチに依存したBashスクリプトをWindowsでも動かしたい
- 元のテキストファイルを
grep
・sed
などで加工している - 入出力はUTF-8 + LF
エラーが発生したらスクリプトを終了
set -eu
↓
$ErrorActionPreference = "Stop"
入力・分割
$Content = Get-Content -Encoding UTF8 -Raw ./test.txt
$ContentLines = (Get-Content -Encoding UTF8 -Raw ./test.txt).Split("`n") # CRは各行末に残る・末尾の改行も残る
$ContentLines = Get-Content -Encoding UTF8 ./test.txt # CRLFも区切り文字・末尾の改行は残らない
Get-Content
= cat
-Encoding UTF8
のF
と8
の間に-
を入れないように注意してください。
出力
[IO.File]::WriteAllText("$PWD\test2.txt", $Content)
[IO.File]::WriteAllText("$PWD\test2.txt", $ContentLines -join "`n")
注意: WriteAllLines
は、行末をCRLFに変えます。
コマンドオプション
Bashでオプション解析を本気でやろうとすると、かなり骨が折れます。
抽出
特定文字で区切ったフィールドの抽出
USER_NAME=$(awk '{ print $3 }' <<<"$LINE")
↓
$Fields = $Line -Split " +"
$UserName = $Fields[2]
番号からの行抽出
配列インデクサを使った抽出
$CONTENT_LINES | head -n 1
$CONTENT_LINES | tail -n 1
$CONTENT_LINES | head -n 3
$CONTENT_LINES | tail -n 3
cat foo.txt | head -n 2
↓
$ContentLines[0]
$ContentLines[-1]
$ContentLines[0..2]
$ContentLines[-3..-1]
(Get-Content -Encoding UTF8 foo.txt)[0..1]
Select-Objectを使った抽出
$CONTENT_LINES | head -n 5
$CONTENT_LINES | head -n -5
$CONTENT_LINES | tail -n 5
$CONTENT_LINES | tail -n +5 # 5番目「から出力」
↓
$ContentLines | Select-Object -First 5
$ContentLines | Select-Object -SkipLast 5
$ContentLines | Select-Object -Last 5
$ContentLines | Select-Object -Skip 4 # 4番目「までを飛ばす」
Select-Object
= select
grepのような行マッチ抽出
$CONTENT_LINES | fgrep foo
$CONTENT_LINES | igrep foo
$CONTENT_LINES | grep "^[Ff]oo"
$CONTENT_LINES | grep "^foo"
$CONTENT_LINES | grep "foo$"
↓
$ContentLines | Where-Object { $_.Contains("foo") }
$ContentLines | Where-Object { $_ -imatch "foo" }
$ContentLines | Where-Object { $_ -match "^[Ff]oo" } # 正規表現
$ContentLines | Where-Object { $_ -like "[Ff]oo*" } # glob形式
$ContentLines | Where-Object { $_.StartsWith("foo") }
$ContentLines | Where-Object { $_.EndsWith("foo") }
Select-String
を使った別解
$ContentLines | Select-String -SimpleMatch -CaseSensitive foo
$ContentLines | Select-String foo
$ContentLines | Select-String -CaseSensitive ^[Ff]oo
$ContentLines | Select-String -CaseSensitive ^foo
$ContentLines | Select-String -CaseSensitive foo$
Where-Object
= ?
or where
Select-String
= sls
おまけ: ファイル検索
grep [fF]oo *.txt
↓
Select-String [fF]oo -CaseSensitive *.txt
置換
$Content.Replace("old", "new")
$Content -replace "[Oo]ld", "new"
$ContentLines | ForEach-Object { $_ -replace "^old", "new" }
ForEach-Object
= %
or foreach
パターンマッチの使用
ACCESS_PATH=$(sed -E 's/^[A-Z]+ ([^ ]+) .+$/\1/' <<<"GET /index.html HTTP/1.1")
↓
$AccessPath = "GET /index.html HTTP/1.1" -replace "^[A-Z]+ ([^ ]+) .+$", "`$1"
PowerShellの場合、パターンマッチの置換先は$
をエスケープする必要があります。さもないと、変数展開により、「$1
」という名前の変数に展開されてしまいます。
特定のコマンドの存在を確認
if !type foo >/dev/null 2>&1; then
echo "foo not found" >&2
exit 1
fi
↓
if(!(Get-Command foo -ErrorAction Ignore)) {
Write-Error "foo not found"
exit 1
}
配列・連想配列
配列宣言
empty=()
local -a empty2=()
array1=(1)
array2=(1 2)
$empty = @()
$array1 = ,1
$array2 = @(1)
$array3 = 1,2
$array4 = (1,2)
$array5 = @(1,2)
$array6 = ,1,2
配列拡張
$a = ,1
$a += 2
$a += ,3,4
連想配列宣言
declare -A hash1
hash1=(
["a"]=1
["b"]=2
["c"]=3
)
$hash1 = @{a = 1; b = 2; c = 3}
連想配列アクセス
$hash1["a"] -eq 1
$hash1.b -eq 2
($hash1 | Select-Object -ExpandProperty c) -eq 3
リンク抽出
# <a href='...'>は不可
curl -LsSf http://foo.bar/ | egrep -i -o '<a( [^>])? href="[^"]+"( [^>]+)?>' | grep -oP '(?<=href=")[^"]+(?=")'
Invoke-WebRequest http://foo.bar/ | Select-Object -ExpandProperty Links
(Invoke-WebRequest http://foo.bar/).Links
Invoke-WebRequest
= iwr
(PowerShell5ではcurl
・wget
も)