しばたテックブログ

気分で書いている技術ブログです。

LinuxとmacOSでPowerShell Coreのパッケージ管理方法が変わります

先日公開されたPowerShell Core 6.1.0-preview.3からLinuxとmacOSでパッケージマネージャーを使ったインストールをする際のパッケージ名に変更が入ります。

github.com

安定版とプレビュー版の分離

PowerShell Core 6.1.0-preview.2まではLinuxやmacOSのパッケージマネージャーを使う際のパッケージ名は「powershell」の一つで管理していました。
これだと安定版のみを更新していきたい人に対してプレビュー版の更新が強制されることになり非常に問題がありました。

本件に先立ってGitHubのホームにあるインストーラーのリンクが安定版とプレビュー版で分離される様になり、今回PowerShell Core 6.1.0-preview.3でパッケージ名も分離されることとなりました。

f:id:stknohg:20180615132146p:plain

(図は安定版、プレビュー版が分離されたGitHubのホーム画面)

パッケージ名の変更

ここまでを踏まえて、今後PowerShell Coreのパッケージ名は以下の様に変更されます。

  • 安定版 : powershell
  • プレビュー版 : powershell-preview

例としてapt-getの場合は以下の様に使い分けることになります。

# 安定版のインストール
sudo apt-get install -y powershell

# プレビュー版のインストール
sudo apt-get install -y powershell-preview

インストール可能なバージョン

ここから少しややこしいのですが、「OSやディストリビューションによってサポート対象となるPowerShellのバージョンが異なる」、「既にPowerShell Core 6.1.0-preview.1~preview.2をリリースしてしまっている」といった事情があるため、パッケージ毎にインストール可能なPowerShell Coreのバージョンはややこしいことになっています。

以下に現時点での一覧を記載します。

f:id:stknohg:20180615131756p:plain

これが将来的には下図の様になっていくのですが、既存のPowerShell Core 6.1.0-preview.1~preview.2がどうなるかはまだ不明です。

f:id:stknohg:20180615131837p:plain

シンボリックリンクの変更

パッケージ名の変更に合わせてPowerShell Coreの実行バイナリに対するシンボリックリンクにも変更が入ります。

たとえばUbuntuの場合、PowerShell Coreのバイナリは

  • /opt/microsoft/powershell/<バージョン番号>/pwsh

にあり、シンボリックリンクが

  • /usr/bin/pwsh

に貼られていますが、このシンボリックリンクが

  • /usr/bin/pwsh-preview

に変更されます。
安定版は/usr/bin/pwshのまま変わりません。

最後に

ざっとこんな感じです。

本来この点は最初のプレビューリリース前に考えるべき話でした。
若干対応が遅れたものの、あるべき姿に落ち着いたのは良いことだと思います。

PowerShellとSelenium WebDriver(Chrome)を使ってブログのMixed Contentをチェックする

つい先日、本ブログをホストしているはてなブログが独自ドメインのサイトに対してHTTPS対応したので本ブログをHTTPS化しました。

staff.hatenablog.com

本ブログはこれまで241エントリ公開しており、そこまで数が多くないため、今回は全エントリを目視で確認してMixed Contentがあったエントリを手動で直す対応を取りました。

Mixed Contentとなったケースは殆ど

  • 古いエントリの画像がHTTPのCDNを向いていた
  • 一部oEmbedのリンクがHTTPになっていた

であり、単純にエントリを更新しなおすことで対応できたので作業自体は非常に楽でした。
HTTPS対応とは別に古いエントリのリンク切れなども修正できたのでブログの棚卸しにちょうど良かった感じです。

本エントリの動機

当座の対応としてはこれで良かったのですが、Mixed Contentをちゃんと全滅させたか確認したくなり、これは手動ではなく機械的に全エントリをチェックすべきだろうと思ったのが本エントリの動機になります。

Mixed Contentの確認方法

私は正直WEB系の知識には疎いのですが、ざっと調べた限りMixed Contentを機械的に確認するには「それ専用のサイトやツールを利用する」「ヘッドレスブラウザを使い警告やエラーを検知する」方法がある様で、今回は後者の方法を選びました。

基本的にはこちらのブログの方式に倣っています。

こちらはPHP + Selenium WebDriver + Chromeを使い、ブラウザコンソールログからMixed Contentの警告・エラーログを検出するものとなっています。
普段PHPを使う方はこちらのブログを参考にすると良いでしょう。

私はPowerShell使いですし、ブログのネタとしても美味しいのでPowerShell + Selenium WebDriver + Chromeで同様のことをやってみることにします。

【補足】他ツール

他に

github.com

というツールも見つけました。
Nodeを使っている方であればこのツール単体でMixed Contentをチェックできます。
(実装を見る限り同じ方式を採っています。内部でpuppeteerを使っているのでサイズはそこそこ大きい様です。)

前準備

事前に以下のソフトウェアを用意しておく必要があります。

Google Chrome

www.google.co.jp

最新バージョンをインストールしておけば良いでしょう。
最低限バージョン60以降であれば全プラットフォームにおいてヘッドレスで動作させることができる様です。

今回はバージョン66を使っています。

Selenium WebDriver (C#)

こちらのダウンロードページからC#のSelenium Client & WebDriver Language Bindingsをダウンロードするか、Nugetから手動でnupkgをダウンロードしてください。
.nupkgはただのZipファイルですので、解凍して中にあるWebDriver.dllを後述するフォルダに配置します。

実行環境がWindows PowerShellの場合は、net45フォルダにあるWebDriver.dllを、PowerShell Core 6.0の場合はnetstandard2.0フォルダにあるWebDriver.dllを使います。
現時点での最新バージョンはVer.3.12です。

f:id:stknohg:20180613214317p:plain

ChromeDriver - WebDriver for Chrome

から最新バージョンのChromeDriverをダウンロードし、Zipファイルの中にあるchromedriver.exeを後述するフォルダに配置します。
現時点での最新バージョンはVer.2.40です。

PowerShellとSelenium WebDriver(Chrome)を使ってMixed Contentをチェックする

ここから本題に入ります。
今回使用するスクリプトをGistに上げました。

gist.github.com

このスクリプトを任意のフォルダに保存し、同じフォルダにWebDriver.dllchromedriver.exeを配置します。
図にするとこんな感じです。

f:id:stknohg:20180613214648p:plain

動作確認

今回はPowerShell Core 6.0(6.0.2)で動作確認してみます。
スクリプトに記載してある

はGoogleによるMixed Contentの確認用サンプルサイトです。
Chromeでアクセスすると下図の様になり、開発者ツールからコンソールログを確認するとMixed Content等の警告・エラーログが出力されていることが分かります。

f:id:stknohg:20180613231303p:plain

PowerShell Coreを起動し、このスクリプトを実行してみると下図の様にブラウザのコンソールログからMixed Contentに関するものを出力してくれます。

f:id:stknohg:20180613231742p:plain

また、接続先を変えてMixed Contentが無いサイトの場合だとこの様になります。

f:id:stknohg:20180613232128p:plain

手元の環境で試した限りではいい感じにログを出力してくれています。

ただ、ブラウザ上で見るコンソールログとSelenium WebDriverから取得するログが完全に一致しないケースがある様です。
原因は解明できていないのですが、ブラウザ上で重複してるログがSelenium WebDriverではフィルタされて取得されない様に見受けられます。
とりあえず今回の用途には問題ないだろうとの判断をしていますが、厳密なログの検証などが必要な場合は注意が要りそうです。

最後に

Selenium WebDriverのおかげで思ってた以上にお手軽にMixed Contentのチェックができました。

本エントリの例は単一のURLに対するチェックですが、スクリプトを改造して複数URLに対応するのは簡単です。
例えばはてなブログであればAtomPub APIsitemap.xmlからエントリの一覧を取得できますので、これらを組み合わせてやるとブログ全体をチェックするスクリプトも比較的容易に作成することができます。

Get-ChildItemの動作に関する問題についての指摘事項

本エントリはブログに書くべきものなのか迷いましたが、文章量が多くなったためここに記します。
また、出題者を責める意図は無いためリンクは張りませんのでご了承ください。

出題された問題

先日Twitter上でPowerShellに関する以下の問題を見かけました。

問題

Windows Powershellにおいて、以下のコマンドを実行した。
実行結果はどうなる?

PS > (Get-ChildItem).Length

回答欄

  1. オブジェクトが無いのでエラーになる
  2. ディレクトリ配下のファイルの総容量が表示される
  3. ディレクトリ配下のファイルの数が表示される
  4. コマンドが存在しないためエラーになる

出題者の意図としては3. ディレクトリ配下のファイルの数が表示されるを選ばせたかったのでしょうが、残念ながらこの選択肢は全て正しくありません。
(前提条件次第では回答欄の内容にマッチする場合もあるのですが、本エントリでは回答欄として不適切の意味で"正しくない"と表現しています)

以下、何が正しくないのかについて順に指摘していきます。

PowerShellにおけるロケーション

まず、DocsにあるGet-ChildItemのヘルプを確認してみると、

Gets the items and child items in one or more specified locations.

とあり、このコマンドレットは「特定の ロケーション にある要素および子要素を取得する」ものです。

blog.shibata.tech

でも触れたとおり、PowerShellにおいて ロケーション はファイルシステム以外のデータ構造も対象とするより抽象度の高い概念です。

このためGet-ChildItemGet-DirectoryItemの様な名前でなくGet-ChildItemが選ばれているといえます。
(とはいえ、ファイルシステムを扱うのが主であるため、現実としては利便性のためにdirlsといったエイリアスが設定されていますが...)

先述の問題の場合、例えばロケーションをレジストリにすると

> cd HKCU:\Software\
> (Get-ChildItem).Length

で取得できるのは「レジストリの値とサブキーの総数」になります。

この問題を正しく出題するためにはロケーションをドライブレベルで確定する必要があります。

PowerShellのバージョン間での挙動の違い

PowerShellにおけるLengthプロパティはバージョン間で挙動が違います。

PowerShell 2.0まで

LengthプロパティはSystem.Arrayのプロパティであり、配列でないスカラ値には使用できません。

例えば

# PowerShell 2.0ではスカラ値にLengthは使えない
> (123).Length
> 

といった指定は何も返しませんし、Strict Modeのレベルによってはエラーとなります。

> Set-StrictMode -Version 2.0
> (123).Length
. : このオブジェクトにプロパティ 'Length' が見つかりません。このプロパティが存在することを確認してください。
発生場所 行:1 文字:7
+ (123). <<<< Length
    + CategoryInfo          : InvalidOperation: (.:OperatorToken) []、RuntimeException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

PowerShell 3.0以降

配列でないスカラ値に対してLengthプロパティが拡張され、

# スカラ値のLengthは1
> (123).Length 
> 1

# $nullのLengthは0
> $null.Length
> 0

といった値を返す様になっています。

最初の問題に戻ると、出題時にバージョンを指定していないので挙動が不定となり回答を確定することが出来ません。
ロケーションの明示もないため、(Get-ChildItem).Lengthが返しうる結果としては、

  • エラー
  • $null
  • 0
  • 1以上の数

のいずれかになります。

敢えて選択肢から答えを選ぶとするなら

  • 1 オブジェクトが無いのでエラーになる
  • 3 ディレクトリ配下のファイルの数が表示される

のいずれか、が近い感じでしょうか。

System.IO.FileInfo.Length プロパティ

【2018/06/11追記】

一点指摘漏れがありました。
(ついさっきまで完全に失念していました...)

例として適当なディレクトリで単一のファイルを作成するケースを考えます。

mkdir sample
cd .\sample\
# 適当なファイル(BOM付きUTF-16)を作成
"Hello World!" | Out-File -FilePath .\sample.txt

ここで(Get-ChildItem).Lengthを実行すると、

> (Get-ChildItem).Length
30

sample.txtのファイルサイズを返します。

これは対象フォルダ内にファイルが一つしかない場合はGet-ChildItemの戻り値はSystem.IO.FileInfo型となり、この型にあるLengthプロパティが使われるためです。

この場合2. ディレクトリ配下のファイルの総容量が表示されるが正答になります。

ちなみに、

mkdir sample2
cd .\sample2\
# 適当なサブディレクトリを作成
mkdir subdir

とサブディレクトリ1つだけの場合はGet-ChildItemの戻り値はSystem.IO.DirectoryInfo型となり、この型にはLengthプロパティはありませんので、(Get-ChildItem).Lengthの結果は

  • エラー
  • $null
  • 1

のいずれかになります。

【追記ここまで】

どう出題すればよかったか?

この問題で不足していたのは環境定義です。

PowerShellの環境はその気になればいくらでもカスタマイズできるので抜け道を全て埋めるのは難しいのですが、一般的な余興レベルとしては

問題

Windows 10においてWindows PowerShellを起動し、直ちに以下のコマンドを実行した。
実行結果はどうなる?

PS > (Get-ChildItem).Length

回答欄

  1. オブジェクトが無いのでエラーになる
  2. ディレクトリ配下のフォルダ、ファイルの総容量が表示される
  3. ディレクトリ配下のフォルダ、ファイルの数が表示される
  4. コマンドが存在しないためエラーになる

とし、

Windows 10

  • PowerShell 5.0~5.1 であることが確定している

Windows PowerShellを起動し、直ちに以下のコマンドを実行した。

  • 環境をカスタマイズしない限りはユーザープロファイルのフォルダ、またはC:\WINDOWS\system32(管理者として実行した場合)がカレントロケーションとなる

の条件を暗示するくらいで良いでしょう。

補足

ちなみに、PowerShellの言語モードや、対象となるロケーションのアクセス権などによっても(Get-ChildItem).Lengthの結果が変わってしまいますが、さすがにそれは穿ち過ぎなので指摘の対象から外しています。