[win: Windows PowerShell] Windows command-line shell and scripting language built on .NET. #powershell #win #cs
PowerShell Documentation - docs.microsoft.com - 公式マニュアル
PoserShell Reference - docs.microsoft.com - コマンドとかはここで検索できる
PowerShellの基本
PowerShell 使い方メモ
従来のコマンドプロンプトは MS-DOS:16bit のエミュレータ → 様々な拡張機能を有する Windows シェルという位置づけだったが、パワーシェルは .NET で駆動する ( シェル操作を引き継いだ ) ターミナルである。
Windows 10 環境なら Win + R > powershell
で起動できる。管理者権限つきで起動する場合は Win アイコンを右クリックから「Windows PowerShell ( 管理者 ) 」を選択。
Using PowerShell v6 for charactor encoding
# インストール $ choco install powershell-core --version=6.2.1 -y # PowerShell 6 の実行 $ pwsh.exe
PowerShell の「コマンド」は Get-Help
など、動詞+名詞の形になっている。コマンドの入力途中で Tab を押下するとコマンドを補完してくれるので便利。また、コマンドプロンプトと同等のコマンドは「エイリアス」という形で旧コマンドのままでも利用できる ( cd
や dir
等 ) 。その他 複数行にわたるコマンドを記述をしたいときは Shift + Enter で改行ができる 。
# touch 的なやつ
PS> ni .gitignore
旧コマンドプロンプトでは、コマンドの返り値は単純な文字列だったので、パイプ先のコマンドを工夫 ( 正規表現使ったり ) する必要があった。しかし PowerShell では返り値が PSObject と呼ばれる .NET オブジェクトのインスタンスになっているため、文字列に比べてより柔軟な処理が可能になっている。
PS> $result = Get-Date
PS> echo $result
PS> $result.GetType()
PS> コマンド A | コマンド B
のように記述すると「パイプ処理」により、コマンド A の戻り値を、パイプ |
の次に書いた コマンド B に引き渡せる。
特殊変数 $_
はそのコマンド内でのみ有効の「前コマンドの返り値が自動的に格納される変数」で $_.Length
のように利用する。返り値は基本オブジェクトなので .
アクセス演算子でプロパティやメソッドにアクセス可能。
Where-Object
Where-Objectのスクリプトブロックで複数の文を使いたいとき渡されたオブジェクトから要素を抽出する。エイリアスは where または ? 。ブロックの中で比較演算子を用いて真偽値を返却することで条件にマッチする要素を抽出することができる。
PS> Get-Service | where Status -eq "Stopped"
# {} ( Script Block ) で true / false 返却による判定もできます
PS> Get-Process | Where-Object { $_.ProcessName -Match "^p.*" }
# 利用可能な比較演算子
-eq 等しい(=) $_.Name -eq "wga.log"
-ne 等しくない(≠) $_.Name -ne "wga.log"
-gt より大きい(>) $_.Length -gt 1MB
-ge 以上(≧) $_.Length -ge 1MB
-lt 未満(<) $_.Length -lt 1MB
-le 以下(≦) $_.Length -le 1MB
-like ワイルドカード $_.Name -like "*.log"
-match 正規表現検索 $_.Name -match "[a-z]{1,}\.log"
-in 含む(..) $_.Length -le 1MB
# 文字列比較で大文字/小文字を区別する場合には「c」、しない場合には「i」を演算子の先頭に付ける。例えば「-ieq」などとする
ForEach-Object - docs.microsoft.com
ForEach-Objectとforeachは違う?渡されたイテレータブルなオブジェクトについて反復処理を行う。エイリアスは foreach または % だが、似たような
foreach () {}
ステートメントも存在するので注意。コマンドの方は内部でバッチ処理的に「要素ごとにメモリ上に展開」するのに対してステートメントは「全要素をメモリに一括展開」する。
PS> 30000, 56798, 12432 | ForEach-Object -Process {$_/1024}
29.296875
55.466796875
12.140625
オブジェクトからプロパティ名や行数を指定して選択する。
PS> Get-Process | Sort-Object -Property WS | Select-Object -Last 5
対象オブジェクトについて min / max / sum / avg などお馴染みの数値演算を行う。
PS> Import-Csv ./serviceyrs.csv | Measure-Object -Property years -Minimum -Maximum -Average
C# リファレンス - docs.microsoft.com - C# 関連の検索はこっち
.NET API ブラウザー - .NET API に関しての検索はここ
PowerShellスクリプティングの第一歩
powershell チートシート
内部的には .NET で動かすことになるので、PowerShell の文法に加えて C# でのプログラミングができる。スクリプトの拡張子は .ps1
。Windows 初期状態では、セキュリティの観点から PowerShell のユーザスクリプト実行は禁止されている。利用する場合は以下コマンドで変更しておくこと ( 要管理者権限 ) 。
PS> Get-ExecutionPolicy // 確認
PS> Set-ExecutionPolicy RemoteSigned
また PowerShell はファイル実行の際にカレントディレクトリを自動探索してくれないので、PATH を通してない場合は PS> ./script.sh1
のように「現在ディレクトリを相対パス・絶対パスで指定する」必要があるのに注意。
About If - docs.microsoft.com
About ForEach - docs.microsoft.com
function main
{
foreach ($file in Get-ChildItem)
{
if ($file.length -gt 100KB)
{
Write-Host $file
}
}
}
type FullName MinValue MaxValue
[array] System.Array
[bool] System.Boolean
[byte] System.Byte 0 255
[char] System.Char
[datetime] System.DateTime 0001/01/01 0:00:00 9999/12/31 23:59:59
[decimal] System.Decimal -79228162514264337593543950335 79228162514264337593543950335
[double] System.Double -1.79769313486232E+308 1.79769313486232E+308
[guid] System.Guid
[hashtable] System.Collections.Hashtable
[int16] System.Int16 -32768 32767
[int32], [int] System.Int32 -2147483648 2147483647
[int64], [long] System.Int64 -9223372036854775808 9223372036854775807
[nullable] System.Nullable
[psobject] System.Management.Automation.PSObject
[regex] System.Text.RegularExpressions.Regex
[sbyte] System.SByte -128 127
[scriptblock] System.Management.Automation.ScriptBlock
[single], [float] System.Single -3.402823E+38 3.402823E+38
[string] System.String
[switch] System.Management.Automation.SwitchParameter
[timespan] System.TimeSpan -10675199.02:48:05.4775808 10675199.02:48:05.4775807
[type] System.Type
[uint16] System.UInt16 0 65535
[uint32] System.UInt32 0 4294967295
[uint64] System.UInt64 0 18446744073709551615
[xml] System.Xml.XmlDocument
# 変数のデータ型を出力
Write-Host $hoge.getType()
# @(1, 2, 3, 4, 5)
$arg = @()
$arg += 'John'
$arg += 'Bob'
$arg += 'Jane'
# @{ <name> = <value>; [<name> = <value> ] ...}
$hash = @{ Number = 1; Shape = "Square"; Color = "Blue"}
Powershell スクリプトでは引数は自動的に
$Args[]
に格納されます。
引数宣言したい場合は以下のように Param()
で宣言すれば宜しい。初期値は $something = 'default'
のようにセット。データ型を [String]$something
のように指定したり [parameter(mandatory)]
で入力を強制させたりできる。
# echo-args.ps1
# 文字列引数 something を出力するスクリプト
#
# @example PS> C:\echo-args.ps1 -something hoge
# @param string $something - parameter(mandatory) により入力を強制
# @return string $something
#
Param([parameter(mandatory)][string]$something)
Write-Host $something
PS> echo $? # True / False
スクリプトに 0..50
みたいなオペレータを含む文字列を渡し、これをパースして [int[]]
な配列要素にしたいようなケースでは、Invoke-Expression で文字列を評価するしかないみたい。ちなみにエイリアスは iex
。
$range = 0..50
$range.length # 51
# 上記を文字列で渡される動的な引数から行いたい
# ... が以下のようにキャストはできない
$range = [int[]]'0..50' # エラー
# よって iex ( Invoke-Expression ) で文字列を式評価
[int[]]$range = iex '0..50'
$range.length # 51
$records = Import-Csv './import.csv' -Encoding UTF8
$counter = 1
$length = $records.length
$records | % {
# Do something ...
$p = $counter / $length * 100
Write-Progress 'Processing ... ' ([String]$p + '%') -percentComplete $p
$counter++
}
where.exe {command}
# diff が Compare-Object のエイリアスみたい
diff (get-content ./hoge.txt) (get-content ./fuga.txt)
Set-Location (Split-Path $MyInvocation.MyCommand.Path -parent)
PS> Get-Service | Get-Member
TypeName: System.ServiceProcess.ServiceController
Name MemberType Definition
---- ---------- ----------
Name AliasProperty Name = ServiceName
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
ToString Method System.String ToString()
...
DisplayName Property System.String DisplayName {get;set;}
MachineName Property System.String MachineName {get;set;}
ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;}
ServiceName Property System.String ServiceName {get;set;}
...
PS > Get-ChildItem | Format-List *
# リスト内容が大量にありそうな時のために more に戻り値を渡して少しずつ出力
PS > Get-ChildItem | Format-List * | more
# リスト表示したいカラムを指定しても良い
PS > Get-ChildItem | Format-List Name, Length
PS > Get-ChildItem |
>> Where-Object {$_.LastWriteTime -lt (Get-Date).AddMonths(-6)} |
>> ForEach-Object {$_.Delete()}
PS> dir | ren -newname { $_.name -replace '旧文字列','新文字列' }
# ./ 以下ディレクトリ内の *.csv の中に存在する 'Y20' を 'X20' へ置換したい
$ Get-ChildItem . -include *.csv -Recurse -Force |
>> ? { Select-String -InputObject $_ -Pattern 'Y20' -Encoding default } |
>> % { Set-Content $_.FullName ((Get-Content -LiteralPath $_) -replace 'Y20', 'X20') }
Get-ChildItem . -Include *.txt -Recurse -Force | Select-String "たぬき" -Encoding default
# -Include *.txt で全 .txt ファイルを取得できる
# -Exclude で除外も可能
# -Recurse で再帰的にオブジェクトを取得
# -Force で隠しファイルも取得
# -encoding default で現在環境のエンコード ( 日本語 S-JIS ) を指定
powershell -command "Start-Process -Verb runas cmd"
PowerShell (&S)
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoExit
Import-Csv / Export-Csv / ConvertTo-Csv / ConvertFrom-Csv
PowerShellのImport-CsvコマンドレットでCSVファイルを読み込む
PowerShellのExport-CsvコマンドレットでCSVファイルを出力する
PowerShell でテキストデータを CSV として読み込んでデータベースっぽく操作するメモ
PowerShell は標準で CSV ファイル操作 API がビルトインされている。
# Import
$csv = Import-Csv file.csv -Encoding Default
# Export ( SJIS )
Get-EventLog system | Export-Csv -path syslog.csv -Encoding Default -NoTypeInformation