Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure IPython to use powershell instead of cmd

Searched for this, and couldn't seem to find anyone who'd already asked, so here goes.

I'm starting to switch over to IPython as my go-to shell on Windows 7, and I'd like to configure it so that the bang magic (!cmd) to pass a command to the system shell uses PowerShell instead of cmd.exe, so that I can take advantage of the enhancements MS has made, but without having to change my %COMSPEC% to PowerShell (my company still uses several CMD-based .BATs for automation, and I don't want to break those).

I'll post my questions here at the top, since the answer may not require any of the information below:

  1. Am I missing something? Is there a way to use a configuration file in IPython to specify the system command interpreter?
  2. If !1, is there a way that I'm missing to launch a child process from within PowerShell with a Machine-scope environment variable that's local to that process?

Investigation/Testing

Looking through the IPython code, I see that it's using os.system() to send the command to the shell, rather than subprocess.call(). That makes things somewhat more complicated, since os.system* just uses COMSPEC, where with subprocess*, you can specify the executable to use.

I tried loading PowerShell, setting the $env:comspec variable, then starting IPython from within that shell, but even though COMSPEC appears set, even within IPython, it looks like CMD is still being used:

[PS] C:\> $env:comspec
C:\Windows\system32\cmd.exe
[PS] C:\> $env:comspec = 'powershell.exe'
[PS] C:\> $env:comspec
powershell.exe
[PS] C:\> & 'C:\Python33\Scripts\ipython3.exe'
Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600 32 bit (Intel)]
Type "copyright", "credits" or "license" for more information.

IPython 1.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import os; os.getenv('comspec')
Out[1]: 'powershell.exe'

In [2]: !gci
'gci' is not recognized as an internal or external command,
operable program or batch file.

In [3]: os.system('gci')
'gci' is not recognized as an internal or external command,
operable program or batch file.
Out[3]: 1

That looks like the locally-modified COMSPEC is being passed to IPython (as a child of the PowerShell process that made the local change), but os.system looks to still be using the persistent setting.

I tried something similar, using [Environment]::SetEnvironmentVariable("ComSpec", 'powershell.exe', [System.EnvironmentVariableTarget]::User), in case I could get away with only changing a User-scope environment variable, but that didn't work, either (same results as above - os.getenv('ComSpec') shows powershell, but !-ed commands are sent to CMD).

Changing the Machine-scope environment variable seems to do what I want it to, but isn't a valid solution to me, for reasons mentioned before.

[PS] C:\> $env:comspec
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe
[PS] C:\> [Environment]::GetEnvironmentVariable('ComSpec','User')
[PS] C:\> [Environment]::GetEnvironmentVariable('ComSpec','Machine')
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe
[PS] C:\> & 'C:\Python33\Scripts\ipython3.exe'
Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600 32 bit (Intel)]
Type "copyright", "credits" or "license" for more information.

IPython 1.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: !gci env:ComSpec

Name                           Value
----                           -----
ComSpec                        C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe

Since that's not a viable ongoing solution, I tested some more by tinkering with settings in ConEmu*. I was able to do this without changing the global variable by setting the Explicit executable and Set ComSpec environment variable for child processes to selected value* flags in Settings > Startup > ComSpec, and providing the path to powershell.exe, but I'm not sure if that will have a negative impact on CMD consoles opened using ConEmu, since that setting is global (within ConEmu). This is what led me to ask Question 2, above, since I'm not sure how to set process/child-local, Machine-scope environment variables in PowerShell (if such a thing is even possible).

In the end, my dream goal would be for IPython to support specification of a command-shell interpreter via a config file, but to do that, os.system() can't be what's used. I'm planning to tinker with replacing it with subprocess.call() in my local copy (a'la this Python Doc) to test, but if anyone's already played with that, or if there's a sufficient advantage to the current model over using subprocess that I'm not aware of, I'd be glad to hear of it. It looks like this is already being done for non-Windows systems (GitHub), but I'm new enough to Python on this scale that I can't say for certain that nothing else would break if that change were used on the Windows side of the conditional, as well.

Footnote

Gah! Have to have 10+ reputation to properly document questions, apparently. Here are the links that I wasn't allowed to post above:

  • os.system - docs.python.org/3.3/library/os.html#os.system
  • subprocess - docs.python.org/3.3/library/subprocess.html
  • ConEmu - code.google.com/p/conemu-maximus5/
like image 274
jmwelch Avatar asked Oct 28 '13 20:10

jmwelch


People also ask

Can I use PowerShell instead of cmd?

To create the best command-line experience, PowerShell is now the command shell for File Explorer. It replaces Command Prompt (cmd.exe) in the Windows Logo Key + X menu, in File Explorer's File menu, and in the context menu that appears when you shift-right-click the whitespace in File Explorer.

How do I run IPython from Command Prompt?

The easiest way is to run easy_install ipython[all] as an administrator (start button, type cmd , shift+right click on “cmd.exe” and select “Run as administrator”). This installs the latest stable version of IPython including the main required and optional dependencies.

Can you run Python in PowerShell?

With your PowerShell command line open, enter python to run the Python 3 interpreter. (Some instructions prefer to use the command py or python3 , these should also work).


1 Answers

You can use IPython's script magics to execute PowerShell commands.

Define Magics

In your IPython profile, modify ipython_config.py to include the lines:

c.ScriptMagics.script_magics = ['powershell']
c.ScriptMagics.script_paths = {
        'powershell':'powershell.exe -noprofile -command -'}

-command - tells PowerShell to execute the stdin text as the command. You can leave out the -noprofile if you need to load your PS profile, but it will be slower.

You can then use %%powershell as a script cell magic.

In [1]: %%powershell
   ...: 1..5
1
2
3
4
5

Custom Magics

To make it a little easier to use, I defined my own magic function to handle PowerShell commands with both line and cell magics. (see: defining your own magics) This can be loaded automatically by placing the script in your IPython profile's startup folder. (i.e. ~\.ipython\profile_default\startup\05-powershell_magic.py)

from IPython.core.magic import register_line_cell_magic

from IPython import get_ipython
ipython = get_ipython()

@register_line_cell_magic
def ps(line, cell=None):
    "Magic that works both as %ps and as %%ps" 
    if cell is None:
        ipython.run_cell_magic('powershell', '--out posh_output' ,line)
        return posh_output.splitlines()
    else:
        return ipython.run_cell_magic('powershell', line, cell)

Usage Examples

To use the %ps line magic and return the output to a variable:

In [1]: ps_version = %ps $PSVersionTable.PSVersion.ToString()

In [2]: print(ps_version)
['2.0']

To use the %%ps cell magic and output to the console:

In [3]: %%ps
   ...: gci c:\

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d-r--         10/2/2013  10:39 AM            Program Files
d-r--         12/6/2013   1:44 PM            Program Files (x86)
d----          2/6/2014   4:33 PM            TEMP
d-r--        11/27/2013  11:10 AM            Users
d----         1/13/2014  11:21 AM            Windows

Cell magics can send output to a variable with --out <variable name>:

In [4]: %%ps --out process_name_id
   ...: $procs = gps| select Name, ID
   ...: $procs| ConvertTo-Csv -NoType| select -skip 1

In [5]: import csv

In [6]: list(csv.reader(process_name_id.splitlines()))
Out[6]:
[['7+ Taskbar Numberer', '3400'],
 ['acrotray', '3668'],
 ['armsvc', '1772'],
 ['audiodg', '4160'],
 ['AutoHotkeyU64', '472'],
 ['chrome', '6276'],
 ...
like image 93
Rynant Avatar answered Oct 28 '22 02:10

Rynant