如何使用bash脚本将.txt文件中存在的所有键值对传输到数组

时间:2017-02-18 14:38:55

标签: regex bash shell grep jq

我有一个变量“var”,它有如下所示的json文本

 var='{ "user": "jack","password": "kilby","install":"False", "deploy":"False", "build":"123","ip":"0.0.0.0" }'

Iam将其写入.txt文件

touch properties.txt

destdir=./properties.txt

if [ -f "$destdir" ]
then 
    echo "$var" > "$destdir"
fi

然后我必须将所有键值对存储在一个数组中,以便我进一步处理,如下所示

arr=( $(grep -o \"[^\"]*. properties.txt) )

因此双引号内的所有文本都会移到“arr”

但问题是如果键值对如下所示,即如果值为空,那么“arr”将获得“作为一个条目,这不是令人难以理解的。”

{"k1":"","k2":"","k3":""}

在这种情况下,我需要一个空字符串作为“arr”的条目。

我不太了解bash命令。所以感谢任何帮助。 如果使用“jq”库可以实现结果,那么也欢迎这些建议。 谢谢

6 个答案:

答案 0 :(得分:1)

考虑:

$ jq -c 'keys[] as $k | ($k, .[$k])' <<< "$var"
"build"
"123"
"deploy"
"False"
"install"
"False"
"ip"
"0.0.0.0"
"password"
"kilby"
"user"
"jack"

然后,您可以使用bash命令readarray或成语:

while read -r line
do ...
done

有关详细信息,请参阅jq FAQ

买者

你不能说出你的最终目标是什么,但我怀疑你最好更全面地使用jq来实现它。

答案 1 :(得分:1)

jq是从shell解析JSON的工具。你可以用其他语言解析JSON(python,php,ruby,GO等),但是如果你走这条路,你也可以用那种语言编写你的整个程序。

所以..来自bash,你使用jq

$ jq 'keys[] as $k | "\($k)=\(.[$k])"' <<<"$var" 
"build=123"
"deploy=False"
"install=False"
"ip=0.0.0.0"
"password=kilby"
"user=jack"

如果你想在bash 4+中的关联数组中使用这些对,你可以这样做:

$ declare -A a="( $(jq -r 'keys[] as $k | "[\($k)]=\"\(.[$k])\""' <<<"$var" ) )"
$ declare -p a
declare -A a=([build]="123" [install]="False" [ip]="0.0.0.0" [user]="jack" [deploy]="False" [password]="kilby" )

只要变量索引或值中没有换行符,这样就可以正常工作。为了使它处理更复杂的数据,你需要一个更复杂的处理程序。如果存在换行风险,请在您的问题中提及。

答案 2 :(得分:0)

这个答案的解决方案将键值对读入常规数组的连续元素(元素0接收第一个键,元素{ {1}}关联值,...),如问题中所示;有关创建Bash v4 + 关联数组的解决方案,请参阅ghoti's helpful answer

<强> TL;博士

仅使用标准实用程序:

1

Bash v3.x等价物:

readarray -t arr < <(grep -o '"[^"]*.' properties.txt | tr -d \")

使用arr=() while IFS= read -r val; do arr+=( "$val" ) done < <(grep -o '"[^"]*.' properties.txt | tr -d \")

jq

readarray -t arr < <(jq -r 'keys_unsorted[] as $k | $k, .[$k]' properties.txt) 命令是peak's helpful answer的变体:jq确保按输入顺序枚举键,keys_unsorted确保输出结果作为原始值而不是JSON编码,在这种情况下意味着双引号被剥离。

Bash v3.x等价物:

-r

首先要做的事情是:如果可行,始终值得使用适当的JSON解析器,例如jq

您的方法存在的问题:

  • arr=() while IFS= read -r val; do arr+=( "$val" ) done < <(jq -r 'keys_unsorted[] as $k | "\($k)\n\(.[$k])"' properties.txt) 在输出中包含封闭的grep -o \"[^\"]*.,然后将数组赋值"解析为值的一部分< / EM>

  • 通过管道到arr=( ... )来删除它们很容易解决这个问题,但tr -d \"语法然后忽略空字符串,所以你会失去空在过程中输入值。

  • 那就是说,你应该一般避免 arr=( $(...) ),因为它 (a)总是将命令替换arr=( $(...) )的输出拆分为空格(分词),而不考虑嵌入的引号字符。(b)默认情况下将得到的字对象扩展(通配符)。

避免这两个问题的安全替代方案 - 至少使用面向行的输入 - 是使用{BES v4.0以来可用的$(...); readarray从读取的行中删除尾随的-t

在Bash v3.x中,必须使用\n循环来读取行,因为的构造几乎等同于while - readarray -t arr - 也忽略空条目

答案 3 :(得分:-1)

首先,我将创建一个返回数组内容的函数。

function myvar {
   cat << "END"
{ "user": "jack","password": "kilby","install":"False", "deploy":"False", "build":"123","ip":"0.0.0.0" }
END
}

现在我们可以通过在&#39;,&#39;中的行中剪切字符串来解析var,并找到字段。

myvar | tr ',' '\n' | sed 's/.*"\(.*\)".*\(".*"\).*/\1=\2/'

现在我们有了这些,我们可以通过采购来评估结果。在采购之前,我们将使用勾号将其作为文件:<( echo "My output looks like a file")
OP要求一个阵列,但首先我将展示如何制作环境变量。

source <(myvar | tr ',' '\n' | sed 's/.*"\(.*\)".*\(".*"\).*/\1=\2/')
echo "Environment variable build=${build}"

现在我们想在一个关联数组中填充它(注意:仅限bash版本4。)

declare -A arr
source <(myvar | tr ',' '\n' | sed 's/.*"\(.*\)".*\(".*"\).*/arr[\1]=\2/')
# Show that it worked
for key in ${!arr[@]}; do
     echo "Array[${key}]=${arr[${key}]}"
done

答案 4 :(得分:-1)

如果不想判断您选择解析该类数据的方法是对还是错,即使我不同意您选择的解析json数据的方法,我也会尝试回答您的问题。

在代码的这一点上,您将创建一个名为arr的数组:

arr=( $(grep -o \"[^\"]*. properties.txt) )

如果你要求bash告诉你这个数组中存储了什么,你会收到回复:

$ declare -p arr
declare -a arr=([0]="\"user\"" [1]="\"jack\"" [2]="\"password\"" [3]="\"kilby\"" \
[4]="\"install\"" [5]="\"False\"" [6]="\"deploy\"" [7]="\"\"" \
[8]="\"build\"" [9]="\"123\"" [10]="\"ip\"" [11]="\"0.0.0.0\"")

请注意我已将var字段"deploy":"false"修改为"deploy":""以测试我的答案 - 这是上面的[7]。

我发现处理数组中缺少字段的最简单方法是使用这样的bash替换技术重新定义相同的arr数组:

$ arr=( ${arr[@]//\"\"/\"empty\"/} )
$ declare -p arr
declare -a arr=([0]="\"user\"" [1]="\"jack\"" [2]="\"password\"" [3]="\"kilby\"" \ 
[4]="\"install\"" [5]="\"False\"" [6]="\"deploy\"" [7]="\"empty\"/" \
[8]="\"build\"" [9]="\"123\"" [10]="\"ip\"" [11]="\"0.0.0.0\"")

请注意,arr [7]已从之前的"empty"

更改为""

实际上,您的代码会在数组中为空字段存储字符串\"\",这些字段将由echo打印,如简单""

在我替换之后,声明-p建议arr[7]=\"empty\",它将由echo打印为简单"empty"。当然,您可以通过保持双引号的转义将此文本更改为您喜欢的任何内容

第二个var2={"k1":"","k2":"","k3":""}

的结果
$ var2='{"k1":"","k2":"","k3":""}'
$ echo "$var2" >properties.txt
$ cat properties.txt
{"k1":"","k2":"","k3":""}
$ arr=( $(grep -o \"[^\"]*. properties.txt) )
$ declare -p arr
declare -a arr=([0]="\"k1\"" [1]="\"\"" [2]="\"k2\"" [3]="\"\"" [4]="\"k3\"" [5]="\"\"")
$ arr=( ${arr[@]//\"\"/\"empty\"/} )
$ declare -p arr
declare -a arr=([0]="\"k1\"" [1]="\"empty\"/" [2]="\"k2\"" [3]="\"empty\"/" [4]="\"k3\"" [5]="\"empty\"/")
$ echo "${arr[@]}"
"k1" "empty"/ "k2" "empty"/ "k3" "empty"/

注意:
此解决方案仅建议您的代码的下一步。我还没有测试过你的数据操作技术(比如你的grep等)。

答案 5 :(得分:-1)

为快速反应的人提供了很多帮助。我通过以下方法解决了我的问题

makeKeyValueArray (){
# remove colon and commas
IFS=':' read -ra ADDR <<< "$var"
IFS=',' read -ra ADDR2 <<< "${ADDR[@]}"

echo ${ADDR2[@]} > "$destdir"

#remove curly braces
sed 's/[{}]//g' $destdir > temp_ip_prop.txt
mv temp_ip_prop.txt $destdir



#store each item in a array "arr"
    cn=0;
        for i in `cat $destdir`
        do
        arr[$cn]=$i;
        ((cn=cn+1));
        done

}

然后在循环中我比较了数组是否包含&#34;&#34;。如果是这样,我只是将该字段作为空白区域。

我正在做的错误是在

  

arr =($(grep -o \&#34; [^ \&#34;] * .properties.txt))

这是存储每个\&#34;到阵列。