Powershell: Suggestion: Improve syntax of "if not exist"

Created on 19 Aug 2016  路  14Comments  路  Source: PowerShell/PowerShell

Related StackOverflow Q/A: http://stackoverflow.com/a/31888581/450913

The syntax of Test-Path in conditional statements is unnecessarily verbose (specially in the negated case), and prone to logical errors if parenthesis are not used properly:

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

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

I would like to suggest the following aliases to be supported natively in PowerShell:

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

With that, the conditional statements will change to:

if (exist $path) { ... }

and

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

Much more readable and avoids logical errors that happen in statements such as: if (-not $path | Test-Path) { ... }

Another idea is to implement them natively as "-Exist" and "-NotExist" operators.

Also as suggested by Derp McDerp in UserVoice item:

An alternative way of fixing this is for powershell to alter the grammar to allow the following syntax sugar:

if !(expr) { statements* } 
if -not (expr) { statements* }
Issue-Enhancement Up-for-Grabs WG-Language

Most helpful comment

if !(expr) { statements* }
if -not (expr) { statements* }

I like Derp's idea at first blush. It would apply to many more cases than just Test-Path.

All 14 comments

if !(expr) { statements* }
if -not (expr) { statements* }

I like Derp's idea at first blush. It would apply to many more cases than just Test-Path.

Combining them together, we'll have:

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

or if we get rid of the redundant dash:

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

The exist alias should work for any item, not just file paths, and never throw errors. Only return true or false.

The dash for

if -not (expr) {}

would likely remain. It's consistent with switch -regex, etc., for example (i.e. there's already precedent for having argument-like things for statements). When implemented the same, this would automatically allow you to shorten it as long as it remains unambiguous:

if -n (expr) {}

Technically this doesn't save any keystrokes, though, but I think it stands out more than the version with !.

As for the aliases, I don't think they are needed by default. People are free to define them if they feel Test-Path is too complicated, but I know at least one PowerShell user who needs Test-Path maybe once a year, and pretty much never interactively. Then there is the whole issue whether it should be exist or exists.

if -not (expr) { ... } would indeed be consistent with the switch statement grammar.

An alternative would be to introduce a negated if statement like perl's unless:

unless (expr) { ... } else { ... }

@omidkrad , good idea, I suggest writing this up as an RFC (https://github.com/powershell/powershell-rfc)

Congrats on PowerShell 7 release. I wanted to bump this up to be noticed. This is basically two different suggestions:

  1. Support the "if not" syntax:

    if !(expr) { statements* }
    if -not (expr) { statements* }
    
  2. Include the exist alias by default:

    Set-Alias exist Test-Path -Option "Constant, AllScope"
    

With these two we can do:

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

Personally I don't see a ton of value in adding that as a default alias, but maybe that's just me. 馃檪

However, I do like the idea of an if !(condition) / if -not (condition) syntax to simplify negated conditions. Another possible option would be a different keyword, like unless, but I'd consider that probably a less clear alternative overall.

if -not (){} seems preferable to me:

  • Re-uses existing keyword
  • Self-explanatory
  • Straightforward to implement both in the parser and compiler
  • There's already precedence for statements with parameter-like switched (switch stmts)

I personally use the not-exist alias, because it avoids the need for extra parentheses which if missed will silently cause logic bugs. (see first post 馃憜)

Yep, that's partly why we're considering ways to make them unnecessary.

There was a brief Twitter thread that has a little more discussion on the matter; myself and a couple other folks voiced support for the unless keyword which we might want to consider a bit more here.

It is a new keyword, but IMO it's ultimately the clearest option. 馃し

While I don't see any reason both couldn't be implemented, if I had to choose one I vote for unless mostly because you could make an alias to emulate the same behavior on 5.1 and keep your code relatively backwards compatible simply, whereas -not (expr) won't be 5.1 compatible.

From a usability standpoint though I like -not (expr) more, I find myself natively typing it all the time and then reminding myself I have to wrap it in parens :)

I quite like Unless it's like having while loops and do until loops; similar but convenient to have both. One would expect to be able to use else, elseUnless and elseIf with it

I would be more cautious about if not (something) {} because the next question will be why not have if (something) and (something) {} with the and or or on the outside
And it would mean IFwas the only place where the not was "outside" without a - sign
No-one is proposing there should be
while not (something)
or Where not { something}
etc.

I would avoid changing the general language pattern of "Execute all/some/none of what's between {} based on on what's between ()" with minimal other switches (switch is IIRC special here)

ifnot (no space) as alternative for unless would work.

Personally I don't see a lot of value for else or any variant thereof in combination for unless.

If you need that, it's probably more idiomatic to just use the if/elseif/else statements as is.

unless ($condition) { 
    Do-Things
}
else {
    Do-OtherThings
}

is the same as

if ($condition) {
    Do-OtherThings
}
else {
    Do-Things
}

I don't really see how adding else / elseUnless or allowing them to be used in tandem with unless is especially valuable tbh.

Personally I don't see a lot of value for else or any variant thereof in combination for unless.

I don't really see how adding else / elseUnless or allowing them to be used in tandem with unless is especially valuable tbh.

(a) people will want it
(b) your examples are semantically equivalent.
However consider

If (Object is OK)  {

200 lines of code

}
else {report error} 

By the time someone looking at the code reads down to report error they have forgotten what the if statement was... It's better, stylistically to write

If (Object is  not OK)  {report error}
else { 

200 lines of code

}

It's the same reason why we have do {} while () or repeat... until loops (do something, then think about the condition for repeating it) but we don't have do {} if () there is nothing preventing that - but it would tax what we can hold on our mental stack for long than our 7 seconds of short term memory.

My only reservation about

Unless (Object is OK)  {report error}
else { 

200 lines of code

}

Is it suggests that error is usual and OK is the special case.

Was this page helpful?
0 / 5 - 0 ratings