Кращий спосіб перевірити, чи існує шлях у PowerShell чи ні


123

Мені просто не подобається синтаксис:

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

і

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

особливо є занадто багато дужок і не дуже читабельних, коли перевіряється "не існує" для такого загального використання. Який кращий спосіб це зробити?

Оновлення: моє поточне рішення - використовувати псевдоніми для existта, not-existяк пояснено тут .

Пов’язана проблема у сховищі PowerShell: https://github.com/PowerShell/PowerShell/isissue/1970


2
Ви можете скористатисяtry{ Test-Path -EA Stop $path; #stuff to do if found } catch { # stuff to do if not found }
Еріс

1
Пов’язаний випуск: github.com/PowerShell/PowerShell/isissue/1970
orad

Відповіді:


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

Як ви вже показали себе, навпаки досить легко, просто псевдонім existsдля Test-Path:

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

1
Якщо $path"спеціальний", як, наприклад, у постачальника Powershell (подумайте HKLM: \ SOFTWARE \ ...), тоді це стане невдалим.
Еріс

4
@Eris питання спеціально просить перевірити, чи існує файл чи ні
Mathias R. Jessen

1
Однозначно, і створити нову командлет на льоту - акуратно. Майже настільки ж незбагненний, як псевдонім, але все ще дуже акуратний :)
Еріс

Приємно! Я думаю, що PS повинен додати підтримку для цього.
орад

4
@orad Я серйозно сумніваюся, що ти змусиш їх це зробити. "Занадто багато дужок" є дуже суб'єктивним міркуванням і насправді не заслуговує на відхилення від мовної конструкції / специфікації. FWIW, я також погоджуюся з конструкцією if / else, запропонованою @briantist, як кращою альтернативою, якщо ви дійсно так ненавидите дужки:if(Test-Path $path){}else{ # do your thing }
Mathias R. Jessen

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 ти маєш рацію! Насправді це негатив у трубопроводах у такому випадку. Мене зачарувало помилковим почуттям безпеки, не кидаючи винятку, коли воно фактично зазнало невдачі. Виклик оригінальним способом призводить до винятку, полегшуючи вирішення проблеми.
британіст

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
Незважаючи на те, відповів я це сам, я приймаю @ Матіас-R-Ессен в відповідь , тому що вона обробляє параметри краще.
орад

2

Ще один варіант - використання, 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

Це мій повноцінний спосіб для новачків

if ((Test-Path ".\Desktop\checkfile.txt") -ne "True") {
    Write-Host "Damn it"
} else {
    Write-Host "Yay"
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.