{ "cells": [ { "cell_type": "markdown", "source": [ "# ``subprocess`` Examples" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 1, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "python interpreter: /Users/sanhehu/Documents/GitHub/Dev-Exp-Share/venv/bin/python\n", "python version: sys.version_info(major=3, minor=8, micro=9, releaselevel='final', serial=0)\n", "current dir: /Users/sanhehu/Documents/GitHub/Dev-Exp-Share/docs/source/02-SDE/01-Program-Language/02-Python-Root/Awesome-Python-Library-for-Software-Development/subprocess\n" ] } ], "source": [ "import os\n", "import sys\n", "import subprocess\n", "\n", "dir_here = os.getcwd()\n", "print(f\"python interpreter: {sys.executable}\")\n", "print(f\"python version: {sys.version_info}\")\n", "print(f\"current dir: {dir_here}\")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 2, "id": "0d9d9e9c", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "def print_result(res: subprocess.CompletedProcess):\n", " print(f\"type(res) = {type(res)!r}\")\n", " print(f\"res = {res!r}\")\n", " print(f\"res.args = {res.args!r}\")\n", " print(f\"res.returncode = {res.returncode!r}\")\n", " print(f\"res.stdout = {res.stdout!r}\")\n", " print(f\"res.stderr = {res.stderr!r}\")" ] }, { "cell_type": "markdown", "source": [ "# Basic\n", "## Run Shell Command" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 8, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello\n", "type(res) = \n", "res = CompletedProcess(args=['echo', 'hello'], returncode=0)\n", "res.args = ['echo', 'hello']\n", "res.returncode = 0\n", "res.stdout = None\n", "res.stderr = None\n" ] } ], "source": [ "res = subprocess.run([\"echo\", \"hello\"])\n", "print_result(res)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 9, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello alice\n", "hello bob\n", "type(res) = \n", "res = CompletedProcess(args=['python', 'print_something.py'], returncode=0)\n", "res.args = ['python', 'print_something.py']\n", "res.returncode = 0\n", "res.stdout = None\n", "res.stderr = None\n" ] } ], "source": [ "res = subprocess.run([\"python\", \"print_something.py\"])\n", "print_result(res)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Handle Exit Code\n", "\n", "出现错误时, 我们一般有两种处理方式:\n", "\n", "1. 无论命令是成功还是失败, 都继续运行. 不过我们希望能获得每个命令的状态码, 自己决定如何处理这些错误.\n", "2. 命令失败后立刻中断 Python 脚本, 阻止继续运行.\n", "\n", "而 subprocess.run 的行为是这样的:\n", "\n", "1. 如果第一个命令比如 ``subprocess.run([\"cat\", ...])`` 的这个 cat 本身就不存在, 找不到这个命令, 那么该行代码将不会被作为命令被执行, 而是直接丢出 ``FileNotFoundErro`` 从而停止后面的程序.\n", "2. 而如果第一个命令存在, 那么如果是由于命令运行时导致的问题, 而你又没有加入 ``check=True`` 参数, 则该行代码则会被执行然后继续执行后面的代码. 用户自己需要处理命令的状态码和错误." ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 12, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(res) = \n", "res = CompletedProcess(args=['cat', 'not-exists-file.txt'], returncode=1)\n", "res.args = ['cat', 'not-exists-file.txt']\n", "res.returncode = 1\n", "res.stdout = None\n", "res.stderr = None\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "cat: not-exists-file.txt: No such file or directory\n" ] } ], "source": [ "# command exit non-zero code, but subprocess.run just did and return result with error message\n", "res = subprocess.run([\"cat\", \"not-exists-file.txt\"])\n", "print_result(res) # this line still run" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 13, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cat: not-exists-file.txt: No such file or directory\n" ] }, { "ename": "CalledProcessError", "evalue": "Command '['cat', 'not-exists-file.txt']' returned non-zero exit status 1.", "output_type": "error", "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31mCalledProcessError\u001B[0m Traceback (most recent call last)", "Input \u001B[0;32mIn [13]\u001B[0m, in \u001B[0;36m\u001B[0;34m()\u001B[0m\n\u001B[1;32m 1\u001B[0m \u001B[38;5;66;03m# raise exception immediately when command exit non-zero code\u001B[39;00m\n\u001B[0;32m----> 2\u001B[0m res \u001B[38;5;241m=\u001B[39m \u001B[43msubprocess\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mcat\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mnot-exists-file.txt\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcheck\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mTrue\u001B[39;49;00m\u001B[43m)\u001B[49m\n\u001B[1;32m 3\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124myou should never see this line\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", "File \u001B[0;32m~/.pyenv/versions/3.8.11/lib/python3.8/subprocess.py:516\u001B[0m, in \u001B[0;36mrun\u001B[0;34m(input, capture_output, timeout, check, *popenargs, **kwargs)\u001B[0m\n\u001B[1;32m 514\u001B[0m retcode \u001B[38;5;241m=\u001B[39m process\u001B[38;5;241m.\u001B[39mpoll()\n\u001B[1;32m 515\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m check \u001B[38;5;129;01mand\u001B[39;00m retcode:\n\u001B[0;32m--> 516\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m CalledProcessError(retcode, process\u001B[38;5;241m.\u001B[39margs,\n\u001B[1;32m 517\u001B[0m output\u001B[38;5;241m=\u001B[39mstdout, stderr\u001B[38;5;241m=\u001B[39mstderr)\n\u001B[1;32m 518\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m CompletedProcess(process\u001B[38;5;241m.\u001B[39margs, retcode, stdout, stderr)\n", "\u001B[0;31mCalledProcessError\u001B[0m: Command '['cat', 'not-exists-file.txt']' returned non-zero exit status 1." ] } ], "source": [ "# raise exception immediately when command exit non-zero code\n", "res = subprocess.run([\"cat\", \"not-exists-file.txt\"], check=True)\n", "print(\"you should never see this line\") # this line will not run" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 4, "outputs": [ { "ename": "FileNotFoundError", "evalue": "[Errno 2] No such file or directory: 'ansible'", "output_type": "error", "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31mFileNotFoundError\u001B[0m Traceback (most recent call last)", "Input \u001B[0;32mIn [4]\u001B[0m, in \u001B[0;36m\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0m res \u001B[38;5;241m=\u001B[39m \u001B[43msubprocess\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mansible\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 2\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124myou should never see this line\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", "File \u001B[0;32m/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py:493\u001B[0m, in \u001B[0;36mrun\u001B[0;34m(input, capture_output, timeout, check, *popenargs, **kwargs)\u001B[0m\n\u001B[1;32m 490\u001B[0m kwargs[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mstdout\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m=\u001B[39m PIPE\n\u001B[1;32m 491\u001B[0m kwargs[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mstderr\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m=\u001B[39m PIPE\n\u001B[0;32m--> 493\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[43mPopen\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mpopenargs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m \u001B[38;5;28;01mas\u001B[39;00m process:\n\u001B[1;32m 494\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 495\u001B[0m stdout, stderr \u001B[38;5;241m=\u001B[39m process\u001B[38;5;241m.\u001B[39mcommunicate(\u001B[38;5;28minput\u001B[39m, timeout\u001B[38;5;241m=\u001B[39mtimeout)\n", "File \u001B[0;32m/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py:858\u001B[0m, in \u001B[0;36mPopen.__init__\u001B[0;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)\u001B[0m\n\u001B[1;32m 854\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mtext_mode:\n\u001B[1;32m 855\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mstderr \u001B[38;5;241m=\u001B[39m io\u001B[38;5;241m.\u001B[39mTextIOWrapper(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mstderr,\n\u001B[1;32m 856\u001B[0m encoding\u001B[38;5;241m=\u001B[39mencoding, errors\u001B[38;5;241m=\u001B[39merrors)\n\u001B[0;32m--> 858\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_execute_child\u001B[49m\u001B[43m(\u001B[49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mexecutable\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mpreexec_fn\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mclose_fds\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 859\u001B[0m \u001B[43m \u001B[49m\u001B[43mpass_fds\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcwd\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43menv\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 860\u001B[0m \u001B[43m \u001B[49m\u001B[43mstartupinfo\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcreationflags\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mshell\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 861\u001B[0m \u001B[43m \u001B[49m\u001B[43mp2cread\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mp2cwrite\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 862\u001B[0m \u001B[43m \u001B[49m\u001B[43mc2pread\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mc2pwrite\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 863\u001B[0m \u001B[43m \u001B[49m\u001B[43merrread\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43merrwrite\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 864\u001B[0m \u001B[43m \u001B[49m\u001B[43mrestore_signals\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mstart_new_session\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 865\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m:\n\u001B[1;32m 866\u001B[0m \u001B[38;5;66;03m# Cleanup if the child failed starting.\u001B[39;00m\n\u001B[1;32m 867\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m f \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mfilter\u001B[39m(\u001B[38;5;28;01mNone\u001B[39;00m, (\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mstdin, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mstdout, \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mstderr)):\n", "File \u001B[0;32m/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py:1704\u001B[0m, in \u001B[0;36mPopen._execute_child\u001B[0;34m(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)\u001B[0m\n\u001B[1;32m 1702\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m errno_num \u001B[38;5;241m!=\u001B[39m \u001B[38;5;241m0\u001B[39m:\n\u001B[1;32m 1703\u001B[0m err_msg \u001B[38;5;241m=\u001B[39m os\u001B[38;5;241m.\u001B[39mstrerror(errno_num)\n\u001B[0;32m-> 1704\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m child_exception_type(errno_num, err_msg, err_filename)\n\u001B[1;32m 1705\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m child_exception_type(err_msg)\n", "\u001B[0;31mFileNotFoundError\u001B[0m: [Errno 2] No such file or directory: 'ansible'" ] } ], "source": [ "res = subprocess.run([\"ansible\"])\n", "print(\"you should never see this line\") # this line will not run" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "try:\n", " res = subprocess.run([\"ansible\"])\n", "except Exception as e:\n", " print(e)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Capture Console Output\n", "\n", "我们希望将 print 到 console output 的字符串捕获成一个变量, 然后对其进行处理." ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 17, "outputs": [ { "data": { "text/plain": "'hello\\n'" }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# capture console output (standard out)\n", "res = subprocess.run([\"echo\", \"hello\"], capture_output=True)\n", "res.stdout.decode(\"utf-8\")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 18, "outputs": [ { "data": { "text/plain": "'hello alice\\nhello bob\\n'" }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res = subprocess.run([\"python\", \"print_something.py\"], capture_output=True)\n", "res.stdout.decode(\"utf-8\")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Pipe Pattern\n", "\n", "Shell Command 里经常会出现 ``|`` pipe 语法, 把前一个命令的输出作为后一个命令的输入. 在 Python 中我们同样可以实现" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 21, "outputs": [ { "data": { "text/plain": "'2.0'" }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipe = subprocess.Popen([\"cat\", \"data.json\"], stdout=subprocess.PIPE)\n", "res = subprocess.run([\"jq\", \".version\", \"-r\"], stdin=pipe.stdout, capture_output=True)\n", "res.stdout.decode(\"utf-8\").strip()" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "# Advance\n", "\n", "## run vs call vs Popen\n", "\n", "- [subprocess.run](https://docs.python.org/3/library/subprocess.html#subprocess.run): 3.5 中被加入, 是最高级的 API, 也是最推荐使用的 API, 返回的是结构化的 [CompletedProcess](https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess) 对象.\n", "- [subprocess.call](https://docs.python.org/3/library/subprocess.html#subprocess.call): 是 2.7 ~ 3.x+ 的 API, 是 Python2 时代的高级 API, 为了兼容性也一直存在着. 返回的是一个整数 return code.\n", "- [subprocess.Popen](https://docs.python.org/3/library/subprocess.html#subprocess.Popen): 是 2.7 ~ 3.x+ 的 API, 是 Python2 时代的高级 API, 为了兼容性也一直存在着. Popen 主要是创建一个管道, 然后 fork 一个子进程, 返回值在标准 IO 流中, 该管道用于父子进程通信. 父进程要么对子进程读, 要么写.\n", "\n", "## run() parameters\n", "\n", "- stdin:\n", "- stdout:\n", "- stderr:\n", "- input: 用于在父进程 pass 到 Popen.communicate() 方法, 然后变成子进程的 stdin\n", "- capture_output: bool, 是否捕获 console output\n", "- shell: bool / str, 如果为 True, 该命令会在一个 shell 中执行, 而 shell\n", "- cwd: 改变运行命令的目录\n", "- timeout: 只有在 Popen.communicate() 的时候才有用, 限制进程通信的时间上限\n", "- check: 如果 return code 不是 0, 则立刻抛出异常\n", "- encoding:\n", "- errors:\n", "- text:\n", "- env:\n", "- universal_newlines:\n" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "markdown", "source": [ "``shell`` 参数详解\n", "\n", "``shell=True`` 意味着命令是在子进程中执行的, 你无法捕获到子进程的状态, 除非你显式的用 Popen 让子进程和父进程通信. 而且 ``shell=True`` 在当你允许用户输入自定义的 argument 的时候会有注注入风险, 导致潜在的安全问题.\n", "\n", "该用法通常用于一个你的命令是做一些工作, 而你只想运行了就走, 不想管它运行的怎样, 结果如何的情况. 其他情况, 尽量避免用 ``shell=True``.\n", "\n", "另外该参数有时候会导致命令锁死, 例如 ``suprocess.run([\"jq\", \"--version\"], shell=True)``, 具体原因未知." ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 27, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "type(res) = \n", "res = CompletedProcess(args=['python', 'print_something.py'], returncode=0, stdout=b'', stderr=b'')\n", "res.args = ['python', 'print_something.py']\n", "res.returncode = 0\n", "res.stdout = b''\n", "res.stderr = b''\n" ] } ], "source": [ "\n", "res = subprocess.run(\n", " [\n", " \"python\", \"print_something.py\",\n", " ],\n", " shell=True,\n", " capture_output=True,\n", ")\n", "print_result(res)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": 29, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "my_value\n" ] } ], "source": [ "# 你可以在 Python 中设定环境变量, 这个环境变量在 Python 运行时中都会生效\n", "os.environ[\"my_var\"] = \"my_value\"\n", "res = subprocess.run(\n", " [\n", " \"python\", \"print_my_var.py\",\n", " ],\n", ")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## String Interpolation\n", "\n", "在 shell 里你可以这么写, 用 ``$()`` 来做字符串替换: ````. 输出的结果\n", "\n", "```bash\n", "$ echo \"Now is: $(date)\"\n", "Now is: Sun Jan 1 00:08:30 EDT 2022\n", "```\n", "\n", "但在 subprocess.run 里, 你应该直接用 Python 的字符串替换来完全替代 ``$()``. 例子如下:" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": 13, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Now is: Sun Jul 17 01:02:25 EDT 2022\n", "\n" ] } ], "source": [ "_ = subprocess.run(\n", " [\n", " \"echo\",\n", " \"Now is: {}\".format(\n", " subprocess.run([\"date\"], capture_output=True).stdout.decode(\"utf-8\")\n", " )\n", " ]\n", ")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.11" } }, "nbformat": 4, "nbformat_minor": 5 }