This question already has answers here:
Can I get "&&" or "-and" to work in PowerShell?
(13 answers)
Closed 9 years ago.
What would be the equivalent statement of CMD's:
dir && cd ..
in Powershell?
I tried:
dir -and cd ..
but it throws error:
Get-ChildItem : A parameter cannot be found that matches parameter name
'and'.
At line:1 char:5
+ dir -and (cd ..)
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell
.Commands.GetChildItemCommand
There is not a direct equivalent in PowerShell of cmd.exe && which means "only execute right-hand side if the left-hand side succeeds." However you could write a short function to do the equivalent:
function IfTrue([ScriptBlock] $testExpression, [ScriptBlock] $runExpression) {
if ( & $testExpression ) { & $runExpression }
}
For example:
IfTrue { get-childitem "fileThatExists.txt" -ea SilentlyContinue } { "File exists..." }
If you want for the $testExpression to produce output, the IfTrue function can be written as follows:
function IfTrue([ScriptBlock] $testExpression, [ScriptBlock] $runExpression) {
& $testExpression
if ( $? ) { & $runExpression }
}
Bill
How about this?
dir; if ($?) {cd ..}
Running get-help about_automatic_variables | more explains:
$? Contains the execution status of the last operation. It contains
TRUE if the last operation succeeded and FALSE if it failed.
In PS, dir is just an alias for get-ChildItem; cd likewise for Set-Location.
edit: Same question here, including an answer straight from the horse's mouth.
Related
I wrote this function in my powershell profile:
function start {
if ($args[0] == "blender") {
Invoke-Item 'C:\Program Files\Blender Foundation\Blender 2.90\blender.exe'
}
}
It gives me the error below on line 1, at character 1:
Unable to find specified file
The file path is correct, since if I copy and paste the code Invoke-Item ... in my powershell it opens Blender normally. What can the problem be?
Thank you!
This happens, as your function name collides with Start-Process.:
get-alias -Name start
CommandType Name Version Source
----------- ---- ------- ------
Alias start -> Start-Process
Renaming the function is the first step for correction like so,
function start-myApps {
if ($args[0] == "MyApplication") {
Invoke-Item 'C:\Program Files\MyApplication\MyApplication.exe'
}
}
But that isn't enough, as it gives another an error:
Start-MyApps "MyApplication"
= : The term '=' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:2 char:19
+ if ($args[0] == "MyApplication") {
+ ~
+ CategoryInfo : ObjectNotFound: (=:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException
This one is caused because Powershell's equality comparison operator is -eq, not ==.
The working version has different name and operator like so,
function start-myApps {
if ($args[0] -eq "MyApplication") {
Invoke-Item 'C:\Program Files\MyApplication\MyApplication.exe'
}
}
My Python2.7 and pypy(using virtualenv) are all under windows 10 environment. From a README file in simulation software, below includes a 'for loop" script in Bash command. How would I convert below 'for loop" (three lines) Bash commands into equivalent Windows 10 Powershell commands?
"It's better to run several processes in parallel, for instance:
$ for i in {1..10}; do
$ time $pypy epto.py conf_epto/ $i > conf_epto/run-$i.log $
$ done
And then to obtain stats for all runs:
$pypy genStats.py conf_epto 10
This will output the stats to stdout and also generate dumps in gnuplot format."
I tried to run Powershell commands but face error:
(my-pypy-env) PS C:\Users\Acer\dev\pypy27home\my-pypy-env\SimpleDA-master> for ( $i = 1; $i -le 10; $i++) {
>> $pypy epto.py conf_epto/ $i > conf_epto/run-$i.log $
>> done}
At line:2 char:7
+ $pypy epto.py conf_epto/ $i > conf_epto/run-$i.log $
+ ~~~~~~~
Unexpected token 'epto.py' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
foreach ($i in 1..10) {
Measure-Command { & $pypy epto.py conf_epto/ $i > conf_epto/run-$i.log } | Out-Host
}
& $pypy genStats.py conf_epto 10
foreach ($i in 1..10) loops over the elements of array 1..10, created with PowerShell's range operator (..); the body of compound statements such as foreach is always enclosed in { ... } in PowerShell.
Measure-Command is PowerShell's equivalent to Bash's time builtin; the command whose execution to measure is passed as a script block ({ ... }).
The command inside the script block basically works the same as in Bash, except that in Windows PowerShell > creates "Unicode" - UTF16-LE - files by default (in PowerShell Core it is UTF-8 without a BOM). > is an effective alias of the Out-File cmdlet; to use a different encoding, pipe to it and use the -Encoding parameter (e.g., ... | Out-File -Encoding utf8 conf_epto/run-$i.log), though note that in Windows PowerShell -Encoding utf8 invariably creates a UTF-8 file with a BOM.
time outputs its result directly to the terminal rather than to stdout; so does Out-Host; it bypasses PowerShell's stdout equivalent, the success [output] stream.
PowerShell requires use of &, the call operator, for executing a command whose name or path is specified as a variable ($pypy); this requirement stems from PowerShell having two distinct parsing modes.
The following code I am writing to replace a batch script which I have below.
$Script:srcpath = ((Get-Location).Path)
$Script:configure = "$Script:srcpath\qtbase\configure.bat"
if (Get-Item "$Script:srcpath\qtbase\configure.bat" -WarningAction (Write-Warning "$Script:configure not found. Did you forget to run 'init-repository'?")) {
continue
}
I am try to rewrite the qt configure batch script:
set "srcpath=%~dp0"
set "configure=%srcpath%qtbase\configure.bat"
if not exist "%configure%" (
echo %configure% not found. Did you forget to run "init-repository"? >&2
exit /b 1
)
if not exist qtbase mkdir qtbase || exit /b 1
echo + cd qtbase
cd qtbase || exit /b 1
echo + %configure% -top-level %*
call %configure% -top-level %*
set err=%errorlevel%
cd ..
exit /b %err%
The error I am getting in PowerShell is the following:
Get-Item : Cannot bind parameter 'WarningAction' to the target. Exception setting
"WarningAction": "Object reference not set to an instance of an object."
At line:4 char:67
+ ... rningAction (Write-Warning "$Script:configure not found. Did you forg ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (:) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.GetItemCommand
The problem is the error being thrown is erroneous because the warning am calling is what should take its place to tell people that the item doesn't exist. so run "init-repository".
There isn't a good, "if not exist" in PowerShell.
Okay, there is, but it looks like this:
catch [System.Management.Automation.ItemNotFoundException]
which I am having problems with getting to work.
Why am I doing this before someone asks is because I feel that Microsoft will phase out CMD some time it's good to have updated scripts.
Why it's not working
WarningAction does not work like that.
From the about_CommonParameters documentation:
Determines how the cmdlet responds to a warning from the command. "Continue" is the default value. This parameter works only when the command generates a warning message. For example, this parameter works when a command contains the Write-Warning cmdlet.
So, essentially, the value of WarningAction is Continue by default and can be set to Inquire, SilentlyContinue or Stop. The value it is set to determine what action is taken if the Get-item command throws a warning, not what warning to write if Get-item throws a warning.
You can change the preference variable $WarningPreference to set the WarningAction within the current scope, or precede by a scope modifier.
How to make it work
Test-Path
I second Richard's comment to use Test-Path. This will return True or False, depending on whether it finds the file.
if (-not (Test-Path -Path "$Script:srcpath\qtbase\configure.bat")){
Write-Warning 'Does not exist!'
# do other stuff
continue
}else{
Get-Item $configure
}
try/catch
You could try to catch the exception thrown by Get-Item directly in a try/catch. In a similar way to WarningAction, there is ErrorAction which determines what to do if an error is thrown. Terminating error is required, so ErrorAction is set to Stop.
try{
Get-Item $configure -ErrorAction Stop
}catch [System.Management.Automation.ItemNotFoundException]{
Write-Output "Item not found"
# do other stuff
}catch{
Write-Output "Some other error"
$Error[0] # prints last error
}
I have bellow script
$ErrorActionPreference = "Stop";
while($true) {
try {
Write-Host "Step 1";
Dir C:\arts #Error
Write-Host "Step 2";
exit 0
break;
}
catch {
"Error in " + $_.InvocationInfo.ScriptName + " at line: " + $_.InvocationInfo.ScriptLineNumber + ", offset: " + $_.InvocationInfo.OffsetInLine + ".";
$Error
exit 1
break;
}
}
It stops on Dir C:\arts line and that is good for me. As I understood it happens cos I have line $ErrorActionPreference = "Stop"; at the beginning.
I also have some docker params
Param(
[Parameter(Mandatory=$True,ParameterSetName="Compose")]
[switch]$Compose,
[Parameter(Mandatory=$True,ParameterSetName="ComposeForDebug")]
[switch]$ComposeForDebug,
[Parameter(Mandatory=$True,ParameterSetName="StartDebugging")]
[switch]$StartDebugging,
[Parameter(Mandatory=$True,ParameterSetName="Build")]
[switch]$Build,
[Parameter(Mandatory=$True,ParameterSetName="Clean")]
[switch]$Clean,
[parameter(ParameterSetName="Compose")]
[Parameter(ParameterSetName="ComposeForDebug")]
[parameter(ParameterSetName="Build")]
[parameter(ParameterSetName="Clean")]
[ValidateNotNullOrEmpty()]
[String]$Environment = "Debug"
)
If I put $ErrorActionPreference = "Stop" line before docker params I will have error Cannot convert value "System.String" to type "System.Management.Automation.SwitchParameter". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.
In case if I put $ErrorActionPreference = "Stop"; line after docker params, script is continued to run and that is not that I want.
I do not know what I need to do here, so I will be grateful for any help
$ErrorActionPreference doesn't work with command line utilities like docker as they don't throw exceptions in PowerShell. You would have to use returncode/errorlevel or parse the output to handle those type of errors. Useful automatic variables:
$?
Contains the execution status of the last operation. It contains
TRUE if the last operation succeeded and FALSE if it failed.
$LastExitCode
Contains the exit code of the last Windows-based program that was run. Same as %errorlevel% in cmd.
If you detect an error, you can throw an exception to stop the script or use something like exit to stop the script. Example:
function Test-Error {
$ErrorActionPreference = "Stop"
Write-Host Before
ping -n 1 123.123.123.123
#If last command was not successfull.
#You can also have checked $lastexitcode, output etc.
if($? -eq $false) {
#Throw terminating error
#throw "Error"
#Or since we've chosen to stop on non-terminating errors, we could use:
Write-Error -ErrorId $LASTEXITCODE -Message "Ping failed"
}
Write-Host After
}
Test-Error
Output:
Before
Pinging 123.123.123.123 with 32 bytes of data:
Request timed out.
Ping statistics for 123.123.123.123:
Packets: Sent = 1, Received = 0, Lost = 1 (100% loss),
Test-Error : Ping failed
At line:22 char:1
+ Test-Error
+ ~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : 1,Test-Error
If you're creating a advanced function, you could set the default ErrorAction for the scope of the cmdlet like this:
function Test-Error {
[CmdLetBinding()]
param(
$Name = "World"
)
#If -ErrorAction is not specified by the user, use Stop for the scope of the function
if(-not $MyInvocation.BoundParameters.ContainsKey("ErrorAction")) { $ErrorActionPreference = "Stop" }
"Hello $Name ! My ErrorAction is: $ErrorActionPreference"
}
PS > $ErrorActionPreference
Continue
PS > Test-Error -ErrorAction Ignore
Hello World ! My ErrorAction is: Ignore
PS > Test-Error
Hello World ! My ErrorAction is: Stop
I'm trying to write a custom prompt for PowerShell and I was wondering how I would filter out the 1...n directories in the output of Get-Location.
function prompt {
"PS " + $(get-location) + "> "
}
So, if the path is too long I would like to omit some of the directories and just display PS...blah\blah> or something. I tried (get-container) - 1 but it doesn't work.
Use Split-Path with the -Leaf parameter if you want just the last element of a path:
function prompt {
"PS {0}> " -f (Split-Path -Leaf (Get-Location))
}
I wanted to make a more dynamic function. I do just basic string manipulation. You could do some logic nesting Split-Path but the string manipulation approach is just so much more terse. Since what you want to be returned wont be a fully validated path I feel better offering this solution.
Function Get-PartialPath($path, $depth){
If(Test-Path $path){
"PS {0}>" -f (($path -split "\\")[-$depth..-1] -join "\")
} else {
Write-Warning "$path is not a valid path"
}
}
Sample Function call
Get-PartialPath C:\temp\folder1\sfg 2
PS folder1\sfg>
So you can use this simple function. Pass is a string for the path. Assuming it is valid then it will carve up the path into as many trailing chunks as you want. We use -join to rebuild it. If you give a $depth number that is too high the whole path will be returned. So if you only wanted to have 3 folders being shown setting the $depth for 3.
Ansgar Wiechers' answer will give you the last directory but if you want a way to do multiple directories at the end of the filepath (using the triple dot notation) you can cast the directory path to a uri and then just get and join the segments:
function prompt {
$curPath = pwd
$pathUri = ([uri] $curPath.ToString())
if ($pathUri.Segments.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathUri.Segments[-2..-1].trim("/") -join ""
}
}
Or using just a string (no uri cast)
function prompt {
$curPath = pwd
$pathString = $curPath.Tostring().split('\') #Changed; no reason for escaping
if ($pathString.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathString[-2..-1] -join ""
}
}
$a = prompt
Write-Host $a
Then just change -2 to whatever you want to be the first directory and -le 3 to match. I typically use the uri cast when I have to run stuff through a browser or over connections to Linux machines (as it uses "/" as a path separator) but there is no reason to not use the string method for normal operations.