如何在bash中将字符串拆分为数组

如何在bash中将字符串拆分为数组

我对程序的输出有问题。我需要在 bash 中启动一个命令并获取其输出(一个字符串)并将其拆分以在某些位置添加新行。该字符串如下所示:

battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500

基本上它是一个xxx.yy.zz:值,但该值可能包含空格。这是我想要得到的输出

battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500 

我有一个想法,搜索第一个点,然后从该位置向后查找空间以在那里放置新行,但我不确定如何在 Bash 中实现它。

答案1

纯bash解决方案,没有使用外部工具来处理字符串,只是参数扩展:

#! /bin/bash
str='battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500'

IFS=: read -a fields <<< "$str"

for (( i=0 ; i < ${#fields[@]} ; i++ )) ; do
    f=${fields[i]}

    notfirst=$(( i>0 ))
    last=$(( i+1 == ${#fields[@]} ))

    (( notfirst )) && echo -n ${f% *}

    start=('' $'\n' ' ')
    colon=('' ': ')
    echo -n "${start[notfirst + last]}${f##* }${colon[!last]}"
done
echo

说明:$notfirst$last是布尔值。第一个字段不会打印最后一个空格之前的部分${f% *},因为没有这样的东西。$start$colon保存分隔字段的各种字符串:第一项notfirst + last为 0,因此没有任何内容前置,对于其余行,$notfirst为 1,因此打印换行符,对于最后一行,加法给出 2,因此打印出一个空格。然后,打印最后一个空格之后的部分${f##* }。除最后一行外,所有行都打印冒号。

答案2

使用 GNU sed,您可以匹配以 结尾的每个连续字符串(即没有空格):,然后在除第一个字符串之外的所有字符串之前放置一个换行符:

sed 's/[^[:space:]]\+:/\n&/g2'

如果您的 sed 版本不支持gn扩展,您可以使用普通g修饰符

sed 's/[^[:space:]]\{1,\}:/\
&/g'

除了在第一个键之前打印一个额外的换行符之外,它的工作方式相同。您可以使用perl -pe 's/\S+:/\n$&/g'相同的附带条件(可能有 GNU sed 的 perl 等效项,g2但我不知道)。

答案3

一个perl办法:

$ perl -pe 's{\S+:}{$seen++ ? "\n$&" : "$&"}ge' file
battery.charge: 90 
battery.charge.low: 30 
battery.runtime: 3690 
battery.voltage: 230.0 
device.mfr: MGE UPS SYSTEMS 
device.model: Pulsar Evolution 500

解释

  • \S+:匹配字符串结尾为:.
  • 对于所有匹配的字符串,我们在它们之前插入换行符(("\n$&")第一个除外)($seen++)

答案4

这是一种简单的方法,假设您不关心输入中的制表符和换行符(如果有)被转换为普通空格,则该方法应该有效。

这个想法很简单:在空格上分割输入,并打印每个标记,除了在:前面添加以换行符结尾的标记(并在其他标记前面重新添加一个空格)。变量$count和相关变量if仅用于防止初始空行。如果没问题的话可以删除。 (该脚本假定输入位于intput当前目录中调用的文件中。)

#! /bin/bash

count=0
for i in $(<input) ; do
   fmt=
   if [[ $i =~ :$ ]] ; then
       if [[ $count -gt 0 ]] ; then
           fmt="\n%s"
       else
           fmt="%s"
       fi
       ((count++))
   else
       fmt=" %s"
   fi
   printf "$fmt" "$i"
done
echo
echo "Num items: $count"

我希望有人能想出一个更好的选择。

$ cat input
battery.charge: 90 battery.charge.low: 30 battery.runtime: 3690 battery.voltage: 230.0 device.mfr: MGE UPS SYSTEMS device.model: Pulsar Evolution 500
$ ./t.sh
battery.charge: 90
battery.charge.low: 30
battery.runtime: 3690
battery.voltage: 230.0
device.mfr: MGE UPS SYSTEMS
device.model: Pulsar Evolution 500
Num items: 6

相关内容