如何在IPython会话中复制内容而不包含终端提示符

87

经常情况下,我的工作流程涉及在IPython shell中进行数据清洗/转换。自从IPython 5.0版本推出以来,终端界面得到了很大的升级,这使得工作变得更加便捷。因此,让我们假设我试图清理一些非结构化数据:

In [11]: for i, (num, header, txt) in enumerate(data):
    ...:     header = [e.strip() for e in header.strip().split('\n')]
    ...:     header[4] = header[4].strip(',').split(',')
    ...:     data[i] = (num, header, txt)
    ...:

太棒了,它起作用了!但现在,我真的想将其添加到我的编辑器中的脚本中。如果我从终端复制和粘贴,我就会捕获左侧所有垃圾。我可以在编辑器中更或多或少地清理这个问题,但如果我可以直接从终端复制代码到剪贴板而不用触摸鼠标并且不抓取额外的东西,那就太好了。在IPython中是否有这样的功能?


1
似乎有人已经实现了这个功能,但它似乎并不随 IPython 一起提供。 - user2357112
3
如果你不想自定义IPython设置,“print(In[11])”应该会给你一些可复制的内容,而左侧的垃圾则被省略了。 - user2357112
@user2357112 嗯,虽然这不是最理想的,但对我来说也可以。如果你把它作为答案,并且没有更好的出现,我会接受的。 - juanpa.arrivillaga
这并不是回答这个问题的方法,但我倾向于反过来——在vim中输入代码,然后使用jupyter-vim插件将代码发送到jupyter以执行。这样代码已经在vim中了,无需复制。 - user202729
1
@user202729 这不是关于Jupyter的问题,而是关于IPython REPL的问题。整个重点在于使用REPL进行实验。我通常不使用Jupyter笔记本。 - juanpa.arrivillaga
我的意思是将代码从vim发送到IPython控制台(或jupyter控制台)。尽管插件名称如此,但它适用于两者。 - user202729
6个回答

124

您可以使用%history魔术命令来提取会话中有趣的部分。它们将在终端中显示,而不会显示任何垃圾信息。

示例

In [1]: import numpy as np    
In [2]: a = np.random(10)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-83ce219ad17b> in <module>()
----> 1 a = np.random(10)

TypeError: 'module' object is not callable

In [3]: a = np.random.random(10)
In [4]: for i in a:
   ...:     print(i)
   ...:     
0.688626523886
[...]
0.341394850998

如果我想保存会话的一部分,可以使用:

In [5]: %history 1 3-4

import numpy as np
a = np.random.random(10)
for i in a:
    print(i)
在上面的例子中,我使用%history 1 3-4来组装我想要保留的命令并省略我不需要的命令(第2行,即错误的命令)。现在你有了一个可以很好地复制的会话版本。
写入文件
你也可以直接使用-f FILENAME参数将此内容写入文件。
In [8]: %history 1 3-4 -f /tmp/foo.py

请注意,这将覆盖现有文件。更多细节请参阅文档中的%history魔法.


这是一个有用的函数,尽管它也会在输出中给出我们使用的命令,例如如果我使用%history,下一次它也会给出。我认为这就是history的作用。 - bhansa

20

所以,我终于找到了一个很棒的解决方案,基本上就是我想要的:在IPython中使用Vi模式。在版本5上,这需要:

$ ipython --TerminalInteractiveShell.editing_mode=vi

现在我可以使用便利的类vi视觉模式,并复制粘贴我需要的任何内容!

这导致在我的.bash_profile/.bash_rc文件中添加了以下新别名:

alias vpython='ipython --TerminalInteractiveShell.editing_mode=vi'

2
你可以将 c.TerminalInteractiveShell.editing_mode = 'vi' 添加到 ~/.ipython/profile_default/ipython_config.py,而不是使用别名。更多细节请参见这里 - BiBi
5
很好的回答,但是如何在终端提示符下复制代码? - markroxor
4
在IPython Vim模式下,似乎无法像Vim中那样使用将内容复制到普通剪贴板的命令“+y”。"+y无效。 - WalksB
在vim模式下,复制操作也无法将内容保存到我的剪贴板中。 - jerpint

16

save 魔法命令[文档]可以将想要保存的输入行保存到文件中;-a 选项是 "追加" 模式,这样行就会添加到文件末尾而不是覆盖文件。我经常使用它。

以你的示例为例:

%save -a myfile.py 11
# the '%' is not necessary
save -a myfile.py 11

你可以继续在IPython中编写代码。

当你想要将另一个命令写入同一文件时,只需键入save,然后使用上箭头将上一次使用"save"的命令调出(这样-a选项和文件名已经存在),然后编辑行号即可。

请注意,您可以给save命令传递多个行以及行范围:

save -a myfile.py 15 18 19-25

我喜欢这个解决方案的原因是它在IPython shell中显示代码时不会显示终端提示。 - John Smith Optional

3
在Shell中,您可以先将IPython文件转换为普通的Python文件(.py),然后进行清理:

http://ipython.org/ipython-doc/3/notebook/nbconvert.html (请参阅--到脚本格式)

您也可以在笔记本编辑器中下载文件作为Python文件,在此步骤之后执行清理操作。

我认为你没有理解,这里没有IPython文件(我猜你指的是Jupyter笔记本)。关键是我在终端中使用shell,理想情况下,我不想使用鼠标。%history命令最接近我的需求,所以我接受了,但仍然需要使用鼠标。 - juanpa.arrivillaga
是的,你说得对,Jupyter笔记本文件(ipynb)格式。我以为你可以在终端中运行以下类型的命令:$ ipython nbconvert --to script <filename>.ipynb,以创建一个可用于在终端中清理的文件(抱歉,我刚开始学Python)。我从你的帖子中学到了东西。 - Dutch_Programmer
是的,不幸的是,我没有使用jupyter笔记本,我正在使用REPL。有时我会用,但是,由于你指出的工具和代码已经处于清理过的文本格式,所以问题已经不存在了。 - juanpa.arrivillaga
有趣的是你之前把REPL称作shell或终端。在REPL窗口中编写代码时,我的大多数编辑器都允许我通过按住shift-alt键并使用箭头来复制矩形。在你的例子中,将光标放在for关键字右侧。这在Visual Studio、Atom、VIM等编辑器中都可以实现。我经常从REPL复制到编辑器中,没有任何问题。如果这对你很重要,但在你的IDE/文本编辑器中无法实现,请考虑寻找替代的IDE/文本编辑器。 - Dutch_Programmer
但问题不在于我的IDE,我不使用IDE,我使用终端仿真器和文本编辑器,而编辑器并不重要(我使用vim和有时Atom),但问题是从终端中的REPL会话复制。 - juanpa.arrivillaga

0

我认为终端应用程序并没有真正获得复制/粘贴缓冲区的访问权限。你需要使用鼠标。如何操作取决于您使用的终端类型。大多数现代终端都有某种“矩形选择”或“块选择”模式。

在Windows上,矩形选择是cmd.exe和Powershell的默认设置。如果您使用Cygwin的mintty,请按住Alt键,然后使用鼠标选择区域。PuTTY也是同样的方法。

在Linux上(我手头没有 - 请谨慎对待),xterm不支持它,Gnome终端使用Ctrl作为修饰符,KDE的Konsole使用Ctrl+Alt

对于OS X终端,互联网告诉我您需要同时点击

其他终端(和GNU Screen)可能具有此功能,只是需要找出如何激活它。


终端应用程序可以获得与任何其他应用程序相同的访问剪贴板的权限。 - user2357112

0
这有点不规范,但你可以使用以下按键:
ggIprint("""<Esc>GA""")

这将使整个单元格的内容成为多行字符串,然后打印出来。只要行数不是太多,这通常就足够方便了。

也可能将其转换为键绑定/宏,但我还没有调查过。


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