Daniel Martin
Daniel Martin

Reputation: 23548

Idiomatic powershell translation of (cd $dir && someCommand)

I have a python script that spits out JSON I'd like to capture with ConvertFrom-Json. Unfortunately, this script requires that I cd to a different directory before I run it. What's the idiomatic powershell way to do this?

This works:

$q = powershell.exe -Command "cd some\other\dir; python JsonMaker.py | ConvertFrom-Json"

As does this:

$cwd=Get-Location
cd some\other\dir
$q=python JsonMaker.py | ConvertFrom-Json
cd "$cwd"

But changing the current working directory seems dicey to me - what if the python script outputs malformed JSON, will I be left in some\other\dir ?

In the unix shell scripting world, I'd obviously do something like

(cd some/other/dir && python JsonMaker.py) | commandThatUsesJson

or read the input in with $(cd some/other/dir && python JsonMaker.py). However, in unix subshells are cheap. In powershell I see a noticeable delay to starting a subshell.

What's the approach long-time Powershell users take to something like this?

Upvotes: 3

Views: 397

Answers (3)

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200293

python.exe JsonMaker.py runs as a child process. Changes made to the current directory in a child process don't affect the parent. ConvertFrom-Json also doesn't affect the current directory. It converts a JSON string to an object representing the JSON data or throws a (non-terminating) error if the JSON string is malformed.

If you want to be on the safe side, run the conversion in a try block and put the statement to return from the temporary working directory after that block:

try {
  $q = python JsonMaker.py | ConvertFrom-Json
} catch {
  # error handling (optional)
}

cd "$cwd"

or in a finally clause:

try {
  $q = python JsonMaker.py | ConvertFrom-Json
} catch {
  # error handling (optional)
} finally {
  cd "$cwd"
}

As others have already mentioned, I'd use Push-Location and Pop-Location (or their aliases pushd and popd) as a simpler way of changing to a different working directory and returning to the original directory. The cmdlets work similar to the Unix shell commands pushd and popd.

I'd also recommend adding the extension to the executable name (to avoid unintentionally running different executable files with the same basename (e.g. python.cmd or python.com) and using the call operator (&). Running the command in a new powershell.exe process is not necessary, and would also return just a string representation of the object created from the JSON string instead of the object itself, which is probably not what you want.


Modified code:

Push-Location 'D:\some\other\dir'

try {
  $q = & python.exe JsonMaker.py | ConvertFrom-Json
} catch {
  # error handling (optional)
} finally {
  Pop-Location
}

or like this if you want to conditionally run the python script only if changing the directory was successful (thus fully emulating the behavior of &&):

try {
  Push-Location 'D:\some\other\dir' -ErrorAction Stop
  $q = & python.exe JsonMaker.py | ConvertFrom-Json
} catch {
  # error handling (optional)
} finally {
  Pop-Location
}

Upvotes: 0

Frode F.
Frode F.

Reputation: 54881

Your script looks fine to me. Unless ConvertFrom-Json throws a terminating exception (which I don't think it will), the script will continue and your cd $cwd line would reutnr you back.

You coulod also use Push-/Pop-Location, but it's basically just a "pretty" way of doing what you already have. Ex.

#Save location
Push-Location

#Script
Set-Location some\other\dir
python JsonMaker.py | ConvertFrom-Json

#Return to previous location
Pop-Location

Upvotes: 1

Mike Shepard
Mike Shepard

Reputation: 18156

I'd probably use pushd/popd:

pushd some\other\dir
$q=python JsonMaker.py | ConvertFrom-Json
popd 

Upvotes: 1

Related Questions