LoginSignup
6
8

More than 3 years have passed since last update.

Bashスクリプト→PowerShellスクリプト 移植レシピ

Last updated at Posted at 2019-07-28

前提

  • macOSやLinuxにガチガチに依存したBashスクリプトをWindowsでも動かしたい
  • 元のテキストファイルをgrepsedなどで加工している
  • 入出力は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 UTF8F8の間に-を入れないように注意してください。

出力

[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ではcurlwgetも)

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8