作成日:2021/12/08 更新日:2022/01/04

概要

初学者がpowershellを学習にするにあたって躓きやすい注意点を記載しています。

注意点1 オブジェクト指向言語だということ

コマンドプロンプトやbash等の習熟者がpowershellを学習するにあたり躓きやすいのがこの点だと思われます。

powershellはオブジェクト指向を取り入れた言語であり、コマンドの実行結果の多くが文字列ではなくクラスのインスタンスを戻します。 ざっくりですがクラスのインスタンスとは変数と関数の集合を変数に入れたもの、と考えると理解しやすいと思います。

例えば、powershellでbashの「ls」コマンドに相当するのはGet-ChildItemコマンドになりますが、 Get-ChildItemで「*.txt」の情報を取得しようとすると以下の結果が表示されます。

PS D:\temp> Get-ChildItem *.txt


    ディレクトリ: D:\temp


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/06/22     17:57              0 aaa.txt
-a----        2021/06/22     17:57              0 abc.txt

表示された「aaa.txt」や「abc.txt」はそれぞれ変数と関数を持っており、 どのクラスでどういった変数や関数を持っているかはget-memberコマンドで確認できます。 (TypeNameがクラス、MemberType がMethodと表示されるものが関数、~Propertyと表示されているのが変数)

Get-ChildItemのメンバーを調べる
PS D:\temp> Get-ChildItem *.txt | get-member


   TypeName: System.IO.FileInfo

Name                      MemberType     Definition
----                      ----------     ----------
LinkType                  CodeProperty   System.String LinkType{get=GetLinkType;}
Mode                      CodeProperty   System.String Mode{get=Mode;}
Target                    CodeProperty   System.Collections.Generic.IEnumerable`1[[System.String, mscorlib, Version=...
AppendText                Method         System.IO.StreamWriter AppendText()
CopyTo                    Method         System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(s...
Create                    Method         System.IO.FileStream Create()
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateText                Method         System.IO.StreamWriter CreateText()
Decrypt                   Method         void Decrypt()
Delete                    Method         void Delete()
Encrypt                   Method         void Encrypt()
Equals                    Method         bool Equals(System.Object obj)
GetAccessControl          Method         System.Security.AccessControl.FileSecurity GetAccessControl(), System.Secur...
GetHashCode               Method         int GetHashCode()
GetLifetimeService        Method         System.Object GetLifetimeService()
GetObjectData             Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Sys...
GetType                   Method         type GetType()
InitializeLifetimeService Method         System.Object InitializeLifetimeService()
MoveTo                    Method         void MoveTo(string destFileName)
Open                      Method         System.IO.FileStream Open(System.IO.FileMode mode), System.IO.FileStream Op...
OpenRead                  Method         System.IO.FileStream OpenRead()
OpenText                  Method         System.IO.StreamReader OpenText()
OpenWrite                 Method         System.IO.FileStream OpenWrite()
Refresh                   Method         void Refresh()
Replace                   Method         System.IO.FileInfo Replace(string destinationFileName, string destinationBa...
SetAccessControl          Method         void SetAccessControl(System.Security.AccessControl.FileSecurity fileSecurity)
ToString                  Method         string ToString()
PSChildName               NoteProperty   string PSChildName=aaa.txt
PSDrive                   NoteProperty   PSDriveInfo PSDrive=D
PSIsContainer             NoteProperty   bool PSIsContainer=False
PSParentPath              NoteProperty   string PSParentPath=Microsoft.PowerShell.Core\FileSystem::D:\temp
PSPath                    NoteProperty   string PSPath=Microsoft.PowerShell.Core\FileSystem::D:\temp\aaa.txt
PSProvider                NoteProperty   ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
Attributes                Property       System.IO.FileAttributes Attributes {get;set;}
CreationTime              Property       datetime CreationTime {get;set;}
CreationTimeUtc           Property       datetime CreationTimeUtc {get;set;}
Directory                 Property       System.IO.DirectoryInfo Directory {get;}
DirectoryName             Property       string DirectoryName {get;}
Exists                    Property       bool Exists {get;}
Extension                 Property       string Extension {get;}
FullName                  Property       string FullName {get;}
IsReadOnly                Property       bool IsReadOnly {get;set;}
LastAccessTime            Property       datetime LastAccessTime {get;set;}
LastAccessTimeUtc         Property       datetime LastAccessTimeUtc {get;set;}
LastWriteTime             Property       datetime LastWriteTime {get;set;}
LastWriteTimeUtc          Property       datetime LastWriteTimeUtc {get;set;}
Length                    Property       long Length {get;}
Name                      Property       string Name {get;}
BaseName                  ScriptProperty System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Re...
VersionInfo               ScriptProperty System.Object VersionInfo {get=[System.Diagnostics.FileVersionInfo]::GetVer...

このようにコマンドごとに様々なプロパティがクラスが定義されており、これらをwhere-object(条件による行の絞り込み)やselect-object(抽出項目の指定)で自由に抽出、選択することができます

ファイルのフルパス(fullnameプロパティ)を選択
PS D:\temp> Get-ChildItem *.txt |select-object fullname

FullName
--------
D:\temp\aaa.txt
D:\temp\abc.txt
ディレクトリパスとファイル名(DirectoryName、nameプロパティ)を選択
PS D:\temp> Get-ChildItem *.txt |select-object DirectoryName,name

DirectoryName Name
------------- ----
D:\temp       aaa.txt
D:\temp       abc.txt
ファイル名に"abc"を含むものを抽出($_.name -like 'abc')し、ベースファイル名と拡張子(basename、Extensionプロパティ)を選択
PS D:\temp> Get-ChildItem *.txt |where-object {$_.name -like '*abc*' }|select-object basename,Extension

BaseName Extension
-------- ---------
abc      .txt

注意点2 性能が悪い

上述のオブジェクト指向であることに起因しているのかもしれませんが powershellはcmdやbashと比べると処理が遅いことが多いです。

感覚的には処理対象が数万件規模になってくると遅さを体感するケースが多いです。 工夫すれば改善するケースも多くありますが言語として性能が求められる処理向きではありませんで注意が必要です。

注意点3 一般感覚と微妙にずれた挙動

powershellは他の言語を使っているとなぜこの仕様にしたのだろうか・・と疑問に思う挙動にかなり遭遇します。

  • 関数内の標準出力が関数の戻り値になる

以下の関数を実行した戻り値は「3」にならず'1+2='と3の配列が戻ります。 型宣言を強制しない言語のためこのコードもエラーにはなりません。

関数の戻り値テスト
function func1($a,$b){
    echo ("$a+$b=")
    return $a+$b 
}
# 引数に1と2を渡す
$ret = func1 1 2
結果
PS D:\git\powreshell-script> $ret

1+2=
3
'1+2='という文字と 3 という数字の配列が戻っている
  • リダイレクト(>)の出力文字コードがUTF16

  • ソースコードの文字コードがデフォルトBOM有のUTF8(powrshell6からBOM無UTF8になりました)

★ORACLE案件承ります