python os 和命令行引号的不同解释

Different interpretation of quotes from python os and command line

我是 运行 一个 python3 脚本,它在 Debian 9 上执行以下片段:

    os.environ["PA_DIR"] = "/home/myname/some_folder"
    command_template = ("sudo java -Dconfig.file=$PA_DIR/google.conf "
    "-jar ~/big/cromwell-42.jar run $PA_DIR/WholeGenomeGermlineSingleSample.wdl "
    "-i {} -o $PA_DIR/au_options.json > FDP{}.log 2>&1")
    command = command_template.format("test.json, "1")
    os.system("screen -dm -S S{} bash -c '{}'".format("1", command))

PA_DIR 的使用按预期工作。当我在命令行上尝试时:

PA_DIR="/home/myname/some_folder"    
screen -dm -S S1 bash -c 'sudo java -Dconfig.file=$PA_DIR/google.conf -jar ~/big/cromwell-42.jar run $PA_DIR/WholeGenomeGermlineSingleSample.wdl -i test.json -o $PA_DIR/au_options.json > FDP1.log 2>&1'

由于单引号,它没有进行变量替换,我不得不用双引号替换它们(它抱怨找不到文件 /google.conf)。 python 运行时有什么不同? 谢谢!

Python os.system() 调用 C 库的底层 system 函数,在 POSIX 系统上相当于做类似

的事情
sh -c "your_command and all its arguments"

所以命令和所有参数已经被 double-quotes 包围,它进行环境变量替换。字符串中的任何单引号与变量替换无关。


您可以轻松测试它。在 shell 中做类似

的事情
$ foo="bar"
$ echo "foo is '$foo'"    # Will print foo is 'bar'
$ echo 'foo is "$foo"'    # Will print foo is "$foo"

等待您对 daltonfury42 的回答,我敢打赌问题是,当在命令行中 运行ning 时,您没有导出 PA_DIR 环境变量,因此它不存在于第二个 bash 口译员。由于 Mihir 的回答,它的行为有所不同。

如果你运行

PA_DIR=foo

你只声明了一个 bash 变量,但它不是环境变量。然后

bash -c "echo $PA_DIR"

这将输出 foo,因为您当前的解释器插入 $PA_DIR,然后使用命令 echo foo 引发第二个 bash 进程。但是

bash -c 'echo $PA_DIR'

这会阻止您的 bash 解释器对其进行插值,因此它会使用命令 echo $PA_DIR 引发第二个 bash 进程。但是在第二个过程中,变量 PA_DIR 不存在。

如果你开始你的旅程运行宁

export PA_DIR=foo

这将成为子进程可以访问的环境变量,因此

bash -c 'echo $PA_DIR'

将输出 foo,因为嵌套的 bash 解释器可以访问变量,即使父 bash 解释器没有插入它。

任何种类的子进程也是如此。尝试 运行宁

PA_DIR=foo
python3 -c 'import os; print(os.environ.get("PA_DIR"))'
python3 -c "import os; print(os.environ.get('PA_DIR'))"
export PA_DIR=foo
python3 -c 'import os; print(os.environ.get("PA_DIR"))'
python3 -c "import os; print(os.environ.get('PA_DIR'))"

在你的 shell 中。此处不涉及引号!

当您在 Python 脚本中使用 os.environ 字典时,Python 会为您导出变量。这就是为什么你会看到由

插入的变量

os.system("bash -c 'echo $PA_DIR'")

os.system('bash -c "echo $PA_DIR"')

但请注意,在每种情况下,插入变量的是父进程或子进程 shell。

你必须在这里了解你的进程树:

/bin/bash  # but it could be a zsh, fish, sh, ...
|- /usr/bin/python3  # presumably
   |- /bin/sh  # because os.system uses that
      |- /bin/bash

如果你想让一个环境变量存在于最嵌套的进程中,你必须将它导出到上层树的任何位置。或者在那个过程中。