Linux 使用bash以类似字典的格式进行迭代

Linux 使用bash以类似字典的格式进行迭代,linux,bash,shell,awk,sed,Linux,Bash,Shell,Awk,Sed,我在输入文件中有一个类似python的字典: $ cat test.txt db={1:['a','b','c','d'], 2:['aa','bb','cc','dd']} 字典中的每个列表只有4个索引,不少于或多于4个。 我需要这样的结果: one1="a" two1="b" three1="c" four1="d" one2="aa" two2="bb" three2

我在输入文件中有一个类似python的字典:

$ cat test.txt
db={1:['a','b','c','d'], 2:['aa','bb','cc','dd']}
字典中的每个列表只有4个索引,不少于或多于4个。 我需要这样的结果:

one1="a"
two1="b"
three1="c"
four1="d"

one2="aa"
two2="bb"
three2="cc"
four2="dd"
我知道如果我们在这里使用python,这很简单,但是我应该用bash脚本来完成这项工作。
可能吗?如何使用bash脚本完成这项工作?

您只需去除所有不必要的字符并循环它们即可获得结果

#!/bin/bash
db="{1:['a','b','c','d'], 2:['aa','bb','cc','dd']}"
count=1
for items in `echo $db|sed 's/{//;s/}//'`
do
        echo one${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f1`
        echo two${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f2`
        echo three${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f3`
        echo four${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f4`
        echo ''
        count=`expr $count + 1`
done
输出

one1 = 'a'
two1 = 'b'
three1 = 'c'
four1 = 'd'

one2 = 'aa'
two2 = 'bb'
three2 = 'cc'
four2 = 'dd'

你只需要去掉所有不必要的字符,并通过它们循环得到你的结果

#!/bin/bash
db="{1:['a','b','c','d'], 2:['aa','bb','cc','dd']}"
count=1
for items in `echo $db|sed 's/{//;s/}//'`
do
        echo one${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f1`
        echo two${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f2`
        echo three${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f3`
        echo four${count} = `echo $items|sed 's/^.*\[//;s/\].*$//'|cut -d ',' -f4`
        echo ''
        count=`expr $count + 1`
done
输出

one1 = 'a'
two1 = 'b'
three1 = 'c'
four1 = 'd'

one2 = 'aa'
two2 = 'bb'
three2 = 'cc'
four2 = 'dd'

这可以通过在GNU sed 4.8中测试的单个sed命令来完成。假设整个表达式位于一行中,并且一对匹配的单引号之间没有嵌入单引号:

echo "db={1:['a','b','c','d'], 2:['aa','bb','cc','dd']}" |
sed -E "s/^[^{]*\{//; s/\}[^}]*$//; s/([^:]+):\['([^']*)','([^']*)','([^']*)','([^']*)'\](, *)?/one\1='\2'\ntwo\1='\3'\nthree\1='\4'\nfour\1='\5'\n\n/g"
输出

one1='a'
two1='b'
three1='c'
four1='d'

one2='aa'
two2='bb'
three2='cc'
four2='dd'
说明:

使用扩展正则表达式,这样我们就不会引用,+个字符

s/^[^{]*\{//;
删除行首的字符,直到并包括{字符

s/\}[^}]*$//;
删除行尾的}字符和尾随字符(如果有)

s/([^:]+):\['([^']*)','([^']*)','([^']*)','([^']*)'\](, *)?/one\1='\2'\ntwo\1='\3'\nthree\1='\4'\nfour\1='\5'\n\n/g
  -------    -------   -------   -------   -------   -----  -----------------------------------------------------
     1          2         3         4         5        6                      R
1:捕获文本,直到: 2:捕获第一对单引号之间的文本 3:捕获第二对单引号之间的文本 4:捕获第三对单引号之间的文本 5:捕获第四对单引号之间的文本 6:捕获,以及任意数量的尾随空格字符。替换文本中未使用此子表达式?意味着这是可选的。 R:替换文本\1、\2、\3、\4和\5将替换为相应的捕获文本。
s命令末尾的g标志确保替换应用于所有匹配项。

这可以通过在GNU sed 4.8中测试的单个sed命令来完成。假设整个表达式位于一行中,并且一对匹配的单引号之间没有嵌入单引号:

echo "db={1:['a','b','c','d'], 2:['aa','bb','cc','dd']}" |
sed -E "s/^[^{]*\{//; s/\}[^}]*$//; s/([^:]+):\['([^']*)','([^']*)','([^']*)','([^']*)'\](, *)?/one\1='\2'\ntwo\1='\3'\nthree\1='\4'\nfour\1='\5'\n\n/g"
输出

one1='a'
two1='b'
three1='c'
four1='d'

one2='aa'
two2='bb'
three2='cc'
four2='dd'
说明:

使用扩展正则表达式,这样我们就不会引用,+个字符

s/^[^{]*\{//;
删除行首的字符,直到并包括{字符

s/\}[^}]*$//;
删除行尾的}字符和尾随字符(如果有)

s/([^:]+):\['([^']*)','([^']*)','([^']*)','([^']*)'\](, *)?/one\1='\2'\ntwo\1='\3'\nthree\1='\4'\nfour\1='\5'\n\n/g
  -------    -------   -------   -------   -------   -----  -----------------------------------------------------
     1          2         3         4         5        6                      R
1:捕获文本,直到: 2:捕获第一对单引号之间的文本 3:捕获第二对单引号之间的文本 4:捕获第三对单引号之间的文本 5:捕获第四对单引号之间的文本 6:捕获,以及任意数量的尾随空格字符。替换文本中未使用此子表达式?意味着这是可选的。 R:替换文本\1、\2、\3、\4和\5将替换为相应的捕获文本。
s命令末尾的g标志确保替换应用于所有匹配项。

这将在所有UNIX框上的任何shell中使用任何awk都能正常工作,如果您需要在每个列表中使用它来处理4个以上的项,只需在开始部分的字符串中添加更多的数字名称即可:

$ cat tst.awk
BEGIN { split("one two three four",names) }
{
    while ( match($0,/[0-9]+:\[('[^']*',?)+/) ) {
        idx = list = substr($0,RSTART,RLENGTH)

        sub(/:.*/,"",idx)
        sub(/[^[]+\[/,"",list)

        split(list,items,/'/)
        for (i=2; i in items; i+=2) {
            printf "%s%d=\"%s\"\n", names[i/2], idx, items[i]
        }
        print ""

        $0 = substr($0,RSTART+RLENGTH)
    }
}


这将在所有UNIX设备上的任何shell中使用任何awk都能很好地工作,如果您需要在每个列表中使用超过4项的awk,只需在“开始”部分的字符串中添加更多的数字名称,这一点就很容易增强:

$ cat tst.awk
BEGIN { split("one two three four",names) }
{
    while ( match($0,/[0-9]+:\[('[^']*',?)+/) ) {
        idx = list = substr($0,RSTART,RLENGTH)

        sub(/:.*/,"",idx)
        sub(/[^[]+\[/,"",list)

        split(list,items,/'/)
        for (i=2; i in items; i+=2) {
            printf "%s%d=\"%s\"\n", names[i/2], idx, items[i]
        }
        print ""

        $0 = substr($0,RSTART+RLENGTH)
    }
}


我不清楚你所说的我是什么意思-是db={1:['a','b','c','d',2:['aa','bb','cc','dd']}文件中的一行,是你试图在shell脚本中填充的变量,是在python脚本中填充的变量还是其他什么?请回答您的问题,以澄清您的输入是什么,清除所有使您的示例输入和预期输出变得混乱且不稳定的…并展示您迄今为止为解决此问题所做的努力。@Ed Morton,谢谢,在我的bash脚本中,我必须从一个文件中获取一些变量,因此我认为在字典格式中,文件格式会很好。所以我有一个test.txt,它包含这个字典格式。不要在注释中添加信息,你的问题会提供所有信息。@Morteza.J:如果你对bash解决方案感兴趣,为什么要使用awk标记?如果您愿意使用不同的编程语言,如awk,您也可以使用python或Perl或其他任何语言,因为它们都可以从bash调用。@user1934428 awk和python或Perl的最大区别在于,awk是所有UNIX设备上都可用的标准UNIX工具,而其他设备则不是。如果有人要一个bash脚本,他们非常、非常、很少指使用所有shell内置的脚本。我不清楚你所说的“我有”是什么意思-是db={1:['a'、'b'、'c'、'd']、2:['aa'、'bb'、'cc'、'dd']}文件中的一行,是您试图在shell脚本中填充的变量,是在python脚本中填充的变量还是其他什么?请澄清您的问题,以澄清您的输入是什么,摆脱所有的…将您的示例输入和预期输出弄乱,使其不稳定,并展示您迄今为止尝试解决问题的方法
问题。@Ed Morton,谢谢,在我的bash脚本中,我必须从一个文件中获取一些变量,所以我认为字典格式的文件格式会更好。所以我有一个test.txt,它包含这个字典格式。不要在注释中添加信息,你的问题会提供所有信息。@Morteza.J:如果你对bash解决方案感兴趣,为什么要使用awk标记?如果您愿意使用不同的编程语言,如awk,您也可以使用python或Perl或其他任何语言,因为它们都可以从bash调用。@user1934428 awk和python或Perl的最大区别在于,awk是所有UNIX设备上都可用的标准UNIX工具,而其他设备则不是。如果有人要求一个bash脚本,他们很少会说一个使用所有shell构建的脚本。非常感谢,太好了。如何将输出添加到数组中。例如:echo one${count}=echo$items | sed's/^.*\[/;s/\].$/'| cut-d'.-f1将此结果添加到数组中除了是错误的方法之外,该脚本实际上包含的错误多于代码行。复制/粘贴到中,它会告诉您一些问题,但可能不是所有问题。@Ed Morton,谢谢。非常感谢,太好了。如何将输出添加到数组中。例如:echo one${count}=echo$items | sed's/^.*\[/;s/\].$/'| cut-d'.-f1将此结果添加到数组中除了是错误的方法之外,该脚本实际上包含的错误多于代码行。复制/粘贴到中,它会告诉您一些但可能不是所有的问题。@Ed Morton,谢谢。很好的解释。谢谢,很好的解释。谢谢