使用分隔符提取行并附加为文件中的列

使用分隔符提取行并附加为文件中的列

我有一个包含类似数据的文件:

a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 

...我需要这样的输出:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

我可以使用 python 来完成此操作,但我想使用 shell 脚本来尝试此操作。我正在考虑首先隔离包含分隔符“,”的行,然后继续工作。到目前为止我已经使用它来隔离行:

perl -F, -ane 'print if $#F >=1' filename

...但我陷入了下一步。

答案1

perl

$ perl -lane '@v=split/,/,$F[1]; $i=0;
              print "$_ $v[$i++]" for split/,/,$F[0]' ip.txt
a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

拆分第一列/第二列,初始化索引计数器,然后通过迭代拆分另一列来打印对

-a选项将自动在空格上分割输入行并将结果放入@F数组中

答案2

Awk解决方案(假设“键”的数量(包含在第一个字段中$1)始终对应于“值”的数量(包含在第二个字段中$2)):

awk '$1 ~ /,/{
         len = split($1, keys, ",");
         split($2, vals, ",");
         for (i = 1; i <= len; i++) print keys[i], vals[i];
         next
    }1' file

输出:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

答案3

使用 sed 编辑器执行此操作的一种方法是:

sed -e '
   s/,/\n/
   s/\(\n.*[[:blank:]]\)\([^,]*\),/ \2\1/
   P;D
' input.file

在职的:

  • 从第二个字段中剪辑以逗号分隔的前导元素。
  • 然后将此元素附加到第一个字段的前导逗号分隔元素中。
  • 打印第一个字段的前导元素,然后将其删除。
  • 对模式空间中剩余的内容重复此过程,直到其为空。

另一种使用 Perl 的方法是:

perl -lane '
   my($kref, $vref, %h) = map { [split /,/] } @F[0,1];
   @h{@$kref} = @$vref;
   print "$_  $h{$_}" for @$kref;
' input.file

另一种方式如下所示:

perl -lpe 'print "$1 $3" while s/^([^,]*),(.*\h)([^,]*),/$2/' input.file

在职的:

  • 这样看正则表达式:(Perl 从文件中一次读取一行)然后:
    • ^([^,]*)应选择当前行的第一个字段的前导逗号分隔元素。这存储在 $1 变量中。
    • (。*\H)对于 while 循环的下一次迭代,应保留从第一个字段的第二个逗号分隔元素开始到第二个字段的第二个逗号分隔元素开头的中间内容。这存储在 $2 变量中。
    • ([^,]*)应从当前行的第二个字段中选择前导逗号分隔的元素。这将存储在 $3 变量中。
    • 现在“$1 $3”被打印到 STDOUT 并且该行被缩小到 $2。 while 循环现在在这个编辑的行上再次执行操作,这是前一行的 $2,.....这会重复,直到 s/// 成功。当我们用完逗号时,就会失败。此时,行中剩余的内容“c 5”将通过 perl 在 -p 模式下的默认行为打印到 STDOUT。
  • 从第一个和第二个字段中取出以逗号分隔的前导元素。
  • 打印这些元素并通过删除来缩小当前记录。
  • 当当前记录有 2 个逗号时,循环遍历该记录。
  • 由于 Perl 的 -p 选项,最后一对会自动打印。

perl -lane '
   my($kref, $vref) = map { [split /,/] } @F;
   print shift @$kref, " ", shift @$vref while @$kref && @$vref;
' input.file

在职的:

  • 键存储在数组@$kref 中,相应的值存储在@$vref 中。请注意,此处不涉及哈希值。
  • 同时打印数组的顶部,然后删除顶部...冲洗,当两个数组都非空时重复。

输出:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

答案4

“使用 shell 脚本”——这是 bash:

while read -r key value; do
    IFS=, read -ra keys <<<"$key"
    IFS=, read -ra vals <<<"$value"
    for ((i=0; i < ${#keys[@]}; i++)); do
        echo "${keys[i]} ${vals[i]}"
    done
done <<END
a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 
END

相关内容