在shell脚本中读取python变量?

jxn*_*jxn 5 python bash shell

我的python文件有以下两个变量:

week_date = "01/03/16-01/09/16"
cust_id = "12345"
Run Code Online (Sandbox Code Playgroud)

我如何将其读入一个接受这两个变量的shell脚本?

我当前的shell脚本需要手动编辑"dt"和"id".我想将python变量读入shell脚本,这样我就可以编辑我的python参数文件而不是那么多文件.

shell文件:

#!/bin/sh

dt="01/03/16-01/09/16" 
cust_id="12345"
Run Code Online (Sandbox Code Playgroud)

在一个新的python文件中,我可以导入参数python文件.

Cha*_*ffy 9

考虑类似于以下内容:

#!/bin/bash
#      ^^^^ NOT /bin/sh, which doesn't have process substitution available.

python_script='
import sys
d = {}                                    # create a context for variables
exec(open(sys.argv[1], "r").read()) in d  # execute the Python code in that context
for k in sys.argv[2:]:
  print "%s\0" % str(d[k]).split("\0")[0] # ...and extract your strings NUL-delimited
'

read_python_vars() {
  local python_file=$1; shift
  local varname
  for varname; do
    IFS= read -r -d '' "${varname#*:}"
  done < <(python -c "$python_script" "$python_file" "${@%%:*}")
}
Run Code Online (Sandbox Code Playgroud)

然后您可以将其用作:

read_python_vars config.py week_date:dt cust_id:id
echo "Customer id is $id; date range is $dt"
Run Code Online (Sandbox Code Playgroud)

...或者,如果您不想在阅读时重命名变量,只需:

read_python_vars config.py week_date cust_id
echo "Customer id is $cust_id; date range is $week_date"
Run Code Online (Sandbox Code Playgroud)

好处:

  • 不像一个天真的基于正则表达式的解决方案(它会解决Python解析的一些细节问题 - 尝试教授sed处理原始和常规字符串,以及单引号和三引号,而不是将它变成毛球!)或类似的方法使用来自Python子进程的换行符分隔的输出,这将正确处理任何对象,该对象为str()您的shell脚本可以使用没有NUL字符的表示.
  • 通过Python解释器运行内容也意味着您可以以编程方式确定值 - 例如,您可能有一些Python代码要求您的版本控制系统提供相关内容的最后更改日期.

    想想这样的场景:

    start_date = '01/03/16'
    end_date = '01/09/16'
    week_date = '%s-%s' % (start_date, end_date)
    
    Run Code Online (Sandbox Code Playgroud)

    ...使用Python解释器来解析Python意味着您不会限制人们将来如何更新/修改Python配置文件.

现在,让我们来谈谈:

  • 如果您的Python代码有副作用,那么这些副作用显然会生效(就像您import在Python中选择文件作为模块一样).不要使用它从您不信任其内容的文件中提取配置.
  • Python字符串是Pascal风格的:它们可以包含文字NUL.shell语言中的字符串是C风格的:它们由第一个NUL字符终止.因此,Python中可能存在一些变量,而不能在没有非文字转义的情况下在shell中表示.为了防止其str()表示包含NUL 的对象溢出到其他赋值中,此代码在其第一个NUL处终止字符串.

现在,我们来谈谈实现细节.

  • ${@%%:*}是一个扩展,$@它修剪:了每个参数中的第一个内容之后的所有内容,因此只将Python变量名称传递给解释器.类似地,${varname#*:}是一个扩展,它修改一切,包括:传递给变量名的第一个read.请参阅参数扩展的bash-hackers页面.
  • 使用<(python ...)是进程替换语法:<(...)表达式求值为一个文件名,当读取该文件名时,将提供该命令的输出.使用< <(...)该文件的重定向输出,从而使用该命令(第一个<是重定向,而第二个是<(开始进程替换的令牌的一部分).使用这种形式将输出输出到while read循环中避免了BashFAQ#24中提到的错误("我在一个循环中设置变量在管道中.为什么它们在循环终止后消失?或者,为什么我不能管道数据读取?").
  • IFS= read -r -d ''构造具有一系列组件,每个组件使得行为read更加真实于原始内容:

    • 清除IFS命令的持续时间可防止从变量内容的末尾修剪空白.
    • 使用-r防止文字反斜杠被read自己消耗而不是在输出中表示.
    • 使用-d ''将空字符串的第一个字符设置为''记录分隔符.由于C字符串是NUL终止的,而shell使用C字符串,因此该字符是NUL.这可确保变量的内容可以包含任何非NUL值,包括文字换行符.

    有关从字符串读取面向记录的数据的过程的更多信息,请参阅BashFAQ#001("如何逐行(和/或逐字段)读取文件(数据流,变量)?")在bash中.