Powershell 手習い

作成日 : 2016-07-03
最終更新日 :

PowerShell とは

PowerShell とは、Microsoft 社のオペレーティングシステムで作動する各種プログラムを、 コマンドラインで作成・参照・更新・削除できるしくみである。

コマンドラインを起動するには、すべてのアプリから、Windows PowerShell を選ぶ。Windows 10 では、 (x86) があるものとないもの、ISE がついているものとついていないものとで計4種類の組み合わせがあるが、 どちらもついていないものを選べばよい。

そのほか、Windows Terminal をインストールして起動することもできる。

PowerShell のバージョン

コマンドプロンプトが出ている画面で、単に $PSVersion と打ち込み、Enter キーを押せばよい。 下記は私の Windows 10 の環境である。

PS C:> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.0.10586.122
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.10586.122
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

CSV ファイルの取り込みと加工

次のような CSV ファイルがあるとしよう。

番号	,年月日		,緯度,	経度,	マグニチュード,	1,日本語名称1	,	2,英語名称	,	3, 日本語別称
404	,1995-01-17	,34.6, 135.0, 		7.3, 	1, 兵庫県南部地震, 	2, Kobe earthquake,	3, 阪神・淡路大震災
411	,2001-03-24	,34.1, 132.7, 		6.7, 	1, 芸予地震
414	,2003-09-26	,41.8, 144.1, 		8.0, 	1, 十勝沖地震
415	,2004-10-23	,37.3, 138.9, 		6.8, 	1, 新潟県中越地震
418	,2007-03-25	,37.2, 136.7, 		6.9, 	1, 能登半島地震
419	,2007-07-16	,37.6, 138.6, 		6.8, 	1, 新潟県中越沖地震
420	,2008-06-14	,39.0, 140.9, 		7.2, 	1, 岩手・宮城内陸地震
423	,2011-03-11	,38.1, 142.9, 		9.0, 	1, 東北地方太平洋沖地震, 2, Tohoku earthquake, 3, 東日本大震災

CSV ファイルを取り込むコマンドレット Import-CSV がある。これを使おう。

PS C:\Users\username> Import-CSV .\earthquake.csv -Encoding Default

すると次のように出力される。

番号: 404
年月日: 1995-01-17
緯度: 34.6
経度: 135.0
マグニチュード: 7.3
1: 1
日本語名称: 兵庫県南部地震
2: 2
英語名称: Kobe Earthquake
3: 3
日本語別称: 阪神・淡路大震災
(中略)
番号: 423
年月日: 2011-03-11
緯度: 38.1
経度: 142.9
マグニチュード: 9.0
1: 1
日本語名称: 東北地方太平洋沖地震
2: 2
英語名称: Tohoku earthquake
3: 3
日本語別称: 東日本大震災

このように1レコードずつ清書されて出てくる。このように、第1行目を列の属性(アトリビュート)として解釈してくれる。

1レコードあたりの処理を行なうには、次のような分解ができる。

Import-Csv 'servers.csv' | ForEach-Object {
  $_."列の属性"
}
PS C:\Users\username> Import-CSV .\earthquake.csv -Encoding Default | ForEach-Object{
>> $_."年月日" +$_."1"+ $_."日本語名称1"+"`n"+ $_."年月日" + $_.3 + $_."日本語別称" }
1995-01-17      1兵庫県南部地震
1995-01-17      3阪神・淡路大震災
2001-03-24      1芸予地震
2001-03-24
2003-09-26      1十勝沖地震
2003-09-26
2004-10-23      1新潟県中越地震
2004-10-23
2007-03-25      1能登半島地震
2007-03-25
2007-07-16      1新潟県中越沖地震
2007-07-16
2008-06-14      1岩手・宮城内陸地震
2008-06-14
2011-03-11      1東北地方太平洋沖地震
2011-03-11      3東日本大震災

Excel と PowerShell

PowerShell を使って、複数の Excel ファイルから固定シートの固定セルの値を拾って読むことを考える。 「平成 27 年」という名前のフォルダーに複数の Excel ファイル(ブック)がある。1ブックにつきシートは1枚だけで、次のような表になっている。

utsunomiya.xlsx

ABCDEF
1日出h日出m日入h日入m
2116521642

これは、都市 utsunomiya (宇都宮)の1月1日の日の出が 6 時 52 分であり、日の入りが 16 時 35 分であることを表している (理科年表 平成 27 年 暦 40 より)。 このようなブックで1月1日の日の出の時間を PowerShell表示する。

# hinode.ps1

param($file)                                       # 引数に使う
$file = (Get-ChildItem $file).FullName             # オープンのためにフルネームにする
$excel = New-Object -ComObject Excel.Application   # Excel を扱うための準備をする
$excel.Visible = $false                            # Excel を非表示
$book = $excel.Workbooks.Open($file)               # フルネームで指定したブックを開く
$sheet = $excel.Worksheets.Item(1)                 # 1 番目のシートを選択する
$line = 2                                          # 第 2 行を選択する
$hinodeh = $sheet.Cells.Item($line,3).Text         # 第 2 行の第 3 列を選択する
$hinodem = $sheet.Cells.Item($line,4).Text         # 第 2 行の第 4 列を選択する
Write-Host "$file : $hinodem " 時 "$hinodeh" 分    # 取得した情報を表示する
$excel.Quit()

> .\hinode.ps1 .\utsunomiya
このシステムではスクリプトの実行が無効になっているため……

というエラーが表示され、実行されないようになっている。 これは PowerShell のポリシーである。

Set-ExectionPolicy RemoteSigned

実行ポリシーの変更 実行ポリシーは、信頼されていないスクリプトからの保護に役立ちます。実行ポリシーを変更すると、about_Execution_Policies のヘルプ トピック (http://go.microsoft.com/fwlink/?LinkID=135170) で説明されているセキュリティ上の危険にさらされる可能性があります。実行ポリシーを変更しますか? [Y] はい(Y) [A] すべて続行(A) [N] いいえ(N) [L] すべて無視(L) [S] 中断(S) [?] ヘルプ (既定値は "N"):

ここで何もせずに Return のみを押してしまうと、変更が反映されない。したがって確実に Y をクリックしよう。

さて、実行した結果は次の通りだ。

PS > .\hinode.ps1 utsunomiya.xlsx
C:\users\username\documents\powershell\utsunomiya.xlsx : 6 時 52 分

では次に、都市 hiroshima (広島)の日の出と同時に出してみよう。 広島は、1月1日の日の出が 7 時 16 分であり、日の入りが 17 時 11 分であることを表している (理科年表 平成 27 年 暦 34 より)。 hiroshima.xlsx に同じようなデータを作ってみたらどうだろうか。

PS > .\hinode.ps1 hiroshima.xlsx
C:\users\username\documents\powershell\hiroshima.xlsx : 7 時 16 分

環境変数

環境変数すべてを表示する

Get-ChildItem env:

環境変数 Path を表示する

Get-ChildItem env:Path

新たに環境変数 RUST_LOG を作り、値として debug を設定する

 $env:RUST_LOG="debug"

折り返す

上のいくつかのページで見たように、幅が足りない場合には折り返しがされず、途中が省略され、切れてしまう (キレるのは、若者だけでも、老人だけでもないようだ)。 その場合には、Format-Table コマンドレットに -Autosize -Wrap オプションを付ける。

Get-ChildItem env:Path | Format-Table -Autosize -Wrap

Unix と PowerShell

私のような老人は、Unix だったらできるのに PowerShell だとできないと考えてしまう。 だから、Unix のコマンドと PowerShell のコマンドを関連付けることを試みた。

which

PowerShell では Get-Command である。 私が作った自作のバッチファイル webserver.bat の場所を探したいとする。

> Get-Gommand webserver
CommandType     Name           Version    Source
-----------     ----           -------    ------
Application     webserver.bat  0.0.0.0    C:\Users\username\documents\bin\webserve...

私の Windows 10 Home Edition では PowerShell のバージョンは 5 であり、 Source の欄にパスも表示されている。 パスが表示されない場合は、Format-List を後続に使う。

> Get-Gommand webserver | Format-List
Name            : webserver.bat
CommandType     : Application
Definition      : C:\Users\username\documents\bin\webserver.bat
Extension       : .bat
Path            : C:\Users\username\documents\bin\webserver.bat
FileVersionInfo : File:             C:\Users\username\documents\bin\webserver.bat
                  InternalName:
                  OriginalFilename:
                  FileVersion:
                  FileDescription:
                  Product:
                  ProductVersion:
                  Debug:            False
                  Patched:          False
                  PreRelease:       False
                  PrivateBuild:     False
                  SpecialBuild:     False
                  Language:

echo

echo は引数を順に表示するコマンドである。

>echo Je pense, donc je suis.
Je pense, donc je suis.

これをまねてみる。

>cat echo.ps1
[string] $s = ""
foreach ($s in $args) {
	Write-Output $s
}
>./echo.ps1 Je pense, donc je suis.
Je
pense, donc
je
suis.

カンマでは分離されないのですね。ワイルドカードはどうだろう。

>./echo.ps1 *.ps1
*.ps1

あれ、ワイルドカードを展開してくれない。どうしよう。

PoweShell のコマンドレットはワイルドカードを展開してくれる。

>./Convert-Path *.ps1
C:<absolute-path>\echo.ps1
C:<absolute-path>\resize.ps1
>./Get-ChildItem -Name *.ps1
echo.ps1
resize.ps1

では、バッチファイルではこうかな。

>cat echo.ps1
$strs = Get-ChildItem -Name $args
foreach ($s in $strs) {
	Write-Output $s
}
>./echo.ps1 *.ps1
echo.ps1
resize.ps1

やれやれ。

参考

コマンドプロンプトと PowerShell

バージョンの違い

Windows のコマンドプロンプト画面は、昔は DOS 窓とか DOS プロンプトなどと呼ばれていた。 今はコマンドプロンプト画面はすべて PowerShell に置き換えられていると思ったが、 思ったよりそうでもないようだ。実際私が作業するときも、PowerShell で作業するよりコマンドプロンプト画面で作業する場合が多い。なぜだろう。

たとえば、PowerShell 画面でコマンドを打つと、こんなエラーが出ていらいらする。

PS C:> wget --help
wget : リモート名を解決できませんでした。: '--help'
発生場所 行:1 文字:1
+ wget --help
+ ~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

PS C:> wget
コマンド パイプライン位置 1 のコマンドレット Invoke-WebRequest 次のパラメーターに値を指定してください: Uri:
PS C:> $PSVersionTable Name Value ---- ----- PSVersion 5.1.22621.963 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.22621.963 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1

わけがわからない。しかし、同じ PowerShell でも、コマンドプロンプトと同じようにうまくいくことがある。 PowerShell のバージョンの違いのようだ。

PS C:> wget --help
GNU Wget 1.15, a non-interactive network retriever.
Usage: wget [OPTION]... [URL]...

Mandatory arguments to long options are mandatory for short options too.
(中略)
PS C:> wget
wget: missing URL
Usage: wget [OPTION]... [URL]...

Try `wget --help' for more options.

PS C:> $PSVersionTable

	Name                           Value
----                           -----
PSVersion                      7.2.9
PSEdition                      Core
GitCommitId                    7.2.9
OS                             Microsoft Windows 10.0.22621
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

コマンドプロンプトの標準出力と標準エラー出力

コマンドプロンプトの時代から、Windows で標準出力と標準エラー出力を同一のファイルに一緒に出力する方法として、 LINUX などの UNIX 系 OS では次の記法が知られている。
$ command >file 2>&1
参考
https://www.itmedia.co.jp/help/tips/linux/l0388.html

この記法 2>&1 という記法は Windows のコマンドプロンプトでも有効だ。では、PowerShell ではどのように記述するのだろう。マイクロソフトの、 リダイレクトについて(learn.microsoft.com) から引用する。

例 1: エラーと出力をファイルにリダイレクトする
	dir 'C:\', 'fakepath' 2>&1 > .\dir.log
	エラー ストリームを成功ストリームにリダイレクトし>、結果の成功ストリームを呼び出されたファイルに送信するために使用2>&1されます。dir.log

何をいっているのかわからない。英語で読んでみよう。

PowerShell
	dir 'C:\', 'fakepath' 2>&1 > .\dir.log
	It uses 2>&1 to redirect the Error stream to the Success stream, and > to send the resultant Success stream to a file called dir.log

これならまだ意味がわかる。「2>&1 (という書き方)を使って、エラーストリームを成功ストリームにリダイレクトし、 (リダイレクトされた)結果の成功ストリームを dir.log ファイルに書き出す」。 ここでは、'C:\' という確実に存在するフォルダの内容と、'fakepath' という、実際に存在しないフォルダの 2 つを同時に表示してその結果を dir.log に書き込む、 という意図がある。もちろん、dir を 'C:\' に 適用した結果は成功ストリームに送られ、dir を 'fakepath' に 適用した結果はエラーストリームに送られる。 2>&1 のおかげでエラーストリームは成功ストリームとマージされ、無事 dir.log に両方の結果が入る。なんだ、UNIX と同じでいいのか。

画像を拡大縮小する

数十個の JPEG ファイルがある。これらの画像をまとめて縮小する(リサイズする)必要が生じた。 どうすべきか。 プログラマならプログラムを書くだろう。さて、どの言語で書けばよいか。 せっかくだから PowerShell を使うことを考えた。以下はその一例である。 もとの JPEG ファイルはすべて同じフォルダにあるものとし、 縮小したファイルは quarter というフォルダに保存されるものとする。

# ワイルドカードの展開

$strs = Convert-Path $args

foreach ($s in $strs) {
	# アセンブリの読み込み
	[void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")

	# 画像ファイルの読み込み
	Write-Output $s
	$image = New-Object System.Drawing.Bitmap("$s")


	# 縮小先のオブジェクトを生成
	$canvas = New-Object System.Drawing.Bitmap([int]($image.Width / 2), [int]($image.Height / 2))

	# 縮小先へ描画
	$graphics = [System.Drawing.Graphics]::FromImage($canvas)
	$graphics.DrawImage($image, (New-Object System.Drawing.Rectangle(0, 0, $canvas.Width, $canvas.Height)))

	# 保存
	$t = (Split-Path $s) + "\quarter\" + (Split-Path -Leaf $s)
	Write-Output $t
	$canvas.Save("$t", [System.Drawing.Imaging.ImageFormat]::Jpeg)
	
	# オブジェクトの破棄
	$graphics.Dispose()
	$canvas.Dispose()
	$image.Dispose()
}

参考リンク

カラー画像をグレースケール画像にする

カラー画像をグレースケール画像にしようと思った。画像の拡大縮小と同じようにできないかと探したが、 プログラムを作るか OpenCV などのライブラリを探すかしないといけない。 面倒だと思っていたら、jpeg ライブラリを使った jpegtran というプログラムがあるということを知った。 何かのついでに入れているのではないかと思ったら、 Python で Anaconda を入れたときについでに入ったようだ。 詳細は Anaconda 参照。

このシステムではスクリプトの実行が無効になっているため、ファイル foo.ps1 を読み込むことができません。

Elm を導入して PowerShell で実行しようとしたが、エラーが出てしまった。 原因は実行ポリシーが Restricted(制限の意味)になっているからで、これは Get-ExecutionPolicy を実行するとわかる。 対策は、実行ポリシーを変更することで、たとえば、 Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process とする。 以下はその例である。

PS C:> elm
elm : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Program Files\nodejs\elm.ps1 を読み込むことが
できません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してくだ
さい。
発生場所 行:1 文字:1
+ elm
+ ~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
PS C:> Get-ExecutionPolicy
Restricted
PS C:> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
PS C:> Get-ExecutionPolicy
RemoteSigned
PS C:> elm
Hi, thank you for trying out Elm 0.19.1. I hope you like it!

-------------------------------------------------------------------------------
I highly recommend working through <https://guide.elm-lang.org> to get started.
It teaches many important concepts, including how to use `elm` in the terminal.
-------------------------------------------------------------------------------

(2022-11-22)

まりんきょ学問所コンピュータの部屋Windows 手習い > PowerShell 手習い


MARUYAMA Satosi