エンジニアの副業は週1からでも可能?副業の例や探し方も解説
- ITエンジニア
- 副業
従来のシェルとはまったく異なる、新しい設計のシェル「PowerShell」をご存知でしょうか。
Microsoftが提供するPowerShellは、自動化や構成管理に役立つ強力なクロスプラットフォームのシェルスクリプトです。
今回はPowerShellをはじめて使う人向けに、PowerShellの特徴や基本的な使い方を実際のコード付きで紹介していきます。
目次
PowerShellとは、OSを問わず動作するクロスプラットフォームの高機能シェルです。以前はWindows専用のシェルだったので「え、PowerShellってLinuxやmacOSでも動くの?」と驚く人もいるかもしれません。
バージョン6で基盤をクロスプラットフォームである「.NET Core」へと移したPowerShellは、現在「PowerShell Core」という名称でオープンソースソフトウェアとして公開されています。
一方、従来のWindows専用のPowerShellは「Windows PowerShell」と呼ばれています。PowerShell CoreとWindows PowerShellでは基盤とする.NETの種類が異なっており、できることが少し違うので気をつけましょう。
PowerShellは、Bashをはじめとする従来のシェルとは異なる性質を持ちます。どのように違うのかを以下で解説していきます。
Bashなど通常のシェルスクリプトは、文字列が入出力のベースです。パイプを利用したコマンド同士の連携では、文字列の受け渡しを行っています。
しかしPowerShellでは違います。PowerShellはデータのやり取りをオブジェクトで行います。コマンドの実行結果のみならず、パイプを行き交うのもすべて文字列ではなくオブジェクトです。
なおオブジェクトとは、データに関する「状態」と「機能」をひとまとめにした情報のまとまりを示す用語です。単なる文字列では「文字列に書かれている以上のこと」を知ることはできませんが、オブジェクトでデータを扱うことができるPowerShellは、単純な操作ひとつで多くの情報を獲得できます。
一般的にシェルスクリプトの文法は、PythonやJava、JavaScriptのような高機能な言語(高級言語)と比べると機能が限られ、文法もやや特殊である場合が多いです。一方で.NETをベースに動くPowerShellは、上述の高級言語の文法に近い書き方でスクリプトを組めます。
また書き方だけではなく、プログラムを組むときの「考え方」の面でもPowerShellは高級言語に近い設計になっています。
というのも、前述の通りPowerShellはオブジェクト指向の考え方を設計に取り入れています。そのためデータをオブジェクトとして扱うための「class構文」やclassのインスタンスを作成するための「New-Objectコマンドレット」など、オブジェクト指向らしい文法で高度なスクリプトを柔軟に作りこめるのです。
Bashなどで利用するコマンドのほとんどは、名前が省略されているため短く書けますが、一目見ただけでは意味が分からないものも多くあります。
しかしPowerShell自身が持っているコマンドは、必ず「動詞 + 名詞」の形式を取っており、PowerShellについて知識のない人でもすぐに意味が分かるようになっているのです。
従来のコマンドとPowerShellのコマンドを較してみましょう。
操作 | 従来のコマンド | PowerShellのコマンド |
---|---|---|
コピー | cp | Copy-Item |
移動 | mv | Move-Item |
文字列の検索 | grep | Select-String |
このようにPowerShellでは操作の意味とコマンドの命名が一致しているので、コマンドの意味を調べる手間を省けます。
なおPowerShellでは「動詞 + 名詞」形式のコマンドのことを、最小単位の操作という意味合いで「コマンドレット」と呼んでいます。コマンドレットを組み合わせれば、さまざまなタスクの自動化が可能です。
Bashなどの通常シェルと同じく、PowerShellはではコマンドを活用することでPC上の操作を行えます。
マウス移動とクリックの繰り返しが必要な定型作業なら、PowerShellで自動化スクリプト組んで、大幅な時間短縮ができます。
標準的なシェルスクリプトと比べて高級言語寄りの書き方で処理を実装できるPowerShellは、複雑なロジックを伴う高度な自動化処理もスマートに書けます。
ややこしい条件分岐やさまざまなプロパティへの同時アクセス、並列処理など「これまでシェルで実装するには負担が大きかった処理」を、PowerShellなら頭を悩ませることなく実現できます。
つまり、PowerShellはやや高度なタスクの自動化に向いているのです。
Excel自動化といえばVBAを思い浮かべる人が多いのではないでしょうか。しかし、ExcelをはじめとするMicrosoft Office製品を操作できるのはVBAだけではありません。PowerShellもまた、VBAのようにExcelやWordの操作を自動化することができるのです。
なぜMicrosoft Office用に作られたわけではないPowerShellが、Excelなどを操作できるのでしょうか。答えは、PowerShellが「COMオブジェクト」と呼ばれる「ソフトウェア利用のための外部接続用オブジェクト」を利用できるからです。
Microsoft Office製品には、他のアプリケーションからソフトを利用するためのCOMオブジェクトがあらかじめ用意されています。そのためオブジェクト指向言語としての機能を持っているPowerShellは、それを利用してExcelの操作などを行うことができるのです。
エクセルVBAでできること7選!超初心者から始める業務効率化入門
Workship MAGAZINE
PowerShellは自身の基盤である.NETのクラスやオブジェクトを利用することもできます。そのためデータ集合ひとつにしても、さまざまな性質の配列/連想配列/セット型を使い分けられるのです。
また.NETに用意されたキューやスタックなどの特殊なデータ集合クラスも、PowerShellから利用できます。
これらを使えば順番待ちの処理や、何らかの履歴を辿る処理などを簡単に実装できます。そのほかにも.NETのMathクラスを利用し、べき乗や対数の計算をすることも可能です。
できることを踏まえた上で、次は実際にPowerShellのスクリプトを自分で書いていくための基本文法を見ていきましょう。
まずはPowerShellの事始めとして、「Hello world.」を出力してみましょう。
PowerShellで画面(コマンドライン)に文字列を出力するには、「Write-Hostコマンドレット」を使います。
Write-Host 'Hello world.'
このように「Write-Host + 文字列」で文字列を出力することができます。
ちなみにWrite-Hostには文字色を変えるオプション「-ForegroundColor」があります。以下のように書くと、水色の文字で「Hello world.」を出力できます。
この他にも背景色を変える「-BackgroundColorオプション」や、実行時に改行しない「-NoNewlineオプション」などがあります。
PowerShellでは「$変数名 = 代入する値」という記法で、変数の宣言/代入を行えます。
Bashでは変数名と代入する値との間にスペースを入れるとエラーになりますが、PowerShellではスペースの有無を気にする必要はありません。そのときに応じてスペースの有無は書きやすい方で書けば大丈夫です。
# スペースの有無は実行結果に関係ない
#(どちらも変数「name」に文字列「sig」を代入している)
$name = 'sig'
$name='sig'
また設定した変数を文字列中で展開したいときには以下のように書きます。
$name = 'sig'
# 基本の変数展開
Write-Host "Hello ${name}."
# 変数名後に『変数名として使える文字』が来ない場合に使える変数展開
# (ドットは変数名に使えないので、変数名直後に書かれていても変数展開できる)
Write-Host "Hello $name."
このように「”${ 変数名 }” 」もしくは「”$変数名”」で変数を展開できます。
なお、文字列を囲む記号がダブルクォーテーションではなくシングルクォーテーションだと、変数展開はできません。
四則演算に特定の記号やコマンドが必要なBashとは異なり、PowerShellではごく一般的な書き方で四則演算ができます。
# 加算(1 足す 1 = 2)
1 + 2
# 減算(1 引く 2 = -1)
1 - 2
# 乗算(1 かける 2 = 2)
1 * 2
# 除算(1 割る 2 = 0.5)
1 / 2
#なお上記もスペースの有無は関係ない
また、.NETの算術クラス「Math」を使うことで絶対値やべき乗、平方根や自然数対数などを簡単に求めることもできます。
.NETのクラスを使うにはいくつか方法がありますが、今回のMathクラスはstaticクラス(インスタンス化せずにメソッドを呼び出せるクラス)なので、「 [クラス名]::メソッド名(引数) 」という記法でメソッドを実行できます。
# 絶対値(-3の絶対値 = 3)
[Math]::Abs(-3)
# べき乗(3の2乗 = 9)
[Math]::Pow(3, 2)
# 平方根(3の平方根 = 1.732...)
[Math]::Sqrt(3)
# 自然対数(3の自然対数 = 1.098...)
[Math]::Log(3)
「もし〜なら、〜する」という処理(条件分岐)を行うもっとも基本的な構文が「if文」です。
PowerShellで、if文は以下のように書きます。
param($num)
if ($num -gt 0) {
# 0より大きいなら(gt = greater than)
Write-Host '正の数です'
} elseif ($num -eq 0) {
# 0と等しいなら(eq = equal)
Write-Host 'ゼロです'
} else {
# 上記以外なら
Write-Host '負の数です'
}
このようにif, elseif, elseで、条件ごとに分岐を分けられます。
なお値の大小の比較に利用している「-gt(greater than)」などの演算子には、ほかにもさまざまな種類があります。以下で代表的なものを紹介します。
-lt(less than) | より小さい |
-le(less than or equal) | 以下 |
-gt(greater than) | より大きい |
-ge(greater than or equal) | 以上 |
-eq(equal) | 等しい |
-ne(not equal) | 等しくない |
-match | 正規表現パターンに一致する |
-like | ワイルドカード付き文字列パターンに一致する |
-contains | 配列が指定の値を含む |
-in | 値が指定の配列に含まれる |
また、最新のPowerShell Core 7.0.0ではif文を使わずに「三項演算子」という演算子で条件分岐処理を行うこともできます。
param($num)
# 三項演算子
$num -ge 0 ? (Write-Host '0以上') : (Write-Host '0未満')
シェルスクリプトに共通的な構文のひとつに「パイプ」があります。パイプを使えば、「左辺の処理の実行結果」を「右辺の処理の入力情報」として渡せます。
とくにPowerShellでは、パイプ間をオブジェクトが経由するという性質があります。そのため複雑な構造のデータを簡単に次のコマンドへ渡せるのです。
たとえば「現在のプロセスごとの仮想メモリの量を取得して、大きい順に並べ替えて表示したい」とき、PowerShellのパイプを使えば以下のように一行で実装できます。
Get-Process | Select-Object Name, VM | Sort-Object VM -Descending
# 出力結果
# Name VM
# ---- --
# pwsh 4312227840
# bash 104284163
# init 921600
# init 921600
# init 913408
この処理は以下の順で行われています。
- Get-Processコマンドレットで現在のプロセス一覧を取得する
- 現在のプロセス一覧情報のオブジェクトが、パイプ経由でSelect-Objectコマンドレットに渡る
- Select-Objectコマンドレットで、渡されたオブジェクトの中から指定したプロパティの値を持つ新しいオブジェクトを取得する
(ここでは「Nameプロパティ(プロセス名)」と「VMプロパティ(仮想メモリの量)」を指定)- 「Name」と「VM」の情報が入ったオブジェクトが、パイプ経由でSort-Objectコマンドレットに渡る
- Sort-Objectコマンドレットで、渡されたオブジェクトの配列を「VMプロパティ」をキーにして降順で並べ替える
このようにパイプで異なるコマンドレットを組み合わせれば、少し手の込んだ処理も簡単に実行できます。
ForEach-Objectコマンドレットを使えば、パイプを経由して渡ってきたオブジェクトの配列に対して、繰り返し処理を行えます。
たとえば「現在のプロセス一覧を取得して【プロセス名:CPU時間】という形式文字列をプロセスごとに出力したい」ときには、次のように書けます。
# ForEach-Objectコマンドレットは引数のスクリプトブロック(中括弧で囲われた処理のまとまり)を、
# パイプの左辺から渡されたオブジェクトの数だけ実行する
Get-Process | ForEach-Object {
# 変数$_には繰り返し処理中のオブジェクトが格納されている
# (つまり、Get-Processで取得したオブジェクトがひとつずつが$_に設定されていく)
$process_name = $_.Name
$cpu_time = $_.CPU
Write-Host "${process_name}:${cpu_time}"
}
# 出力結果
# bash:0.23
# init:0
# init:0
# init:2.84
# pwsh:25.89
また、最新のPowerShell Core 7.0.0では「-Parallelオプション」を使うことで、繰り返し処理を並列的にこなせます。
# ForEach-Objectコマンドレットは引数のスクリプトブロック(中括弧で囲われた処理のまとまり)を、
# パイプの左辺から渡されたオブジェクトの数だけ実行する
Get-Process | ForEach-Object -Parallel {
# 変数$_には繰り返し処理中のオブジェクトが格納されている
# (つまり、Get-Processで取得したオブジェクトがひとつずつが$_に設定されていく)
$process_name = $_.Name
$cpu_time = $_.CPU
Write-Host "${process_name}:${cpu_time}"
}
# 出力結果
# (並列で実行しているため、実行結果は処理が終わった順に出力される)
# init:0
# pwsh:27.36
# bash:0.23
# init:0
# init:2.87
なお、ForEach-Objectコマンドレットには「foreach」と「%」というふたつの省略記法があります。一文字でも早くスクリプトを書きたいときや、コードを簡潔に書きたいときに便利なので覚えておくのがおすすめです。
Where-Objectコマンドレットを使えば、パイプを経由して渡ってきたオブジェクトの配列に対して、フィルタリングをかけることができます。
たとえば「プロセス一覧から、ワーキングセットが1メガバイト以上のプロセスのみを取得したい」ときには、次のように書けます。
Get-Process | Where-Object {
# スクリプトブロック中の処理結果が正となるオブジェクトのみを抽出する
# (ForEach-Objectと同じで、変数$_には繰り返し処理中のオブジェクトが設定されている)
$_.WS -ge [Math]::Pow(1000, 2)
}
# 出力結果
# NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
# ------ ----- ----- ------ -- -- -----------
# 0 0.00 4.98 0.23 5237 …37 bash
# 0 0.00 133.83 102.44 9758 …37 pwsh
このように、スクリプトブロック(Where-Objectに引数として渡している中括弧で囲まれた処理のこと)内の条件判定が正となるオブジェクトのみを簡単に抽出できるのです。
またWhere-Objectにも「where」と「?」というふたつの省略記法があるので、覚えておくとよいでしょう。
UNIX系OSにおける「grepコマンド」の役割を果たすPowerShellコマンドレットが、「Select-String」です。Select-Stringコマンドレットを使えば、ファイル中から特定の文字が出現する箇所を簡単に抽出できます。
たとえば「現在のディレクトリにある.logファイルの中から”ERROR”が登場する行だけを抽出したい」ときには次のように書けます。
# ファイルのオブジェクトを渡すだけで、Select-Stringコマンドレットがそのファイルの中身を自動で走査してくれる
Get-Item *.log | Select-String "ERROR"
また、コマンドレットの実行結果として出力される文字列を一行ずつgrepコマンドにかけたいときには、Out-Stringコマンドレットと組み合わせ次のように書けます。
# 単にGet-Processコマンドレットを実行すると次のような表形式でプロセス一覧が表示される
Get-Process
# 出力結果
# NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
# ------ ----- ----- ------ -- -- -----------
# 0 0.00 4.98 0.23 5237 …37 bash
# 0 0.00 0.54 0.05 1 0 init
# 0 0.00 0.08 0.00 5235 …35 init
# 0 0.00 0.09 4.11 5236 …35 init
# 0 0.00 135.99 186.41 9758 …37 pwsh
# --------------------
# 上記の出力をそのまま文書としてSelect-Stringコマンドレットに渡したければ
# Out-Stringコマンドレットを「-Streamオプション」付きで利用する
Get-Process | Out-String -Stream | Select-String "sh"
# 出力結果
# 0 0.00 4.98 0.23 5237 …37 bash
# 0 0.00 135.99 186.52 9758 …37 pwsh
# --------------------
# なおOut-Stringコマンドレットを使わないままSelect-Stringコマンドレットに渡すと、
# ProcessオブジェクトをtoString()した結果の文字列から走査を行う
Get-Process | Select-String "sh"
# 出力結果
# System.Diagnostics.Process (bash)
# System.Diagnostics.Process (pwsh)
「No.001~No.100のディレクトリを作りたい」とき、以下を利用すれば簡単にPowerShellで連番ディレクトリをつくれます。
# 0..100で「0から100までの数値」の配列を取得できる
$numlist = 0..100
$list | ForEach-Object {
# .ToString()メソッドに引数で'000'を指定すると、
# 数値の桁数を100の位までゼロ埋めしてくれる
# (10の位までなら'00'を指定)
$dirname = $_.ToString('000')
# New-Itemコマンドレットを『-ItemType Directory』で使うと、
# 指定した名前の新規ディレクトリを作成してくれる
New-Item -ItemType Directory "No.$dirname"
}
ファイルの中身を読み出す「Get-Contentコマンドレット」には、ファイルをリアルタイムで監視する「-Waitオプション」があります。
-Waitオプション付きのGet-Contentコマンドレットを「Select-Stringコマンドレット」や「ForEach-Objectコマンドレット」と組み合わせれば、「ログを監視して”ERROR”が登場したときの現在日時を出力する」処理などを簡単に実装できます。
# 以下の順で処理が進む
# 1. Get-Contentコマンドレットの『-Waitオプション』で指定ファイルを常時監視する
# ※ -Tailオプションは【ファイル末尾から指定行読み込む】ためのオプション.
# 今回は【既に書かれている"ERROR"の行を取得しない】ために、あえて『-Tail 0』をしている
# (Wait中に新たに書き込まれた行はすべて読み込まれる)
# 2. Select-Stringコマンドレットで、書き込まれた行のうち"ERROR"が登場する行だけを抽出
# 3. 抽出したタイミングでForEach-Objectで指定した処理(現在日時の出力)を実行
Get-Content ./hoge.log -Wait -Tail 0 | Select-String "ERROR" | ForEach-Object {
# "2020-08-01 09:00:01"のような形式で現在の日時を取得
$now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
# 画面に現在日時を出力
Write-Host $now
}
このようにGet-Content -Wait で読み込まれた行は、すぐにパイプを経由して後続のコマンドレットを実行させられるのです。
上記の処理を応用すれば「特定の文字が登場したときにログをコピーして履歴を残す」「特定の文字の登場をトリガーにして、アプリケーションの再起動を行う」などのタスク自動化も実装できます。
PowerShellには、PowerShell上での作業ログを取れる「Start-Transcript」というコマンドレットがあります。Start-Transcriptコマンドレットを使えば、PowerShell上のコマンドの実行記録やその結果をすべて指定したファイルに書き込んでくれます。
実際に「作業ログ_年月日-時分.log」という形式のファイル名で適当なPowerShell上の操作をログに記録してみましょう。
$now = Get-Date -Format 'yyyyMMdd-HHmm';
$logname = "作業ログ_$now.log"
# PowerShellの記録開始
Start-Transcript $logname
# ---- 何らかの処理 ----
# (以降、PowerShell上の操作が丸ごとログに記録されていく)
# 処理の例;ワーキングセットの量が大きい上位3プロセスをコマンドライン上に表示
Get-Process | Sort-Object WS -Descending -Top 3
# ---- 処理ここまで ----
# PowerShellの記録終了(以下コマンドレットでログへの記録が停止する)
Stop-Transcript
# _____________________________________
#
# ▼ これだけで以下の作業ログが記録される
#
# **********************
# PowerShell transcript start
# Start time: 20200706150134
# Username: LAPTOP-B7H0NMER\sig
# RunAs User: LAPTOP-B7H0NMER\sig
# Configuration Name:
# Machine: LAPTOP-B7H0NMER (Unix 4.19.104.0)
# Host Application: /home/sig/powershell/pwsh.dll
# Process ID: 11122
# PSVersion: 7.0.0
# PSEdition: Core
# GitCommitId: 7.0.0
# OS: Linux 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020
# Platform: Unix
# PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.10032.0, 6.0.0, 6.1.0, 6.2.0, 7.0.0
# PSRemotingProtocolVersion: 2.3
# SerializationVersion: 1.1.0.1
# WSManStackVersion: 3.0
# **********************
# Transcript started, output file is 作業ログ_20200706-1501
# PS /home/sig/work/02_powershell_introduction> Get-Process | Sort-Object WS -Descending -Top 3
#
# NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
# ------ ----- ----- ------ -- -- -----------
# 0 0.00 117.31 11.00 11122 …82 pwsh
# 0 0.00 109.53 3.09 13803 …97 pwsh
# 0 0.00 5.24 0.08 11082 …82 bash
#
# PS /home/sig/work/02_powershell_introduction> Stop-Transcript
# **********************
# PowerShell transcript end
# End time: 20200706150148
# **********************
このように「Start-Transcriptコマンドレット」と「Stop-Transcriptコマンドレット」を使うだけで、簡単にPowerShell上の操作を記録できるのです。
Web-APIをテストするとき、条件を変えて何度も手動でHTTPリクエストを送信するのはなかなか時間がかかるものです。そんなとき「Invoke-WebRequestコマンドレット」を使えば、簡単にHTTPリクエストを送って、テストの自動化を図ることができます。
例として、日本郵便が公開する【郵便番号API】への郵便番号テストをPowerShellで実装してみましょう。(負荷をかけすぎると良くないので、以下では4件分の郵便番号だけをテストしています)
5..8 | % {
# URLの生成(例として日本郵便が公開する【郵便番号API】へのリクエストURLを作成)
$url = "https://api.zipaddress.net/?zipcode=904000$_"
# HTTPリクエストの送信
Write-Host "テスト実行(URL:${url})"
$res = Invoke-WebRequest $url
# HTTPレスポンスの応答本文のJSONをオブジェクトに変換する
$obj = ConvertFrom-Json $res.Content
# テスト結果を出力
if ( $obj.code -eq '200' ) {
Write-Host 'コード値正常' -ForegroundColor Cyan
Write-Host ('住所:' + $obj.data.fullAddress) -ForegroundColor Cyan
} elseif ( $obj.code -eq '404' ) {
Write-Host ('コード値異常:' + $obj.code ) -ForegroundColor Red
Write-Host '住所が存在しません' -ForegroundColor Red
} else {
Write-Host ('コード値異常:' + $obj.code ) -ForegroundColor DarkRed
}
# 次のURLをテストする前に2秒開ける(郵便番号APIサーバへの負荷防止)
Start-Sleep -Seconds 2
}
これを実行すると、以下のような結果が得られます。
このように「Invoke-WebRequestコマンドレット」はForEach-Objectコマンドレットなどと組み合わせることで、複数ケースのWeb-APIテストを自動で実行できます。
また上記コード中で利用している「ConverFrom-Jsonコマンドレット」も、APIの応答JSONをPowerShellですぐに柔軟に扱えるオブジェクト形式に変えてくれるので、Web-APIのテストをしたいときには重宝します。
Bashなど従来のシェルと比べ、クロスプラットフォームシェルであるPowerShellには、以下のような特徴があります。
その高機能さから、PowerShellは少々複雑なタスクでも簡単に実装することが可能です。
とくに「ForEach-Object」や「Where-Object」コマンドレットなど、パイプを経由したオブジェクトの加工処理に慣れてくれば、さまざまな業務を自動化/効率化できるようになります。
BashやZshしか使ったことがないという人から、シェル全般に触れたことのない人まで幅広くおすすめできるシェルスクリプトがPowerShellです。これを気に、興味をもった人はぜひPowerShellを使ってみてください。
(執筆:sig_Left 編集:Sato Mizuki)
シェルスクリプト(Bash)入門。できること、基礎文法、業務自動化の方法を解説【事例あり】
Workship MAGAZINE