shell怎么多行并作一行处理?


如果第1行和第2行都不以;开头,则合并这两行为新行,并继续处理新行和第3行;
如果第1行以;开头,则继续处理第2行和第3行。
以上流程仅为方便描述,只要能达到相同效果即可。
如输入为:
;a
;;b
c
d;
e;;;
;f
g
期望输出:
;a
;;b
cd;e;;;
;f
g

Linux awk sed shell

新世界的卡米 10 years, 1 month ago

这种略复杂的需求,用sed/awk是可以实现,但是我觉得不推荐,这时候一般用python或者perl来做更高效。

不过为了看看我的sed功底还在不在,还是尝试了一下,目前简单测试没有发现问题,代码如下:


 sed -r -n '/^;/!{h;s/.*//;x;:l $!{H;n;/^;/!b l};x;s/\n//gp;g};p'

解释几个重点的部分:
1. h;s/.*//;x; 是为了清空hold space
2. :l $!{H;n;/^;/!b l}; 部分是一个循环,将所有的非 ; 开头的行合并到hold space,退出条件有两个:到达最后一行或者遇到一个 ; 开头的行
3. x;s/\n//gp;g 将hold space中的内容去掉换行符并打印出来
4. 最后 p 打印pattern space中的内容,用来打印 ; 开头的行

答案还没写完,发现一个bug了: 如果文件的末尾有2行以上的非 ; 开头的行,最后一行没有被合并。

修改一下原来的答案为:


 sed -r -n '/^;/!{h;s/.*//;x;:l $!{H;n;/^;/!b l};/^;/!{H;g;s/\n//gp;t};x;s/\n//gp;g};p'

修改说明:
1. 跳出循环之后,判断当前行如果不是 ; 开头(根据之前的跳出条件可知,这是最后一行),将当前行加入hold space,然后处理hold space的内容

看吧,一个其实也不是很复杂的需求,用sed来写,命令越写越长,1个月后还能一眼看懂么?这类需求还是不要用sed来做了吧

长-门-有-希 answered 10 years, 1 month ago

awk版本

awk '$0 !~ /^;/{a=a$0;}/^;/{print a?a"\n"$0:$0;a=""}END{if(a)print a}' urfile

再附加一个sed的

sed -n '/^;/{H;x;s/^\n//g;p;s/.*//g;x;};/^;/!{H;x;s/\n//g;x};${/^;/!p}' urfile
amonn answered 10 years, 1 month ago

首先: 将换行符去掉, /\n//g
然后: 判断字母序列右侧是否有数字,有则在右侧添加换行符 /([a-z]+)(?=[0-9]+)/\1\/n/g
判断字母序列左侧是否有数字,有则在左侧添加换行符 /([a-z]+)(?<=[0-9]+)/\/n\1/g
最后: 将上面正则表达式用 sed 或者 awk 实现。

ciadean answered 10 years, 1 month ago

答案


 awk '!/^;/{a=a$0}/^;/{if(a!="")print a;print $0;a="";}END{if(a!="")print a;}'

解释

awk语法 test1{statements1}test2{statements2}...

针对 每一行 ,如果满足test1,则执行statement1,如果满足test2,则执行statement2 ...

所以分3部分:

  • !/^;/{a=a$0} 如果不以 ; 开头,则将当前行追加到临时变量 a (作为缓冲区)中

    • !/^;/

      • ! 否定
      • /.../ 表示测试当前行是否满足给定正则表达式
      • ^; 正则表达式,表示以 ; 开头
    • a=a$0

      • a 变量,无需声明,直接使用,默认值是0、null、"",根据使用场景自动转换,这里第一次用就是空字符串
      • $0 表示整个一行的内容
      • a$0 两个字符串写在一起,表示字符串拼接
  • /^;/{if(a!="")print a;print $0;a="";} 如果以 ; 开头,先输出临时拼接的变量 a (若有),再输出当前行

    • /^;/ 判断当前行是否以 ; 开头
    • if(a!="")print a;print $0;a="";

      • if(a!="")print a; 如果a不为空,则输出a的值(print自动换行)
      • print $0; 打印当前行
      • a=""; 清空 a 的值,以备下次使用
  • END{if(a!="")print a;} 处理完所有行,最后再判断缓冲区 a 中是否有内容,若有,则打印

    • 如果最后几行都不以 ; 开头,会全部追加到 a 中,一直没有机会输出出来,因为碰到 ; 开头的行才会输出
    • END 条件表示处理完最后一行之后(相对的当然有 BEGIN ,表示处理第一行之前)
爱喝妇炎洁 answered 10 years, 1 month ago

Your Answer