在shell脚本中读取python变量?

时间:2016-01-12 19:12:16

标签: python bash shell

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

week_date = "01/03/16-01/09/16"
cust_id = "12345"

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

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

shell文件:

#!/bin/sh

dt="01/03/16-01/09/16" 
cust_id="12345"

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

3 个答案:

答案 0 :(得分:8)

考虑类似于以下内容:

#!/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" "${@%%:*}")
}

然后您可以将其用作:

read_python_vars config.py week_date:dt cust_id:id
echo "Customer id is $id; date range is $dt"

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

read_python_vars config.py week_date cust_id
echo "Customer id is $cust_id; date range is $week_date"

优点:

  • 与一个天真的基于正则表达式的解决方案不同(它会解决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)
    

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

现在,让我们来谈谈警告:​​

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

现在,我们来谈谈实施细节。

  • ${@%%:*}$@的扩展,用于修剪每个参数中的第一个:之后的所有内容,从而仅将Python变量名称传递给解释器。同样,${varname#*:}是一个扩展,可以修改传递给:的变量名称中的第一个read。见the bash-hackers page on parameter expansion
  • 使用<(python ...)是进程替换语法:<(...)表达式求值为一个文件名,当读取该文件名时,将提供该命令的输出。使用< <(...)重定向来自该文件的输出,从而使用该命令(第一个<是重定向,而第二个是启动进程替换的<(令牌的一部分)。使用此表单将输出转换为while read循环可以避免BashFAQ #24 ("I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?")中提到的错误。
  • IFS= read -r -d ''构造包含一系列组件,每个组件都使read的行为更符合原始内容:

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

    有关从bash中的字符串中读取面向记录的数据的过程的更多信息,请参阅BashFAQ #001 ("How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?")

答案 1 :(得分:2)

其他答案提供了一种方法来完全按照你的要求行事,但我认为这个想法有点疯狂。有一种更简单的方法来满足两个脚本 - 将这些变量移动到配置文件中。您甚至可以保留简单的分配格式。

创建配置本身:( ini-style)

jlayeredpane.add(jbutton, JLayeredPane.DEFAULT_LAYER);

在python中:

dt="01/03/16-01/09/16"
cust_id="12345"

在bash中:

config_vars = {}
with open('the/file/path', 'r') as f:
    for line in f:
        if '=' in line:
            k,v = line.split('=', 1)
            config_vars[k] = v
week_date = config_vars['dt']
cust_id = config_vars['cust_id']

你不再需要做疯狂的源解析了。或者你可以只使用json作为配置文件,然后在python中使用source "the/file/path" 模块,在shell中使用json进行解析。

答案 2 :(得分:0)

我会做这样的事情。你可能想稍微修改它以进行微小的更改以包含/排除引号,因为我没有为你的场景测试过它:

#!/bin/sh
exec <$python_filename
while read line
do
        match=`echo $line|grep "week_date ="`
        if [ $? -eq 0 ]; then
                dt=`echo $line|cut -d '"' -f 2`
        fi

        match=`echo $line|grep "cust_id ="`
        if [ $? -eq 0 ]; then
                cust_id=`echo $line|cut -d '"' -f 2`
        fi
done