如何将Bash数组读入Python中
如何将Bash数组读入Python中
我无法像这样从bash脚本导出数组到另一个bash脚本:
export myArray[0]="Hello" export myArray[1]="World"
当我像这样写时,没有问题:
export myArray=("Hello" "World")
由于几个原因,我需要将我的数组初始化为多个行。你有什么解决方案吗?
TL;DR: 到目前为止,包括bash-5.1在内,不支持直接导出数组,但有两种方法可以(有效地)导出数组:
- 简单修改调用子脚本的方式
- 使用导出函数存储数组初始化,简单修改子脚本即可
或者等待bash-4.3发布(截至2014年2月处于开发/RC状态,详见Changelog中的ARRAY_EXPORT)。更新:该功能未在4.3中启用。如果在构建时定义了ARRAY_EXPORT
,则构建将失败。作者已经说明了不计划完成该功能。
首先要了解的是,bash环境(更准确地说是命令执行环境)与POSIX环境的概念不同。 POSIX环境是未类型化的name=value
对的集合,并且可以通过各种方式将其从进程传递到其子进程中(实际上是一种受限的IPC形式)。
bash执行环境实际上是POSIX环境的超集,具有类型化变量、只读和可导出标志、数组、函数等。这部分解释了为什么set
(bash内置)的输出与env
或printenv
的输出不同。
当您调用另一个bash shell时,您启动了一个新进程,会丢失一些bash状态。但是,如果您通过点号将脚本源化,则该脚本在同一个环境中运行;或者如果您通过( )
运行子shell,则也会保留环境(因为bash fork,保留其完整状态,而不是使用进程环境重新初始化)。
@lesmana答案中提到的限制是因为POSIX环境仅仅是一个name=value
对的集合,没有额外的含义,所以没有一种约定俗成的方式来编码或格式化类型化变量,请参见下面有关函数的有趣的bash怪癖以及bash-4.3中即将推出的变化(已放弃建议数组功能)。
有几种简单的方法可以使用declare -p
(内建)将部分bash环境输出为一个或多个declare
语句的集合,这些语句可以用来重构“name”的类型和值。这是基本的序列化,但其他一些答案的复杂性要少得多。 declare -p
保留数组索引、稀疏数组和麻烦值的引号。对于数组的简单序列化,您可以仅逐行转储值,并使用read -a myarray
来恢复它(对于连续的0索引数组有效,因为read -a
会自动分配索引)。
这些方法不需要修改您将数组传递到的脚本。
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file bash -c ". .bash_arrays; . otherscript.sh" # source both in the same environment
上述bash -c“…”形式的变体有时会(误)用于crontabs中设置变量。
可供选择的包括:
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file BASH_ENV=.bash_arrays otherscript.sh # non-interactive startup script
或,作为一行代码:
BASH_ENV=<(declare -p array1 array2) otherscript.sh
最后一种使用过程替换将declare命令的输出作为rc脚本传递。 (这种方法仅在bash-4.0或更高版本中有效:早期版本无条件fstat()rc文件并使用返回的大小一次性读取文件; FIFO返回0大小,因此无法按预期工作。)
在非交互式shell(即shell脚本)中,由BASH_ENV变量指向的文件将自动生成源。 您必须确保正确调用bash,可能使用shebang显式调用“bash”,而不是#!/bin/sh,因为在历史/ POSIX模式下,bash不会遵守BASH_ENV。
如果所有数组名称恰好具有共同的前缀,则可以使用declare -p $ {!myprefix *}来扩展它们的列表,而不是枚举它们。
您可能不应尝试使用此方法导出并重新导入整个bash环境,一些特殊的bash变量和数组是只读的,在修改特殊变量时可能会产生其他副作用。
(您还可以通过将数组定义序列化为可导出变量并使用eval来执行一些不同意的操作,但让我们不鼓励使用eval ...
$ array=([1]=a [10]="b c") $ export scalar_array=$(declare -p array) $ bash # start a new shell $ eval $scalar_array $ declare -p array declare -a array='([1]="a" [10]="b c")'
))
如上所述,有一个有趣的怪癖:通过环境导出函数的特殊支持:
function myfoo() { echo foo }
使用export -f或set + a以启用此行为,将在(进程)环境中产生此结果,可通过printenv查看:
myfoo=() { echo foo }
变量是functionname(或functioname()以向后兼容),其值为(){ functionbody}。 当随后的bash进程启动时,它将从每个这样的环境变量中重新创建一个函数。 如果您窥视bash-4.2源文件variables.c,您将看到以(){开头的变量被特别处理。 (但是使用此语法使用declare -f创建函数是被禁止的。)更新:与此功能相关的“shellshock”安全问题,现代系统可以禁用从环境自动导入函数作为缓解措施。但是,如果您继续阅读,则会看到一个#if 0(或#if ARRAY_EXPORT)守卫的代码,该代码检查以([开头并以)结尾的变量,并注释说明“可能尚未导出数组变量”。 好消息是,在当前开发版本的bash-4.3rc2中启用了导出索引数组(而不是关联数组)的功能。 但是,正如上面所述,不太可能启用此功能。我们可以使用这个来创建一个函数,用来恢复需要的任何数组数据:
% function sharearray() { array1=(a b c d) } % export -f sharearray % bash -c 'sharearray; echo ${array1[*]}'
所以,类似于之前的方法,可以使用以下命令调用子脚本:
bash -c "sharearray; . otherscript.sh"
或者,您可以通过在某个适当的位置添加以下内容,有条件地调用子脚本中的sharearray
函数:
declare -F sharearray >/dev/null && sharearray
注意,在sharearray
函数中没有declare -a
。如果这样做,数组会被隐式地限制在函数局部范围内,这不是想要的。bash-4.2 支持declare -g
,可以将在函数中声明的变量变成全局变量,因此可以使用declare -ga
。对于在 bash-4.2 之前的全局关联数组,由于关联数组需要一个declare -A
,所以无法使用此方法。从 v4.2 开始,declare -Ag
将按预期工作。GNU parallel
文档对此方法进行了有用的变化,详见man page中的--env
讨论。
从您所提出的问题的措辞中,也表明您可能在使用export
时遇到了问题。您可以在创建或修改名称后将名称导出。 "exportable" 是变量的标志或属性,为了方便起见,您也可以在一个语句中设置和导出。在 bash-4.2 之前,export
仅接受名称,支持简单(标量)变量或函数名称。
即使您可以(在将来)导出数组,也可能不支持导出选定的索引(切片)(虽然由于数组是稀疏的,没有理由不能允许)。尽管 bash 也支持语法declare -a name[0]
,但下标会被忽略,"name"只是一个普通的索引数组。
数组变量可能(尚)未被导出。
来自Ubuntu 10.04下的bash版本4.1.5的man手册。
以下来自Chet Ramey(截至2011年的当前bash维护者)的声明可能是关于这个“错误”的最正式的文档:
真的没有一种好的方式将数组变量编码到环境中。