「PowerShellワンライナーで解く『シェル・ワンライナー160本ノック』第1章」という記事をみたので、つられてやってみました(ぇ
書籍情報はここ。問題自体は書籍を参照してください。
問題に使われているデータはこちらで提供されています。
また、このページではワンライナーを「改行がないこと」「複文としないこと(";" で区切らないこと)」とします。(一般的な定義がどうだかは知りません)
そのうえで、個人的にワンライナーは好きではないので、以下の解は基本的にワンライナーで書いていません(汗 普通に改行やインデントを使用しています。
- 問1
正規表現で Match させた例。
gc .\files.txt | ? { $_ -match "\.exe$" }
拡張子を求めて比較した例。
gc .\files.txt | ? { [System.IO.Path]::GetExtension($_) -eq ".exe" }
- 問2
Add-Type -AssemblyName System.Drawing gci *.jpg | % { $image = [System.Drawing.Image]::FromFile($_.fullname) $newname = [System.IO.Path]::ChangeExtension($_.fullname, "png") $image.Save($newname, [System.Drawing.Imaging.ImageFormat]::Png) }
下記は、上記の変数部分を展開し、ワンライナーにしてみた例です。(";" を使わない例/改行は読みにくくなるので削っていませんが、削っても動きます。ただし、Add-Type の行はどうもならないのでここだけは ";" で区切っています。)
問11 の例を除いて、このページの解答例は、同様の手法(変数部分の展開や;区切り、文字列の連結など)でワンライナーにすることができます。Add-Type -AssemblyName System.Drawing; gci *.jpg | % { [System.Drawing.Image]::FromFile($_.fullname).Save([System.IO.Path]::ChangeExtension($_.fullname, "png"), [System.Drawing.Imaging.ImageFormat]::Png) }
- 問3
書式を "0000" とした例。
gci | % { $newname = "{0:0000}" -f [int]$_.Name ren $_ $newname }
書式を "D4"(桁数を数値で指定)とした例。
gci | % { $newname = "{0:D4}" -f [int]$_.Name ren $_ $newname }
- 問4
gci | ? { [int](gc -raw $_) -eq 10 } | % { del $_ }
- 問5
gc .\ntp.conf | % { if ($_ -match "^pool (.+ )") { $Matches[1] } }
- 問6
スペースの数を書式で指定した例。
@(5..1) | % { "{0,$_}" -f "x" }
- 問7
1.1 や 1.08 は二進小数(= 浮動小数点型)では無限小数となるので、Decimal を使用しています。
あと、-split した配列を "," 演算子を使用して配列のままパイプに流しています。
( gc .\kakeibo.txt | % { ,($_ -split " ") } | % { if ($_[1] -match "\*" -or $_[0] -lt "20191001") { [decimal]$_[2] * 1.08d } else { [decimal]$_[2] * 1.1d } } | Measure -Sum ).Sum
- 問8
時刻文字列が年と時の間に ":" が入っているので、[DateTime]::ParseExact を使用し、時刻の書式を指定しています。
ちなみに夜の 12 時 (AM12 時)、昼の 12 時 (PM12 時)はともに午前です(ぇ
参考「質問4-1)正午は午前12時?それとも、午後12時?」gc .\access.log | % { if ($_ -match "\[(.+)\]$") { $date = [DateTime]::ParseExact($Matches[1],"dd/MMM/yyyy:HH:mm:ss zzz",[System.Globalization.CultureInfo]::InvariantCulture) if ($date.Hour -le 12) { "午前" } else { "午後" } } } | group | select Name, Count
- 問9
時刻文字列が年と時の間に ":" が入っていない一般的な形式なので、Get-Date -Date を使用し、時刻の書式は指定していません。
gc .\log_range.log | ? { if ($_ -match "\[(.+)\]") { $from = Get-Date -Date "2016/12/24 21:00:00" $to = Get-Date -Date "2016/12/25 4:00:00" $date = Get-Date -Date $Matches[1] ($from -le $date) -and ($date -lt $to) } }
- 問10
gc .\headings.md | % { if ($_ -match "# (.+)$") { $Matches[1] "===" } elseif ($_ -match "## (.+)$") { $Matches[1] "---" } else { $_ } }
- 問11
この例は唯一ワンライナーに書き換えることができません。
$names = @{}; $names["すず"] = "鈴木"; $names["さと"] = "佐藤"; $names["やま"] = "山本"; $gijiroku = gc .\gijiroku.txt; for ($i = 0; $i -lt $gijiroku.Length; $i += 3) { $name = $gijiroku[$i] $text = $gijiroku[$i + 1] "{0}: {1}`n" -f $names[$name], $text }
ワンライナーにこだわるなら以下のようにも書けますし、ある意味上記よりこちらのほうがシンプルではあります。
しかし個人的に読解性/メンテ性の観点から好きではありません。gc -Raw .\gijiroku.txt | % {$_ -replace "(^|`r?`n)さと`r?`n","`n佐藤:"} | % {$_ -replace "(^|`r?`n)すず`r?`n","`n鈴木:"} | % {$_ -replace "(^|`r?`n)やま`r?`n","`n山本:"}