Powershell」カテゴリーアーカイブ

PowershellでREST APIを利用する

最近PowershellでREST APIを使う用事がありました。特にPowershellにこだわる必要はなかったのですが、使い慣れているのと、例によってWindowsであればどの端末でも実行できるという点が便利だったのでPowershellを使うことにしました。

要件としては単純にGETメソッドを使って情報を取り出すというだけです。こういう簡単なスクリプトではよくPythonが推奨されるところかと思いますが、Powershellでも割と簡単でした。

コマンドレットとしてはInvoke-Restmethodというものがあるのでそれを使います。

Invoke-RestMethod

[Microsoft Learn]

サンプルとして何のAPIを使おうかと思いましたが、よく考えたらWordpressがREST APIに対応していたので、このブログに対して情報取得を試してみることにしました。

APIで取得したデータはJSON形式で返ってくるということは何となく知っていたのですが、ああいった構造化されたデータをどう処理するのかがピンときませんでした。Powershellの場合は結果を変数に格納すると暗黙的にPSObjectに変換してくれるということで、Powershellに慣れている身からすればこれは結構使いやすく感じました。

試しにこのブログの直近の投稿をAPIで取得し、結果をテーブル形式に変換して表示してみるコードを作ってみました。

<Powershell>
$uri = 'https://hebodj.net/wp-json/wp/v2/posts/'
$Response = Invoke-RestMethod -URI $uri

#メンバー一覧を出力
$Response | Get-Member | Format-Table

これでとりあえずPSObjectにどんなメンバーが格納されているかが分かります。ざっと眺めてみるとこれが投稿のタイトルかな、これが投稿時刻かな、というのが分かってきます。

続いて投稿のタイトルを抜き出して列挙するコードを考えてみます。

<Powershell>
$uri = 'https://hebodj.net/wp-json/wp/v2/posts/'
$Response = Invoke-RestMethod -URI $uri

# 各投稿のタイトルを表示
foreach ($post in $Response) {
    Write-Host $post.title.rendered
}
特に何も指定しないとトップページに表示されているエントリが対象になるようです。

こんな感じでOKです。同じような要領で本文も抜き出すことができるので、APIで今までのエントリの内容をファイルに出力して統計情報を出すなど、面白い使い方ができそうです。

今まであまりWebに関係する技術に縁がなかったのでREST APIも苦手意識があったのですが、Powershell経由であれば割と簡単にハンドリングできそうな気がします。

Windows Powershell ISEがサポートするのはv5.1まで

先日Powershellの開発環境について調べ物をしていたところ、標題の通りPowershell ISEがサポートしているのはv5.1までであることが公式ドキュメントから分かりました。

Windows PowerShell ISE

[Microsoft Learn]

Powershell ISEはWindowsに最初から入っているので気軽に使えて良かったのですが、最新のPowershellには対応していないということです。

また、このことからMicrosoftとしてはPowershell ISEをアクティブな機能開発の対象でないと位置づけているそうです。サポート終了というわけではないようですが、引退が迫っているような状況のようです。

代替としての標準的な開発環境は VS CodeとPowershell用拡張機能の組み合わせということです。確かにVS Codeは非常に高機能ですし、昔のVisual Studioなどと比べても簡単に導入できるようになったのでこちらがメインになるというのも納得です。

Powershell(7.3.6)でShift-JISのCSVファイルを読む

Powershellでスクリプトを作っていると結構困らされるのが文字コードの問題です。CSVを読み込んで加工後に書き出すであるとか、複数のCSVをマージするという作業ではスクリプトがよく使われるところだと思いますが、読み込むCSVと書き出すCSVの文字コードが異なると文字化けが起きる場合があります。

CSVの読み込みにImport-CSV、書き出しにExport-CSVを使う場合、コマンドレットで文字コードを明示的に指定することで問題を回避できます。

Microsoftのドキュメントによれば、Powershell7.1以降であれば以下の内容で文字コードを明示的に指定できるということです。

about_Character_Encoding

[Microsoft Learn]

上記ドキュメントの内容を踏まえてImport-CSVでShift-JISのCSVを読み込む場合、例えば以下のようなコマンドで読み込むことが可能です。

[Powershell]
#$csvFileは読み込むCSVファイルのファイル名を含むフルパス
Import-Csv -Path $csvFile.FullName -Encoding oem

文字エンコードoemというのは”MS-DOS およびコンソール プログラムの既定のエンコードを使用します。”ということですので、日本語Windowsを使っている場合、デフォルトではShift-JISです。

確認する場合はコマンドプロンプトを開き、”chcp”と入力します。”932”という値が返ってきた場合、Shift-JISに設定されています。

書き出す時にも明示的に文字エンコードが指定できるので、例えばUTF-8(BOMなし)にしたい場合は以下のようなコードで出力可能です。

[Powershell]
#$outputFileNameは書き出すCSVファイルのファイル名を含むフルパス
Export-Csv -Path $outputFileName -NoTypeInformation -Encoding utf8NoBOM

少々厄介なのはこの文字エンコードの指定方法がPowershellのバージョンによって異なることです。Powershellで何か作る場合はとりあえず最新版にしておくと、Microsoftのドキュメントと食い違いがなくなって良いと思います。

Windows Terminalで”ユーザー設定の読み込み中にエラーが発生しました”というエラーが表示される問題

Windows11(22H2)に内蔵されているWindows Terminalを起動した時に、毎回”ユーザー設定の読み込み中にエラーが発生しました 設定ファイルに同じGUIDを持つ複数のプロファイルが見つかりました。重複は無視されます…”というエラーが表示されて困っていました。

実用上特に問題はなさそうなのですが、うっとうしかったので原因を調べてみました。Windows Terminalは”動的プロファイル”という仕組みを採用しており、プロファイルを切り替えることで異なるシェルを切り替えて使うことができます。

例えば、Windows PowershellとWindows用LinuxのシェルであるWSLのシェルなどを切り替えて使えます。このシェルにはそれぞれGUID(ユニークな識別子)が振られますが、どうもこれが内部で重複しているということのようです。

Windows Terminalの設定画面左下に”JSONを開く”という項目があり、ここを開くとターミナルの設定がJSON形式で保存されています。GUI上でプロファイルごとの設定変更も可能になっていますが、GUIの設定ではGUIDを編集することはできなさそうでした。

その中にある”profiles”:で始まるブロックにプロファイルの設定が書かれています。確認してみると確かに複数のGUIDに重複があったので、重複がないように片方を変更してみたところエラーは解消しました。

<JSON>
{
    //↓guidを変更して重複がないようにする
    "guid": "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",
    "hidden": false,
    "name": "PowerShell",
    "source": "Windows.Terminal.PowershellCore"
}

通常重複することはないはずの値だと思うので気になりますが、エラーが解消して何よりでした。

Powershellでパスを結合する

Powershellを使っていると良くあるのがパスとフォルダ名や、パスとファイル名を結合するという場面です。ほとんどの場合パスやファイル名は変数に格納されているので、結合する時に少々工夫が必要になります。

例として、パスが”C:\hoge”でその中に格納された”piyo.txt”というファイルを開くコードを考えます。そのままパスとファイル名を結合すると間に\が無くて動かないので、何らかの方法で\を入れる必要があります。

力技

単純に文字列結合で行うのであればこんな感じになります。

[Powershell]
###パスとファイル名を結合して実行するスクリプト###
#パス変数
$Path = 'C:\hoge'

#ファイル名変数
$Filename = 'piyo.txt'

#パス結合
$Fullpath = $Path +'\'+ $Filename

#ファイル実行
#Invoke-Item $Fullpath

やっていることの分かりやすさという点では良いと思うのですが、結合する対象が3つ以上になると可読性が著しく悪くなるという難点があります。

Microsoft Learnのドキュメントに参考になる情報があったので、これを見ながら改良してみます。

文字列での変数の代入について知りたかったことのすべて

[Microsoft Learn]

変数を置換して結合

変数が含まれる場合でも、二重引用符でくくることによって変数を置換しつつ文字列結合を行うことができます。

最初のコードを書き換えるとこんな感じになります。

[Powershell]
###パスとファイル名を結合して実行するスクリプト###
#パス変数
$Path = 'C:\hoge'

#ファイル名変数
$Filename = 'piyo.txt'

#変数の置換を使ってパス結合
$Fullpath = "$Path\$Filename"

#ファイル実行
Invoke-Item $Fullpath

必ず二重引用符を使う必要があるというところがちょっと難しいですが、可読性という点では良いのではないかと思います。

Join-Pathコマンドを使う

パス結合用のコマンドがあるので、これを使う方法もあります。ドキュメント内で触れられている通り、この方法は\マークのことを気にしなくていいのが便利です。

[Powershell]
###パスとファイル名を結合して実行するスクリプト###
#パス変数
$Path = 'C:\hoge'

#ファイル名変数
$Filename = 'piyo.txt'

#Join-Pathコマンドを使ってパス結合
Join-Path -Path $Path -ChildPath $Filename

#ファイル実行
Invoke-Item $Fullpath

文字数的には冗長になりますが、パスを結合していることが明瞭なので分かりやすさはかなり優れていると思います。

○○について知りたかったことのすべてシリーズはオススメ

Microsoft LearnにKevin Marquette氏が書いているブログエントリが”○○について知りたかったことの全て”という題名で掲載されているのですが、いずれも非常に参考になります。

powershellexplained.com

[powershellexplained.com]

リファレンスほど難解ではなく、Kevin氏の私見も交えてコマンドや構文が紹介されているので今まで知らなかった記述方法やコマンドを知ることができます。

PowershellでTPMの情報を調べる

Windows11の動作要件になったことからよく知られるようになったTPM(Trusted Platform Module)ですが、PCに搭載されているのか、有効になっているのかなどの情報がPowershellで調べられることを最近知りました。

Get-Tpm

[Microsoft Docs]

コマンドレットは非常に簡単で、 Get-Tpm と入力するだけです。Powershellは管理者モードでないとこのコマンドレットが使えないのでその点だけは注意が必要です。

TPMの搭載/非搭載のほか、有効になっているかなどの情報を調べられます。Powershellですのでログオンスクリプトに組み込んで、組織内のPCのTPMの状態をまとめて調査するなんていうこともできそうです。

Powershellでアプリケーションのインストールを検出する

最近アプリケーションのインストール有無を検出して、その結果に応じて違う処理を行うという用事がありました。Powershellで自動的に判定と処理が行えたのでまとめておきます。

アプリケーションのインストール有無を検出する方法はいくつか考えられましたが、最も単純な”そのアプリケーションの実行ファイルが存在するか否か”を検出することにしました。

Test-Pathというコマンドレットがパスやファイルの有無を検出するのに使えそうだったので、以下のようなスクリプトにしてみました。

<Powershell>
#C:\Program Files\hoge\hoge.exeの検出
$Appexist = Test-Path 'C:\Program Files\hoge\hoge.exe'

#検出結果による分岐
if($Appexist -eq $true){
 #存在した時の処理
}else{
  #存在しなかった時の処理
}

こんな感じで大丈夫でした。Test-Pathの結果を変数に格納すると、パスやファイルが存在した場合はTrue、存在しない場合はFalseが代入されます。

Test-Pathはパス以外にも今回のように個別のファイルやレジストリエントリの存在も検出できるそうなので、かなり応用が利きそうなコマンドレットです。

Windows11のターミナルをPowershell7にする

このエントリを書いている時点での最新版のPowershellはバージョン7.2となっています。インストーラによるインストールや、コマンドのwingetによりインストールすることができます。

Windows への PowerShell のインストール

[Microsoft Docs]

Powershell7をインストールしても、スタートボタンから起動するWindowsターミナルでは従来のPowershell5系統のものが起動します。

Powershell7を起動する度にスタートメニューから選ぶのは面倒だと思っていたのですが、WindowsターミナルでPowershell7を起動する設定がありました。

設定方法は以下の通りです。

  1. Windowsターミナルを起動する
  2. キーボードで Ctrlキーと,キーを同時押しする
  3. ターミナルのオプション画面が開くので既定のプロファイルをPowershell7にする

Windowsターミナルの設定画面がWindowsの”設定”の中にあるのかと思っていましたが、そうではないようです。

Powershell5もWindows10と同時の登場で、当時は目新しさもありましたが気づけばリリース後7年近い年月が経っています。最新版にしておいた方が何かとトラブル回避にもなるのではないかと思います。

コマンドプロンプトでドライブを移動する

コマンドプロンプトでディレクトリを移動する際にはcdコマンドを使います。通常コマンドプロンプトを起動するとカレントディレクトリは”C:\Users\[ユーザ名]”になっています。

ここからカレントディレクトリを例えば”C:\hoge”にしたい場合、

[command prompt]
cd C:\hoge

と入力してEnterキーを押せばOKです。

Dドライブにも同様にhogeというフォルダがある場合、C:\hogeの部分をD:\hogeにすれば良さそうなのですが、この入力だとディレクトリを移動できません。ドライブに変更が加わる場合は/dオプションをつけて、

[command prompt]
cd /d D:\hoge

とすればOKです。

結構長いことコマンドプロンプトを触っているつもりでしたが、この操作は最近まで知りませんでした。コマンドを打ち込むときには大体Cドライブに用事があるからなのかもしれません。

ちなみにPowershellの場合は別ドライブであってもcdコマンドのみで移動ができるので簡単です。

【参考情報】

cd|Microsoft Docs

[ Microsoft Docs ]

Powershellによるファイルのリネーム

画像や音楽データを整理していると、一括でファイル名を変更したくなる場面があります。

私の場合はかつて採用していたファイル命名ルールに問題があることが分かり、新しく決めたファイル命名ルールに更新したくなるというケースが多いです。

リネーム用ツールなども色々あるとは思うのですが、柔軟に運用できるという点でこういう時はPowershellの出番です。

Powershellによるファイルのパラメータ取得

Powershellでリネームを行う場合は”Get-Childitem”コマンドレットを使うのが一般的ではないかと思います。このコマンドレットはフォルダ内のファイルのパラメータを一括取得してくれます。私はもっぱらファイルやフォルダの処理に使いますが、レジストリハイブや証明書ストアにも使えるそうです。

今回はC:\Photoに入っているyyyymmdd.jpgという命名ルールで名前のついている画像データをyymmdd.jpgに変更するという場面を想定します。

まず、フォルダ内のjpgファイルの情報を取得するには以下の通りの記述でOKです。

[Powershell]
Get-Childitem "C:\Photo" *.jpg

拡張子を指定するとその拡張子に当てはまるファイルだけのパラメータを取得できます。

Powershellによるパイプライン接続

次にリネーム処理を行うためのパイプライン接続を行います。パイプラインというのはちょっと難しそうですが、連続した処理を行う際に1つ目の処理と2つ目の処理を接続する役割を果たします。

例えば、 フォルダ内のファイル名を取得する→特定のファイル名でフィルタする→結果をcsv出力する といった処理があった場合、矢印部分にパイプライン処理が使われます。記述は簡単で、専用の演算子”|”を入れるだけです。

Powershellによるリネーム

リネームを行います。取得されたファイル名のデータはパイプライン経由で使用できます。パイプライン経由のデータは自動変数”$_”に格納されているので、この変数に対して処理を行えば大丈夫です。

ファイルのリネームの場合は”Rename-Item”コマンドレットが使えます。コードはこんな感じになります。

[Powershell]
Get-Childitem "C:\Photo" *.jpg | Rename-Item -NewName {$_.Name -replace "20", ""}

自動変数のNameパラメータに対して”20″を””(空)に置き換える処理を行っています。

続きを読む