配置IPython使用PowerShell而不是命令提示符cmd

17

我搜索过,但似乎找不到已经问过这个问题的任何人,那么就由我来问一下吧。

我开始将IPython作为我的Windows 7默认 shell,我想将感叹符魔法(!cmd)配置为使用PowerShell而不是cmd.exe来传递命令给系统shell,这样我可以利用微软所做的增强功能,但又不必更改我的 %COMSPEC% 到 PowerShell(因为我的公司仍然使用几个基于CMD的.BAT文件进行自动化,我不想破坏这些文件)。

我将我的问题放在这里,因为答案可能不需要下面的任何信息:

  1. 我有遗漏吗?IPython中是否有使用配置文件指定系统命令解释器的方法?
  2. 如果!1,则是否有我忽略的方式可以从PowerShell启动一个子进程,并具有对该进程局部的机器范围环境变量?

调查/测试

浏览IPython代码,我发现它使用os.system()将命令发送到shell,而不是subprocess.call()。这使事情变得更加复杂,因为os.system*只使用COMSPEC,而使用subprocess*可以指定要使用的可执行文件。

我尝试加载PowerShell,设置$env:comspec变量,然后从该shell中启动IPython,但即使在IPython中,COMSPEC看起来已设置,CMD仍然被使用:

[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

看起来像是本地修改的COMSPEC被传递给了IPython(作为进行本地更改的PowerShell进程的子进程),但os.system似乎仍在使用持久设置。

我尝试过类似的方法,使用[Environment] :: SetEnvironmentVariable("ComSpec",'powershell.exe',[System.EnvironmentVariableTarget]::User),以便只更改用户范围的环境变量,但这也不起作用(与上面相同的结果 - os.getenv('ComSpec') 显示powershell,但!-ed命令被发送到CMD)。

更改机器范围的环境变量似乎符合我的要求,但出于之前提到的原因,这不是一个有效的解决方案。

[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

由于这不是一个可行的持续解决方案,因此我通过调整ConEmu *中的设置进行了更多测试。在“设置>启动>ComSpec”中设置显式可执行文件为子进程设置ComSpec环境变量为所选值*标志,并提供powershell.exe的路径,我能够做到这一点而不改变全局变量,但我不确定这是否会对使用ConEmu打开的CMD控制台产生负面影响,因为该设置是全局的(在ConEmu内)。这就是导致我问问题2的原因,因为我不知道如何在PowerShell中设置进程/子进程本地、机器范围的环境变量(如果这样的事情真的存在)。

最终,我的梦想目标是IPython通过配置文件支持指定命令行解释器,但要实现这一点,不能使用os.system()。我计划在本地副本中使用subprocess.call()替换它(参考Python文档),以进行测试,但如果有人已经玩过这个,或者如果当前模型与使用subprocess相比具有足够的优势,并且我不知道,请告诉我。看起来这已经在非Windows系统上完成(GitHub),但我在这个规模上的Python是新手,不能确定如果在条件语句的Windows部分使用该更改是否会导致其他问题。

脚注

糟了!似乎必须拥有10+声望才能适当记录问题。这里是我无法在上面发布的链接:

  • 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/

1
如果您想为在Windows上使用subprocess做出贡献,我们一定会考虑。我认为它还没有被更改的主要原因是我们没有太多的Windows用户来测试这个更改。 - Thomas K
@ThomasK - 很好。我不想去做这样的更改,只是发现使用 os.system() 是一个经过计算的选择,并且一直存在。 - jmwelch
3个回答

15
你可以使用IPython的脚本魔法来执行PowerShell命令。
定义魔法
在你的IPython配置文件中,修改ipython_config.py包含以下行:
c.ScriptMagics.script_magics = ['powershell']
c.ScriptMagics.script_paths = {
        'powershell':'powershell.exe -noprofile -command -'}

-command - 告诉 PowerShell 将 stdin 文本作为命令执行。如果需要加载您的 PS 配置文件,可以省略 -noprofile,但速度会变慢。

然后,您可以使用 %%powershell 作为 script cell magic

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

自定义魔法函数

为了让使用变得更加容易,我定义了自己的魔法函数来处理带有行和单元格魔法的PowerShell命令。(参见: 定义自己的魔法函数) 可以通过将脚本放置在IPython配置文件的启动文件夹中来自动加载此函数。(即 ~\.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)

使用示例

使用%ps行魔法并将输出返回到一个变量:

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

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

要使用%%ps单元格魔法并输出到控制台:

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

单元格魔术命令可以使用--out <变量名>将输出发送到变量中:

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'],
 ...

9
最简单的方法是在Jupyter笔记本或Python shell中导入os并更新环境变量comspec
import os
os.environ['comspec']='powershell.exe'
os.getenv('comspec')

然后使用以下命令。

!gc log.txt | select -first 10 # head
!gc -TotalCount 10 log.txt     # also head
!gc log.txt | select -last 10  # tail
!gc -Tail 10 log.txt           # also tail (since PSv3), also much faster than above option
!gc log.txt | more             # or less if you have it installed
!gc log.txt | %{ $_ -replace '\d+', '($0)' }         # sed

更多信息请访问https://ss64.com/ps/


我不得不这样做才能使它工作:os.putenv('COMSPEC',r'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe') - Robbie Capps

0

这有点靠不住,但你可以通过以下方式调用预先制作的PowerShell脚本:

os.system('powershell C:\file.ps1')

或者像这样使用单个命令:

os.system("powershell; gci | where {$_.Fullname -contains 'some stuff'}")

编辑:

我刚刚测试了一下,似乎只需要打开一个PowerShell实例:

os.system("powershell; gci; gc C:\windows\system32\drivers\etc\services; [system.net.dns]::gethostbyaddress('203.20.74.6') | more")

但如果您需要在脚本中多次调用此类内容,可能会变得丑陋和缓慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接