いつもUSP友の会様を拝見してます。 さて、前回のシェル芸が第2回だったこともあり第1回を触ってみました。 触るのはおもしろそうな3,5,6,7問だけです。(問題4は意図が不明でした)
ちなみに、前回の挑戦はこんな感じでした。
第2回チキチキ!シェル芸人養成勉強会をPowerShellでやってみた
前提
前回同様の縛りプレイです。 なるべく1ライナーで……敢えて、変数に収めるべきところすら、そのまま利用できるところはパイプで繋ぐという制約です。 出題内容は、UPS友の会様をご覧下さい。
※ シェル環境前提なので、なるべくAliasを利用しているのはご了承ください。
※ 私はAlias余り好きじゃない派です。
※ PowerShellとBashの大きな違いは|
(パイプ)で渡されるのが文字列ではなくオブジェクトということを念頭に…
Alias一覧
Get-ChildItem #ls Get-Content #cat #gc Get-Random #random Foreach-Object #% Where-Object #? Measure-Object #measure Compare-Object #diff Format-Table #ft Format-List #fl
問題1: ユーザの抽出
そもそもpasswdはWindowsにないので省略。
問題2: ユーザの抽出2
同上、省略。
問題3: ファイルの一括変換
以下のフォルダ・ファイル構成でファイルを作って、ファイル一覧を表示(ls)、ファイル内容を表示(cat)。
※ 注意: #bin/bash
のshebangはPowerShellにはないため置換対象を変更しています。
- 置換操作前:
#requires -Version 2.0
- 置換操作後:
#requires -Version 3.0
これで一撃です。
Select-String -Path ".\etc\*.*" -Pattern "#requires -Version 2.0" -Encoding default | %{ $filename = $_.Filename; cat "$($_.Path)" | %{$_ -replace "#requires -Version 2.0","#requires -Version 3.0" | Out-File ".\hoge\$filename" -Encoding default -Force -Append } }
例のごとく改行します。
Select-String -Path ".\etc\*.*" -Pattern "#requires -Version 2.0" -Encoding default ` | %{ $filename = $_.Filename; cat "$($_.Path)" -Encoding default ` | %{$_ -replace "#requires -Version 2.0","#requires -Version 3.0" ` | Out-File ".\hoge\$filename" -Encoding default -Force -Append } }
やっていることは簡単です。
- 対象の文字列が含まれるファイルをSelect-Stringで調べる
- 対象ファイルで文字列置換
- ファイルを出力
ポイントは、Out-File
での出力が一行ごとのため-Append
が必要なぐらいでしょうか?
問題4: 集計
やることの意味が分からず…省略。
※ Jan/25/2013追記: 牟田口先生が書かれていました!
問題5: Fizz Buzz
これは先行して別記事で解いていますので、こちらを参照してください。
問題6: 日付の計算
1978年2月16日は2012年10月27日の何日前か……簡単です。
解法1.
まずは、Foreach-Objectを使った手法です。
1978..2011 | %{ (Get-Date "$_/12/31").DayOfYear} | measure -Sum | %{$_.Sum - (Get-Date "1978/02/16").DayOfYear + (Get-Date "2012/10/27").DayOfYear }
改行します。
1978..2011 ` | %{ (Get-Date "$_/12/31").DayOfYear} ` | measure -Sum ` | %{ $_.Sum - (Get-Date "1978/02/16").DayOfYear + (Get-Date "2012/10/27").DayOfYear }
解法2.
この考えはそのままScriptBlockの手法にも使えます。
1978..2011 | %{ (Get-Date "$_/12/31").DayOfYear} | measure -Sum | select @{label="DIFF";expression={$_.Sum - (Get-Date "1978/02/16").DayOfYear + (Get-Date "2012/10/27").DayOfYear}} | fl
改行します。
1978..2011 ` | %{ (Get-Date "$_/12/31").DayOfYear} ` | measure -Sum ` | select @{ label="DIFF"; expression={$_.Sum - (Get-Date "1978/02/16").DayOfYear + (Get-Date "2012/10/27").DayOfYear} } ` | fl
解法1の変則
Get-Date
にはAliasがないので、短くするのに冒頭でdというAliasを当ててみました。あまり変わらなかった。
begin{Set-Alias d "Get-Date"}process{1978..2011 | %{ (d "$_/12/31").DayOfYear} | measure -Sum | %{$_.Sum - (d "1978/02/16").DayOfYear + (d "2012/10/27").DayOfYear }}
改行します。
begin{ Set-Alias d "Get-Date" } process{ 1978..2011 ` | %{ (d "$_/12/31").DayOfYear} ` | measure -Sum ` | %{$_.Sum - (d "1978/02/16").DayOfYear + (d "2012/10/27").DayOfYear } }
※ 答えは、12672日
Jan/25/2013追記
New-Timespan
があることを知らず無駄な事をしてました。牟田口先生のお陰で勉強になります。
Select-Object
の-ExpandProperty
パラメータを付けると結果だけになるのですね。いつもForeach-Object
してましたが、このパラメータをつければ省ける状況も生まれますね。
問題7: リストにないものを探す
前提の「1から10の数字がかいてあり、そのうちの1つの数がかけているファイルを作りましょう」には、2つの手法があります。
手法1
9つそろったことを確認する手法で、条件達成が保証されます。
begin{$b=$c=@()}process{while ($c.count -lt 9){$a = Get-Random -Minimum 1 -Maximum 11;$b += $a;$c = $b | select -Unique | select -First 9}}end{$c | select -Unique | select -First 9}
改行します。
begin{$b=$c=@()} process{ while ($c.count -lt 9) { $a = Get-Random -Minimum 1 -Maximum 11 $b += $a $c = $b | select -Unique | select -First 9 } } end{$c | select -Unique | select -First 9}
手法2
十分な数を回す手法で、必ずしも条件達成が保証はされないので母数を大きくします。
1..100 | %{Get-Random -Minimum 1 -Maximum 11} | select -Unique | select -First 9
Jan/25/2013追記
Get-Random
の-Count
が有ることを牟田口先生の例で学びました。これでこんな悩まなくて済みます。
TechNet - The Get-Random Cmdlet
手法3
そもそも-Count
を使えばいいのでは。
1..10 | Get-Random -Count 10 | select -First 9
さてファイルの生成後は、いよいよお題です。今回、3つの解法を考えました。
解法1
合計値との差異から判定は、正直卑怯というか目的が。
(cat .\num.txt) | measure -Sum | %{[int](1..10 | measure -sum).Sum - [int]$_.Sum }
解法2
比較で含まれないモノを判定する方法です。
1..10 | %{diff $_ ([int[]](cat .\num.txt))} | ?{$_.SideIndicator -eq "<="} | fl
Jan/25/2013追記
これも牟田口先生の指摘で勉強しました。
なるほど、diffに与えた-PassThru
パラメーターですか。このパラメーターを渡すと、パイプラインに渡す出力が変化します。
この辺で勉強したり。
Advanced Compare-Object: Working with Results
あとは、1..10
ではなく(1..10)
と(cat num.txt)
で比較しているのがいいです。(..)
とすることで、直接比較しているので明らかに私のは無駄で。
解法3
※ 牟田口先生がFizzBuzzで示されていた方法の応用です。
含まれないと以降結果0になることを利用して真偽値で判定
(0..9 | %{($_+1)*!(($_+1) - ([int[]](cat .\num.txt) | sort)[$_])} | measure -Maximum).Maximum + 1
手法2のファイル生成と、解法1をワンライナーで行う例です。
※ ファイル生成がないので意味がない?
1..100 | %{Get-Random -Minimum 1 -Maximum 11} | select -Unique | select -First 9 | measure -Sum | %{[int](1..10 | measure -sum).Sum - [int]$_.Sum }
Jan/25/2013追記
少し改良するとこうでしょうか。
1..10 | Get-Random -count 10 | select -First 9 | measure -Sum | %{[int](1..10 | measure -sum).Sum - [int]$_.Sum }
diffにパイプで渡せず…これではダメなんですね…・
1..10 | Get-Random -count 10 | select -First 9 | diff $a (1..10) -PassThru
問題8: CPU使用率
Get-Process
の記事で分かるとおり、そもそもCPU %
をWindowsで取得は省略。
PowerShellを使ってサーバで動いているプロセスを知りたい
Jan/25/2013追記
牟田口先生もこのように……むむむ…ワンライナーは難しいかー
問題9: 横にならんだ数字のソート
前回の第2回でやったことと同様なので…省略
Jan/25/2013追記
牟田口先生が書かれていたので参考に。
問題10: 横にならんだ数字のソート
牟田口先生が書かれていたので参考に。
まとめ
実は、社内のBash使いと「せーの」で開始して問3の完成速度で負けたので…ぐぬぬ…。 是非、USP友の会様には第3回の公開を期待しています。