如何将集合数据从Python传递到bash?

1 投票
3 回答
2504 浏览
提问于 2025-04-18 16:16

我正在写一个类似于开发运维的bash脚本,这个脚本用于在本地开发环境中运行一个应用,尽量让配置和生产环境相似。为了避免重复一些已经在Python脚本中的代码或数据,我想让我的bash脚本调用Python,获取那些在Python脚本中硬编码的数据。Python中的数据结构是字典,但我其实只关心字典的键,所以我可以只返回一个键的数组。这个Python脚本在生产环境中使用,我希望能直接用它,而不是在我的shell脚本中重复这些数据,这样就不用担心在生产脚本修改后,还要在本地环境的shell脚本中做相应的更改。

有没有办法让我从bash中调用Python函数,并获取这些值的集合?如果不行,我是不是应该让Python函数把结果打印到标准输出,然后让shell脚本解析这个结果?

3 个回答

1

是的,这几乎是将数据从Python传递到Bash的最好也是唯一的方法。

另外,你的函数可以把数据写入文件,然后Bash脚本可以读取这个文件。

1

要把一个Python字典从模块写成一个用NUL(空字符)分隔的键值流(这种格式是最推荐的,因为它能表示bash能处理的所有值):

#!/usr/bin/env python
import sys, yourmodule
saw_errors = 0
for k, v in yourmodule.data.iteritems():
    if '\0' in k or '\0' in v:
        saw_errors = 1 # setting exit status is nice-to-have but not essential
        continue       # ...but skipping invalid content is important; otherwise,
                       #    we'd corrupt the output stream.
    sys.stdout.write('%s\0%s\0' % (k, v))
sys.exit(saw_errors)

...然后把这个流读入一个关联数组中:

# this is bash 4.x's equivalent to a Python dict
declare -A items=()
while IFS= read -r -d '' key && IFS= read -r -d '' value; do
    items[$key]=$value
done < <(python_script) # where 'python_script' behaves as given above

...之后你就可以在你的Python脚本中访问这些项目:

echo "Value for hello is: ${items[hello]}"

...或者遍历这些键:

printf 'Received key: %q\n' "${!items[@]}"

...或者遍历这些值:

printf 'Received value: %q\n' "${items[@]}"

注意:Python中的字节字符串(在Python 2.x中是普通字符串)是Pascal风格的;它们有一个明确的长度存储,所以可以包含任何原始的二进制数据。(Python 3.x中的字符字符串也是Pascal风格的,也可以包含NUL,但前面提到的情况不太适用,因为它们不包含原始的二进制内容——而下面的情况仍然适用)。bash中的字符串是C字符串;它们是以NUL结尾的,所以不能包含原始的NUL字符。

因此,有些在Python中可以表示的数据在bash中是无法表示的。

1

作为一种替代方案,你可以写一个Python脚本,让它输出一个bash数组。

脚本名:bashify.py

#! /usr/bin/python

from sys import argv
from importlib import import_module

def as_bash_array(mapping):
    return " ".join("[{!r}]={!r}".format(*item) for item in mapping.items())

def get_mapping(name):
    module, var = name.rsplit(".", 1)
    return getattr(import_module(module), var)

executable, mapping_name = argv

mapping = get_mapping(mapping_name)

print "(", as_bash_array(mapping), ")"

使用方法:

declare -A my_arr="`./bashify.py my_module.my_dict`"

在格式字符串中使用!r,意味着像NUL这样的不可打印字符会被转义(NUL会变成"\x00")。这也意味着字符串值会被加上引号,这样可以避免一些字符破坏数组声明的语法。

撰写回答