連載
» 2008年09月12日 05時00分 UPDATE

Tech TIPS:WindowsのPowerShellでパス文字列を操作する

PowerShellでは、パス文字列を操作するためにさまざまなコマンドレットが用意されている。パスを結合する「Join-Path」、パスから特定の要素を切り出す「Split-Path」、相対パスから絶対パスを導出する「Convert-Path」、パスの実在を確認する「Test-Path」といったコマンドレットが使用できる。

[山田祥寛,著]
「Tech TIPS」のインデックス

連載目次

対象ソフトウェア:Windows PowerShell



解説

 PowerShellでスクリプトを記述していると、パス文字列を操作するような局面が少なからず発生する。例えば、パス文字列からドライブ名やファイル名だけを抽出したい、特定のフォルダ・パスとファイル名を結合して1つのパスを生成したい、などのケースである。このような操作は、もちろん、Stringクラス(System名前空間)を利用して純粋に文字列的に操作しても構わないが、実はなかなか手間な操作を強いられる。

 例えば、パスの結合1つをとっても、「C:\Windows」と「\data.txt」であれば単純に文字列同士を結合すればよいが、「C:\Windows\」と「\data.txt」であれば、片方の「\」を除去する必要があるし、「C:\Windows」と「data.txt」であれば、間に「\」を追加する必要がある。

 これはほんの一例にすぎないが、パス文字列を純粋に文字列的に操作するのは、単純に見えつつも意外と複雑であることはお分かりいただけるのではないかと思う。

 そこで登場するのが、PowerShellの「〜-Path」コマンドレットである。PowerShellでは、パス関連のさまざまなコマンドレットを用意しており、パスの操作をごく直感的に、かつ確実に行うことができる。

コマンドレット 概要
Convert-Path 相対パス→絶対パスの変換
Join-Path 与えられたパスを結合
Split-Path パスから特定の要素を抽出
Test-Path パスが存在するかを確認
パス関連のコマンドレット

 これらのコマンドレットは、その性質上、それ単体で利用することはそれほど多くないが、スクリプトを記述するうえでは知っておいて損はないものばかりだ。

操作方法

●与えられたパスを結合する――Join-Path

 まずは、与えられたパスを結合するJoin-Pathコマンドレットから。Join-Pathコマンドレットを利用することで、与えられたパスを結合することができる。簡単な例から見てみよう。

PS > Join-Path C:\Windows\ php.ini
C:\Windows\php.ini



 与えられたパス同士を結合した結果が返されることが確認できる。もっとも、このような例では(Join-Pathコマンドレットではなく)文字列連結を利用してもよいではないかと思われるかもしれないので、もう少しJoin-Pathコマンドレットのありがたみが分かる例も示しておこう。

PS > Join-Path C:\Windows php.ini
C:\Windows\php.ini
PS > Join-Path C:\Windows\ \php.ini
C:\Windows\php.ini



 このように、Join-Pathコマンドレットではパスの区切り文字(\)が連結する/されるパスのいずれに付いていてもよいし、双方に付いていても(付いていなくても)構わない。Join-Pathコマンドレットで区切り文字の有無を判定して、自動的に補完してくれるためだ。これは単純な文字列連結では得られない便利な機能だ。

 また、-Resolveオプションを付けると、結合されるパスが存在するかどうかを判定したうえで、パスを生成することができる。この機能はワイルドカードを含んだパスを結合させたい場合などに有効だ。以下のような例を見てみよう。

PS > Join-Path C:\Windows *.txt -Resolve
C:\Windows\OEWABLog.txt
C:\Windows\SchedLgU.Txt
C:\Windows\setuplog.txt
C:\Windows\T30DebugLogFile.txt
C:\Windows\UPGRADE.TXT



 ここでは「C:\Windows」フォルダ直下の拡張子が「.txt」である要素のパスを生成している。Get-ChildItemコマンドレット()で-Nameオプションを付与した場合の結果に似ているように思われるかもしれないが、Get-ChildItemコマンドレットではあくまでカレント・フォルダからの相対パスで返されるのに対して、Join-Pathコマンドレットでは絶対パスで返される点が異なる。また、Get-ChildItemコマンドレットでは-Recurseオプションを付与することで再帰的にパスを検索できるが、Join-Pathコマンドレットではワイルドカードでパスを表現している関係上、再帰的な検索には対応できないという制限がある。目的に応じて、両者をうまく使い分けるとよいだろう。

Get-ChildItemコマンドレットについては、TIPS「PowerShellのGet-ChildItemコマンドレットでファイル名の一覧を取得する(基本編)」「同(応用編)」参照。


 ちなみに、-Resolveオプションを付与した場合にもワイルドカードが必須というわけではない。この場合、Join-Pathコマンドレットは指定されたパスが存在するかどうかの判定を行い、存在する場合には結合した結果を、存在しない場合にはエラーを出力する。

PS > Join-Path C:\Windows system.ini -Resolve    …… パスが存在する場合
C:\Windows\system.ini
PS > Join-Path C:\Windows sys.ini -Resolve    …… パスが存在しない場合
Join-Path : パス 'C:\Windows\sys.ini' が存在しないため検出できません。
発生場所 行:1 文字:10
+ Join-Path  <<<< C:\Windows sys.ini -Resolve



●パスから特定の要素だけを抽出する――Split-Path

 Join-Pathコマンドレットとは逆に、与えられたパスから特定のパス要素だけを切り出すにはSplit-Pathコマンドレットを使用すればよい。

Split-Path 対象のパス パラメータ



 パラメータには、抽出するパス要素の種類を指定する。指定可能な値は、以下のとおりである。

パラメータ 取得する部分
-Qualifier ドライブ名
-noQualifier ドライブ名を除いた部分
-Parent 親フォルダ
-Leaf 末端の要素
Split-Pathコマンドレットで利用可能なパラメータ

 ではいくつか具体的な例を見てみよう。

PS > $path = "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config"
PS > Split-Path $path -Qualifier
C:
PS > Split-Path $path -noQualifier
\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config
PS > Split-Path $path -Parent
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG
PS > Split-Path $path -Leaf
machine.config



 -Qualifier、-noQualifierパラメータは特別な説明は不要だろう。注意してほしいのは、-Parent、-Leafパラメータを指定した場合だ。まず上の例では、ファイルを含むパスを対象としているので、-Parent、-Leafパラメータはそれぞれ「ファイルを含むフォルダ」「ファイル名」を取得している。これが(ファイルを含まない)フォルダ・パスを渡した場合の挙動は以下のようになる。

PS > $path = "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG"
PS > Split-Path $path -Parent
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
PS > Split-Path $path -Leaf
CONFIG



 対象のパスがフォルダを表している場合、-Parent、-Leafパラメータはそれぞれ「親フォルダまでのパス」「末端のフォルダ名」を返すわけだ。このように、フォルダ・パスを渡した場合とファイルパスを渡した場合の挙動の違いに注意してほしい。

●相対パスを絶対パスに変換する――Convert-Path

 Convert-Pathコマンドレットを利用することで、与えられた相対パスを絶対パスに変換することができる。Convert-Pathコマンドレットの使い方は簡単である。パラメータとして変換したい相対パスを指定すればよいだけだ。

PS > Set-Location C:\Windows
PS > Convert-Path .\Fonts
C:\WINDOWS\Fonts
PS > Convert-Path "..\Program Files"
C:\Program Files



 このようにカレント・フォルダを基点として、絶対パスが生成される。「.」はカレント・フォルダを、「..」はカレント・フォルダの上位フォルダを意味する記号である。

 ちなみに、Convert-Pathコマンドレットはパスを変換する際に、指定されたパスが存在するかをチェックする。従って、存在しないパス(以下の例では「.\Fonts2」)を変換しようとした場合には、エラーとなるので注意してほしい(つまり、これから作成しようとしているフォルダの絶対パスを生成することはできない)。

PS > Set-Location C:\Windows
PS > Convert-Path .\Fonts2
Convert-Path : パス 'C:\WINDOWS\Fonts2' が存在しないため検出できません。
発生場所 行:1 文字:13
+ Convert-Path  <<<< ./Fonts2



 また、Convert-Pathコマンドレットのパラメータにはワイルドカードを含めることも可能だ。例えば、以下のように記述することで「C:\Windows\System32」フォルダ直下のすべての要素について絶対パスを取得することができる。

PS > Set-Location C:\Windows
PS > Convert-Path .\System32\*
C:\WINDOWS\System32\1025
C:\WINDOWS\System32\3COM_DMI
C:\WINDOWS\System32\appmgmt
C:\WINDOWS\System32\bits
C:\WINDOWS\System32\Cache
……(以下省略)……



 同様に、2階層下の要素を絶対パスに変換したいという場合には、以下のように記述すればよい。この場合、取得されるのは2階層下の要素「のみ」で2階層下の要素「まで」パスが生成されるわけではない点に注意してほしい。

PS > Set-Location C:\Windows
PS > Convert-Path ./System32/*/*
C:\WINDOWS\System32\1033\dwintl.dll
C:\WINDOWS\System32\1041\dwintl.dll
C:\WINDOWS\System32\1041\vsjitdebuggerui.dll
C:\WINDOWS\System32\appmgmt\MACHINE
C:\WINDOWS\System32\appmgmt\S-1-5-21-2276771132-3414436671-3676186044-1006
C:\WINDOWS\System32\bits\qmgr.dll
……(以下省略)……



●指定されたパスが存在するかをチェックする――Test-Path

 Join-PathやConvert-Pathコマンドレットでもパスが存在するかどうかを判定することができるが、パスの結合や変換が不要で、存在チェックのみを行いたい場合には、Test-Pathコマンドレットを利用するのが便利だ。

PS > $path = "C:\Windows\system.ini"
PS > Test-Path $path    ……(1)
True
PS > Test-Path $path -PathType Leaf    ……(2)
True
PS > Test-Path $path -PathType Container    ……(3)
False



 上の例のように、ただ単にパスの存在をチェックしたいだけならば、Test-Pathコマンドレットのパラメータにチェック対象のパスを指定するだけでよい。-PathTypeオプションを指定することで、チェック対象となるパスの種類を指定することも可能だ。「Leaf」はファイルを、「Container」はフォルダを、それぞれ表す値である。

 つまり、ここでは(1)で特にパスの種類を指定せずに存在チェックのみを行い、(2)(3)ではそれぞれ指定されたファイル/フォルダが存在するかをチェックしているわけだ。この場合、指定された変数$pathの内容がファイルであるので、(1)(2)はTrueを返すが、フォルダの存在をチェックしている(3)はFalseを返す。

 次に、-Include/-Excludeオプションを使用した、もう少し複雑な例を見てみよう。-Includeオプションは指定されたパスに指定されたパターンの要素が存在するかを、逆に、-Excludeオプションは指定されたパターン以外の要素が存在するかをチェックするものだ。具体的な例を見てみよう。

PS > Test-Path C:\Windows\* -Include *.txt    ……(4)
True
PS > Test-Path C:\Windows\* -Include *.txt2    ……(5)
False
PS > Test-Path C:\Windows\tmp\* -Exclude *.tmp    ……(6)
False



 この場合、(4)(5)では「C:\Windows」フォルダの配下に拡張子が「.txt」「.txt2」であるファイルが存在するかを、(6)では「C:\Windows\tmp」フォルダの配下に「.tmp」以外のファイルが存在するかを、それぞれチェックするものだ(結果は環境によって異なる可能性がある**)。

**複数の条件で確認したい場合には「*.txt,*.dat」のようにカンマ区切りで記述すればよい。


 -Include/-Excludeオプションを使用する場合は、あくまでチェックの対象は(フォルダそのものではなく)フォルダ配下のファイルになるので、チェック対象のパスを指定する場合にも「C:\Windows」ではなく「C:\Windows\*」のようにする必要がある点に注意する。例えば、

PS > Test-Path C:\Windows -Include *.txt



のように記述すると、正しい結果が得られない。

 また、パスが存在するかではなく、単にパス文字列が妥当であるかどうかだけを確認したい場合には、-IsValidオプションを指定することもできる。

PS > Test-Path C:\Windows\nothing.ini    ……(7)
False
PS > Test-Path C:\Windows\nothing.ini -IsValid    ……(8)
True
PS > Test-Path C:\Windows\nothing<.ini -IsValid    ……(9)
False



 この例では「C:\Windows\nothing.ini」は存在しないので、(7)のTest-PathコマンドレットはFalseを返すが、-IsValidオプションを付与した(7)ではTrueを返す(パス文字列としては正しいため)。試しに、パスの途中に「<」を交ぜてみると、これはパス文字列としては正しくないので、確かにFalseが返される((9))。

「Tech TIPS」のインデックス

Tech TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。