问题复现

弄了个函数,去重启docker容器,执行docker restart xxx,发现老不行,提示:No such file or directory: 'docker restart xxx'

后来发现加上shell=True就可以了。。。

def execute_docker_restart(container_name):
    """
    重启 container_name 容器
    """
    try:
        # 构建命令

        command = f'docker restart {container_name}'
        # command = f'/usr/bin/docker restart {container_name}' # 也不行

        # 执行命令并获取输出
        # 运行提示 No such file or directory: 'docker restart kyai_kykj'
        # result = subprocess.run(
        #     command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        result = subprocess.run(
            command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        # 打印脚本的输出
        print(f"[{command}] 标准输出:", result.stdout)

        # 打印脚本的错误输出
        print(f"[{command}] 标准错误输出:", result.stderr)

        # 执行成功,返回True,否则返回False
        return result.returncode == 0
    # except subprocess.CalledProcessError as e:
    except Exception as e:
        # 执行失败,返回False
        print(f'[{command}]执行失败:[{e}]')
        return False

测试(Python 3.8.10)

测试不加shell=True参数,也不用[bin, arg, arg, …]传列表的方式分别执行ls -l和ll -a和docker -v命令,发现都不能执行成功

import subprocess


def execute_command(command):
    """
    执行命令
    """
    print(f'\n+++ execute command: [{command}]')
    try:
        # 构建命令

        result = subprocess.run(
            command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        # 打印脚本的输出
        print(f"[{command}] 标准输出:", result.stdout)

        # 打印脚本的错误输出
        print(f"[{command}] 标准错误输出:", result.stderr)

        # 执行成功,返回True,否则返回False
        return result.returncode == 0
    # except subprocess.CalledProcessError as e:
    except Exception as e:
        # 执行失败,返回False
        print(f'[{command}]执行失败:[{e}]')
        return False


if __name__ == "__main__":
    execute_command('ls -l')
    execute_command('ll -a')
    execute_command('docker -v')

运行结果:

在这里插入图片描述

测试加shell=True参数,不用[bin, arg, arg, …]传列表的方式分别执行ls -l和ll -a和docker -v命令,发现只有ll提示找不到命令,其余都能执行成功

import subprocess


def execute_command(command):
    """
    执行命令
    """
    print(f'\n+++ execute command: [{command}]')
    try:
        # 构建命令

        result = subprocess.run(
            command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        # 打印脚本的输出
        print(f"[{command}] 标准输出:", result.stdout)

        # 打印脚本的错误输出
        print(f"[{command}] 标准错误输出:", result.stderr)

        # 执行成功,返回True,否则返回False
        return result.returncode == 0
    # except subprocess.CalledProcessError as e:
    except Exception as e:
        # 执行失败,返回False
        print(f'[{command}]执行失败:[{e}]')
        return False


if __name__ == "__main__":
    execute_command('ls -l')
    execute_command('ll -a')
    execute_command('docker -v')

在这里插入图片描述

测试不加shell=True参数,用[bin, arg, arg, …]传列表的方式分别执行ls -l和ll -a和docker -v命令,发现只有ll提示找不到命令,其余都能执行成功

import subprocess


def execute_command(command):
    """
    执行命令
    """
    print(f'\n+++ execute command: [{command}]')
    try:
        # 构建命令

        result = subprocess.run(
            command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        # 打印脚本的输出
        print(f"[{command}] 标准输出:", result.stdout)

        # 打印脚本的错误输出
        print(f"[{command}] 标准错误输出:", result.stderr)

        # 执行成功,返回True,否则返回False
        return result.returncode == 0
    # except subprocess.CalledProcessError as e:
    except Exception as e:
        # 执行失败,返回False
        print(f'[{command}]执行失败:[{e}]')
        return False


if __name__ == "__main__":
    execute_command(['ls', '-l'])
    execute_command(['ll', '-a'])
    execute_command(['docker', '-v'])

在这里插入图片描述

测试加shell=True参数,并且用[bin, arg, arg, …]传列表的方式分别执行ls -l和ll -a和docker -v命令,发现三者输出都不正常(说明shell=True和用[bin, arg, arg, …]传列表的方式,只能二者取其一)

import subprocess


def execute_command(command):
    """
    执行命令
    """
    print(f'\n+++ execute command: [{command}]')
    try:
        # 构建命令

        result = subprocess.run(
            command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

        # 打印脚本的输出
        print(f"[{command}] 标准输出:", result.stdout)

        # 打印脚本的错误输出
        print(f"[{command}] 标准错误输出:", result.stderr)

        # 执行成功,返回True,否则返回False
        return result.returncode == 0
    # except subprocess.CalledProcessError as e:
    except Exception as e:
        # 执行失败,返回False
        print(f'[{command}]执行失败:[{e}]')
        return False


if __name__ == "__main__":
    execute_command(['ls', '-l'])
    execute_command(['ll', '-a'])
    execute_command(['docker', '-v'])

在这里插入图片描述

原因

如果不加shell=True参数,也不用[bin, arg, arg, ...]传列表的方式,函数就会直接把整个串当成一个命令(注意是命令,不是参数),比如根据上面示例,会把docker -v当成一个命令(可执行文件),去系统目录(比如/usr/bin)找这个命令的文件,发现找不到,就报错了。。。

如果加上shell=True参数,或者用[bin, arg, arg, ...]传列表的方式,即传个['docker', '-v'],就会把docker当成命令,去系统目录找这个可执行文件,而把-v当作命令的参数,去执行。

解决办法

不能既不加shell=True参数,也不用[bin, arg, arg, ...]传列表的方式,两个必须用一个,且只能用一个,两个一起加会冲突。

参考文章

参考文章:python3 subprocess.Popen 报错 No such file or directory

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐