【问题标题】:IPython inline ! bang shell commandIPython 内联!砰壳命令
【发布时间】:2021-04-07 20:46:00
【问题描述】:

我个人觉得在 Python 中使用 ossystem 既冗长又麻烦。因此,我喜欢在 IPython 中使用 bang 运算符 (!) 在文件系统上编写脚本,并以 $variable{variable} 的形式进行变量扩展,如文档中的 here 所述。多年来,我广泛使用了它,最常见的是通过 shell 与 MRI 处理工具交互并管理它们的 MRI 输入和输出。

有没有办法在线使用 IPython ! (bang)?或者是否有一种很好的通用 Pythonic 方式来运行 shell 命令并在列表或字符串中捕获其输出?您可以使用 Python 的 os.listdir 方法和其他 shell 包装器方法,但是您必须知道包装常用 shell 命令的 Python 方法。当然,使用这些的好处是,您的代码也可以在 Windows 和 Linux 上运行。但是,如果您在 Windows 上使用 Python 进行编码,请删除您的 stackoverflow 帐户,但我离题了。 ;)

例如使用cat in-line,!cat /etc/os-release(我通常会在 Jupyter Lab 中运行它)会引发语法错误:

In [1]: os_release_info = {(split := info.split('='))[0]:split[1] for info in !cat /etc/os-release}
  File "<ipython-input-4-b5d2c7182f4f>", line 1
    os_release_info = {(split := info.split('='))[0]:split[1] for info in !cat /etc/os-release}
                                                                          ^
SyntaxError: invalid syntax

这与海象运算符一起允许人们在理解中进行有趣的文件系统输出处理。

否则,您必须通过 bang 操作符在单独的行上分离出 shell 命令:

Python 3.9.1 (default, Feb  3 2021, 07:04:15)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.20.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: os_release_cat = !cat /etc/os-release

In [2]: os_release_info = {(split := info.split('='))[0]:split[1] for info in os_release_cat}

【问题讨论】:

  • 改用pathlib.Path("/etc/os-release").read_text().splitlines()
  • 希望您已经意识到运行和解析像ls 这样的shell 命令是缓慢、脆弱且充满陷阱的,而os.listdir 是快速、安全且健壮的。您是否考虑过寻找一个用 Python 实现常用 shell 命令的库?
  • ipython shell 可以在行首捕获!,在var=!cmd 语法中,ipython.readthedocs.io/en/stable/interactive/…,但在其他任何地方,Python 解释器都会看到!并抱怨。
  • 为什么运行和解析像ls这样的shell命令“缓慢、脆弱、充满陷阱”?至少部分是因为您使用的是 shell 而不是直接与操作系统交互?

标签: python shell jupyter-notebook ipython jupyter-lab


【解决方案1】:

subprocess 如果你愿意,可以很容易地调用 shell。您需要做一些额外的事情才能让它感觉像是! 的直接替代品。但是编写一个可以帮助实现这一点的包装函数很简单。

import subprocess

class StringResult:
    def __init__(self, result):
        self.result = result

    def __iter__(self):
        return iter(str(self).splitlines())

    def __str__(self):
        return self.result.stdout.decode()

    def __bool__(self):
        return self.result.returncode == 0

def run(*args, **kwargs):
    result = subprocess.run(*args, shell=True, capture_output=True, **kwargs)
    return StringResult(result)

glob = '*.py'
# a very silly way to print "py" if any .py files exist
for line in run(f'ls -1 {glob} | cut -d . -f 2 | sort | uniq'):
    print(line)

if run('which python3'):
    print('Python 3 exists on this machine')

这对于一些手动黑客来说很好,但如果你正在编写程序或脚本,那么你最好使用 Python 的本机功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-12
    • 2021-03-27
    • 2015-10-19
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    相关资源
    最近更新 更多