使用 sed 进行文本操作

使用 sed 进行文本操作

目前,我有多个文本文件,其内容如下所示(有很多行):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

我希望将每一行更改为以下格式:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

有没有办法使用 sed 完成上述操作?还是我需要求助于 Python?

答案1

是的,您可以使用 sed 来完成此操作,但其他工具更简单。例如:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

解释

awk 将按空格分割每行输入(默认情况下),并将每个字段保存为$1, $2, $N。所以:

  • printf "%s ", $2;将打印第二个字段和尾随空格。
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }:将遍历字段 3 到最后一个字段(NF是字段数),并且对于每个字段,它将打印第 1 个字段 a :,然后是当前字段和 a :1
  • print "":这只是打印最后的换行符。

或者 Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

解释

makes-aperl行为类似于awk并在空格上拆分其输入。在这里,字段存储在数组中@F,这意味着第一个字段将是$F[0],第二个字段将是$F[1]等等。所以:

  • print "$F[1] ":打印第二个字段。
  • print "$F[0]:$_:1 " for @F[2..$#F];:迭代字段 3 到最后一个字段($#F是数组中元素的数量@F,因此@F[2..$#F]从第 3 个元素开始获取数组切片直到数组末尾)并打印第 1 个字段 a :,然后是当前字段和 a :1
  • print "\n":这只是打印最后的换行符。

答案2

这里有一个可怕 sed方式!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

更具可读性:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

笔记

  • -r使用 ERE
  • s/old/new/old用。。。来代替new
  • ^([0-9]+)在行首保存一些数字
  • \1反向引用第一个保存的模式
  • :a标记此部分脚本a
  • ( |$)空格或行尾
  • t测试最后一次替换是否成功 - 如果成功,则执行下一个命令
  • a找到标签:a并再次执行
  • s/ $//删除尾随空格

因此,在将结构添加到第一部分后,我们反复找到该结构的最后一个实例并将其应用于下一个数字......

但我同意其他工具可以让它变得更容易......

答案3

使用 awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

或者使用 bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

输出:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

答案4

awk

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

这一切都是为了按照所需的格式格式化空格分隔的字段:

  • printf("%s ", $2)打印带有尾随空格的第二个字段

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i)遍历倒数第三个到第二个字段,并以所需格式打印这些字段(第一个字段,然后是冒号,然后是当前字段,然后是冒号,最后是 1),尾随空格

  • printf("%s:%s:1\n", $1, $NF)打印最后一个字段并添加换行符

例子:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

相关内容