WindowsのPowerShellでパス文字列を操作する:Tech TIPS
PowerShellでは、パス文字列を操作するためにさまざまなコマンドレットが用意されている。パスを結合する「Join-Path」、パスから特定の要素を切り出す「Split-Path」、相対パスから絶対パスを導出する「Convert-Path」、パスの実在を確認する「Test-Path」といったコマンドレットが使用できる。
対象ソフトウェア: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))。
■この記事と関連性の高い別の記事
- PowerShellでファイルやフォルダーを操作する(TIPS)
- PowerShellのGet-ChildItemコマンドレットでファイル名の一覧を取得する(基本編)(TIPS)
- PowerShellを使って指定したファイルをインターネットからダウンロードする(TIPS)
- PowerShellのGet-ChildItemコマンドレットでファイル名の一覧を取得する(応用編)(TIPS)
- Windowsのwhereコマンドでファイルを探す(TIPS)
Copyright© Digital Advantage Corp. All Rights Reserved.