PowerShellでパスが存在するかどうかを確認するより良い方法


122

私はただの構文が好きではありません:

if (Test-Path $path) { ... }

そして

if (-not (Test-Path $path)) { ... }
if (!(Test-Path $path)) { ... }

特に、このような一般的な用途で「存在しない」を確認する場合、括弧が多すぎて読みづらくなります。これを行うためのより良い方法は何ですか?

アップデート:私の現在のソリューションは、のためにエイリアスを使用することであるexistnot-existのように説明し、ここで

PowerShellリポジトリの関連問題:https : //github.com/PowerShell/PowerShell/issues/1970


2
使用できますtry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Eris

回答:


130

コマンドレット構文の代わりに、特にファイルが必要な場合は、File.Exists().NETメソッドを使用します。

if(![System.IO.File]::Exists($path)){
    # file with path $path doesn't exist
}

一方、の汎用否定エイリアスTest-Pathが必要な場合は、次のようにします。

# Gather command meta data from the original Cmdlet (in this case, Test-Path)
$TestPathCmd = Get-Command Test-Path
$TestPathCmdMetaData = New-Object System.Management.Automation.CommandMetadata $TestPathCmd

# Use the static ProxyCommand.GetParamBlock method to copy 
# Test-Path's param block and CmdletBinding attribute
$Binding = [System.Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($TestPathCmdMetaData)
$Params  = [System.Management.Automation.ProxyCommand]::GetParamBlock($TestPathCmdMetaData)

# Create wrapper for the command that proxies the parameters to Test-Path 
# using @PSBoundParameters, and negates any output with -not
$WrappedCommand = { 
    try { -not (Test-Path @PSBoundParameters) } catch { throw $_ }
}

# define your new function using the details above
$Function:notexists = '{0}param({1}) {2}' -f $Binding,$Params,$WrappedCommand

notexistsこれでとまったく同じようTest-Pathに動作しますが、常に反対の結果を返します。

PS C:\> Test-Path -Path "C:\Windows"
True
PS C:\> notexists -Path "C:\Windows"
False
PS C:\> notexists "C:\Windows" # positional parameter binding exactly like Test-Path
False

すでに自分を示さきたように、反対は、非常に簡単単なる別名であるexistsTest-Path

PS C:\> New-Alias exists Test-Path
PS C:\> exists -Path "C:\Windows"
True

1
$pathPowershellプロバイダーのような(特別な)場合(HKLM:\ SOFTWARE \ ...と考えてください)、これは無残に失敗します。
エリス

4
@Erisの質問は、ファイルが存在するかどうかを確認することを具体的に要求します
Mathias R. Jessen

1
もちろん、その場で新しいコマンドレットを作成するのは簡単です。エイリアスとほぼ同じように保守不可能ですが、それでも本当にすっきりします:)
Eris

いいね!PSはこれに対するネイティブサポートを追加する必要があると思います。
orad

4
@ orad私はあなたが彼らにそうすることを真剣に疑います。「括弧が多すぎます」は非常に主観的な推論であり、言語の設計/仕様から逸脱することにはあまり意味がありません。FWIW、私はまた、あなたが本当に括弧を嫌う場合は、より良い代替手段として@briantistによって提案された場合/ else構造に同意それほど:if(Test-Path $path){}else{ # do your thing }
マティアスR.ジェッセン

38

あなたが投稿したエイリアスソリューションは賢いですが、スクリプトでエイリアスを使用したくないのと同じ理由で、スクリプトでのエイリアスソリューションの使用には反対します。読みやすさを損なう傾向があります。

これをプロファイルに追加して、クイックコマンドを入力したり、シェルとして使用したりしたい場合は、それが理にかなっていることがわかります。

代わりにパイピングを検討するかもしれません:

if ($path | Test-Path) { ... }
if (-not ($path | Test-Path)) { ... }
if (!($path | Test-Path)) { ... }

または、否定的なアプローチの場合、コードに適切であれば、それを肯定的なチェックにしelseてから否定的に使用できます。

if (Test-Path $path) {
    throw "File already exists."
} else {
   # The thing you really wanted to do.
}

1
私はここでのパイプが好きですが、提案された否定のチェックは括弧なしでは正しくありませんFalse。そうでない場合は常にに評価されます。のようにする必要がありますif (-not ($path | Test-Path)) { ... }
orad

1
@oradあなたは正しい!実際、それはその場合の配管のネガティブです。それが失敗しているのに例外をスローしないので、私は誤った安心感に落ち着きました。元の方法で呼び出すと例外がスローされるため、問題を簡単に検出できます。
ブライアンティスト、2015

10

次のエイリアスを追加します。私はこれらがデフォルトでPowerShellで利用できるようにすべきだと思います:

function not-exist { -not (Test-Path $args) }
Set-Alias !exist not-exist -Option "Constant, AllScope"
Set-Alias exist Test-Path -Option "Constant, AllScope"

これにより、条件ステートメントは次のように変わります。

if (exist $path) { ... }

そして

if (not-exist $path)) { ... }
if (!exist $path)) { ... }

4
PowerShellチームに「既存の」エイリアスを追加する場合は、Microsoft Connectを通じて機能リクエストを送信する必要があります
Mathias R. Jessen

1
私は自分で答えましたが、@ mathias-r-jessenの答えを受け入れます。これは、パラメーターをより適切に処理するためです。
orad

2

もう1つのオプションは、IO.FileInfo次のタイプを使用するだけで簡単にファイルの情報を取得できるようにすることです。

PS > mkdir C:\Temp
PS > dir C:\Temp\
PS > [IO.FileInfo] $foo = 'C:\Temp\foo.txt'
PS > $foo.Exists
False
PS > New-TemporaryFile | Move-Item -Destination C:\Temp\foo.txt
PS > $foo.Refresh()
PS > $foo.Exists
True
PS > $foo | Select-Object *


Mode              : -a----
VersionInfo       : File:             C:\Temp\foo.txt
                    InternalName:
                    OriginalFilename:
                    FileVersion:
                    FileDescription:
                    Product:
                    ProductVersion:
                    Debug:            False
                    Patched:          False
                    PreRelease:       False
                    PrivateBuild:     False
                    SpecialBuild:     False
                    Language:

BaseName          : foo
Target            : {}
LinkType          :
Length            : 0
DirectoryName     : C:\Temp
Directory         : C:\Temp
IsReadOnly        : False
FullName          : C:\Temp\foo.txt
Extension         : .txt
Name              : foo.txt
Exists            : True
CreationTime      : 2/27/2019 8:57:33 AM
CreationTimeUtc   : 2/27/2019 1:57:33 PM
LastAccessTime    : 2/27/2019 8:57:33 AM
LastAccessTimeUtc : 2/27/2019 1:57:33 PM
LastWriteTime     : 2/27/2019 8:57:33 AM
LastWriteTimeUtc  : 2/27/2019 1:57:33 PM
Attributes        : Archive

私のブログの詳細。


1

ディレクトリへのパスが存在するかどうかを確認するには、次のパスを使用します。

$pathToDirectory = "c:\program files\blahblah\"
if (![System.IO.Directory]::Exists($pathToDirectory))
{
 mkdir $path1
}

ファイルへのパスが存在するかどうかを確認するには、@ Mathiasが提案したものを使用します。

[System.IO.File]::Exists($pathToAFile)

0

これはこれを行う私のPowershell初心者の方法です

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.