Bash 在shell脚本中从字符串中提取信息

Bash 在shell脚本中从字符串中提取信息,bash,shell,awk,sed,Bash,Shell,Awk,Sed,我无法从shell脚本中的字符串中提取所需的信息。我已经阅读并试图想出正确的awk或sed命令来完成它,但我就是想不出来。希望你们能帮忙 str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false, "ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182, "isvalid":true,"name":"somena

我无法从shell脚本中的字符串中提取所需的信息。我已经阅读并试图想出正确的awk或sed命令来完成它,但我就是想不出来。希望你们能帮忙

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
假设我有一个字符串,如下所示:
[“ids”:2817262,“isvalid”:true,“name”:“somename”,“hasproperty”:false,“ids”:2262,“isvalid”:false,“name”:“somename”,“hasproperty”:false,“ids”:28182,“isvalid”:true,“name”:“somename”,“hasproperty”:true]

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
现在我要做的是将所有这些属性提取到单个字符串数组中。例如:

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
我想要一个ID为2817262226228182的数组 名称somename somename somename的数组 hasproperty的数组false false true

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
有人能帮我想出我需要的命令来把它拔出来吗。还要记住,字符串可能比这个长得多,因此如果我们不能将其特定于3种情况,这将是有帮助的。非常感谢。你可以使用grep

grep -oP '"ids":\K\d+' file
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
示例:

$ echo '["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,"isvalid":true,"name":"somename","hasproperty":true]' | grep -oP '"ids":\K\d+'
2817262
2262
28182
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"

grep解决方案很漂亮。你的问题被贴上了awk标签。awk解决方案很难看:

echo '["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,"isvalid":true,"name":"somename","hasproperty":true]' \
| awk '{split(substr($0,2,length($0)-2),x,",");
 for(i=0;i<length(x);i++) {split(x[i],a,":");
 if(a[1]=="\"ids\"") print a[1],a[2]}}'
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"

请选择grep解决方案作为正确答案。

因为它带有awk标记

awk '{while(x=match($0,/"ids":([^,]+)/,a)){print a[1];$0=substr($0,x+RLENGTH)}}' file
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
这只是保持匹配任何
id
s,然后将行更改为仅包含id后面的内容

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
输出 也可以这样做(灵感来源于Wintermutes对另一个答案的评论)

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"

下面是一个纯粹的bash解决方案(冗长,不是吗?我倾向于同意@chepner):

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
唯一的好处是没有子进程。

awk'BEGIN{
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
awk 'BEGIN {
   Field = 1
   Index = 0
   }
   {
   gsub( /[][]/,"")
   gsub( /"[a-z]*":/, "")
   FS=","

   while ( Field < NF) {
      ThisID[ Index]=$Field
      ThisName[ Index]=$(Field + 2)
      ThisProperty [ Index]=$(Field + 3)

      Index+=1
      Field+=4
      }
   }
END {
   for ( Iter=0;Iter<Index;Iter+=1) printf( "%s ", ThisID[Iter])
   printf "\n"
   for ( Iter=0;Iter<Index;Iter++) printf( "%s ", ThisName[Iter])
   printf "\n"
   for ( Iter=0;Iter<Index;Iter++) printf( "%s ", ThisProperty[Iter])
   printf "\n"
   }' YourFile
字段=1 索引=0 } { gsub(/[]]/,“”) gsub(/“[a-z]*”:/,“”) FS=“,” while(字段unset n string='[“ids”:2817262,“isvalid”:true,“name”:“somename”,“hasproperty”:false,“ids”:2262,“isvalid”:false,“name”:“somename”,“hasproperty”:false,“ids”:28182,“isvalid”:true,“name”:“somename”,“hasproperty”:true]' 当IFS=','读取-ra行时 做 ((n++) 对于“${line[@]/\”/}”中的i 做 eval${i%:*}[$n]=${i%:*:} 完成
完成<根据您发布的输入,如果您只需要每种类型项目的列表,那么这就是您所需要的:

str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"
$ awk -v RS=, -F: '{gsub(/[[\]"\n]/,"")} /^ids/{print $2}' file                 
2817262
2262
28182
$ awk -v RS=, -F: '{gsub(/[[\]"\n]/,"")} /^name/{print $2}' file
somename
somename
somename
$ awk -v RS=, -F: '{gsub(/[[\]"\n]/,"")} /^hasproperty/{print $2}' file
false
false
true
$ awk -v RS=, -F: '{gsub(/[[\]"\n]/,"")} /^isvalid/{print $2}' file    
true
false
true

但是,这不太可能是解决问题的正确方法。正如我在评论中提到的,如果您需要一些真正的帮助,请编辑您的问题以提供更多信息。

grep-oP'(?不要为此使用
bash
;使用具有适当数据结构和JSON解析器的语言。@chepner输入数据看起来类似于JSON,但不是JSON。需要更多
{}
。是的,请注意非常重要的改变游戏规则的问题:您引用的字段是否可以包含逗号(
)或冒号(
)例如,
“name”:“somename”
实际上可以是
“name”:“Smith,John”"
?wrt
我想要一个数组
-如果您不知道如何执行第一步,那么您认为下一步需要做的不太可能是正确的方法,所以现在不要太在意需要一个生成bash数组的解决方案。如果您告诉我们更多关于您试图对某些预期输出执行的操作,那么我们可以ELP。考虑<代码> AWK-V RS=,-F::{Sub(/^ \[/]));Sub(/\\\n,$/,“”);} 1==“\”ID“”{打印1美元,2美元}。“
.awk的功能比你想象的要强大一点。@Wintermute太棒了!请留下它作为答案,这样我就可以投票支持它。我并不认为像awk或grep这样的纯文本工具是最好的解决方法,所以我会通过。例如,如果字符串属性(
“name”
)中的一个属性值中包含冒号或逗号(更不用说
”)。这看起来像是应该由适当的解析器处理的结构化数据,但我不识别其格式。@Wintermute您可以只使用一个子
子(/^\[\\]\n?$/,“”)
在主体中设置FS太晚了,因为此时记录已被拆分为多个字段。[a-z]更便于携带[:lower:]。为什么在第一个循环中
Iter+=1
,而在其余的循环中
Iter++
?此外,所有awk函数和惯例创建的unlikc C awk数组都从1开始,所以虽然可以从零开始,但这可能会给您自己和/或以后阅读您的脚本的任何人造成混乱。nbd,但您不需要在printf ar周围设置参数您可以使用
print“”
而不是
printf“\n”
但是最好使用
printf“%s%s”,val(i@EdMorton+=和++是由于未能识别错误而进行调试(数组中最后一项的最后3个打印值等于$0,而索引0则等于$0)。奇怪的是,我先尝试打印““行失败了,所以我改为printf(事实上这是第一个问题,是原因,但我保留了代码)。您能解释一下为什么我的FS在体内仍然工作吗?它也是默认的分隔符吗?谢谢您的所有评论。
{FS=“,”}
将导致FS设置为“,”对于从输入文件中读取的第二行和后续行,而不是第一行,因为直到第一行已被读取并拆分为字段后,awk才会读取该语句。读取第二行时,FS已
“,”
因为这是awk读取/拆分第一行后脚本中的第一个操作。第一行将被读取/拆分,默认FS为
“”
str='["ids":2817262,"isvalid":true,"name":"somename","hasproperty":false,
"ids":2262,"isvalid":false,"name":"somename","hasproperty":false,"ids":28182,
"isvalid":true,"name":"somename","hasproperty":true]'

#Remove [ ]
str=${str/[/}
str=${str/]/}

declare -a ids
declare -a names
declare -a properties
oldIFS="$IFS"
IFS=','

for record in $str
do
    type=${record%%:*}
    value=${record##*:}

    if [[ $type == \"ids\" ]]
    then
        ids[ids_i++]="$value"
    elif [[ $type == \"name\" ]]
    then
        names[names_i++]="$value"
    elif [[ $type == \"hasproperty\" ]]
    then
        properties[properties_i++]="$value"
    else
        echo "Ignored type: '$type'" >&2
    fi
done

IFS="$oldIFS"
echo "ids: ${ids[@]}"
echo "names: ${names[@]}"
echo "properties: ${properties[@]}"