bash 条件 getline 与 awk/tr/sed?

bash conditionnal getline with awk/tr/sed?

我正在为此苦苦挣扎,我想将一组行连接成一行 line/row。 我文件的每一行 (titi/toto/tata) 都有 2 或 3 个字段,用“;”分隔 所以我的输入是这样的:

titi1
titi2 
titi3
43;75;97
1;2;87
toto1
toto2
toto3
40;50;60
tata1
tata2
tata3
4;5;2
5;3;7
2;5;9

我需要这个输出:

titi1;titi2;titi3;43;75;97
titi1;titi2;titi3;1;2;87
toto1;toto2;toto3;40;50;60
tata1;tata2;tata3;4;5;2
tata1;tata2;tata3;5;3;7
tata1;tata2;tata3;2;5;9

您是否看到前 3 行是信息(toto/tata 等...),在以数字开头的每一行之后都应重复这些信息。

首先,我的输入只有一行数字,所以它是一个 4 乘 4 的分组。所以我在论坛中搜索了我是否找到了一个示例并使用这样的 getline 进行了此操作:

awk '{getline b; getline c; getline d;printf("%s %s %s %s\n",[=14=],b,c,d)}'

但后来我开始有 2 个甚至 3 个 lign with numbers... 所以我正在努力做一个 'conditionnal' 理解它应该在每次看到以数字开头的 lign 时重复前 3 lign。

能否请您尝试以下。

awk '
{
  sub(/ +$/,"")
}
/^[a-zA-Z]+/{
  if(val && flag){
    val=""
  }
  val=val?val ";" [=10=]:[=10=]
  flag=""
  next
}
{
  flag=1
  print val ";" [=10=]
}'  Input_file

解决方案 2: 如果您的 Input_file 可以将最后一行设为 tot 等,而您想要也打印它然后使用以下内容。

awk '
{
  sub(/ +$/,"")
}
/^[a-zA-Z]+/{
  if(val && flag){
     val=""
  }
  val=val?val ";" [=11=]:[=11=]
  flag=""
  next
}
{
  flag=1
  print val ";" [=11=]
}
END{
  if(val && !flag){
     print val
  }
}'  Input_file

你可以试试这个 awk :

awk -F';' 'NF==1{if(b){a=b=""};a=a[=10=]FS;next}{b=1;[=10=]=a[=10=]}1' infile

更易懂

awk -F ';' '
  NF==1 {
    if ( b ) {
      a = b = "" 
    }
    a = a [=11=] FS 
    next
  }
  {
    b = 1
    [=11=] = a [=11=]
  } 1
' infile

这个程序应该是这样的:

awk 'f&&/^[^0-9]/{b="";f=0} /^[^0-9]/{b=b[=10=]";"} /^[0-9]/{print b[=10=];f=1}'

解释:

  1. /^[^0-9]/{b=b[=11=]";"}
  2. /^[0-9]/{print b[=11=];f=1}
  3. f&&/^[^0-9]/{b="";f=0}
  1. 行不以数字开头:收集输入 (titi,toto,tata)
  2. 行以数字开头:打印收集的行并[=12=],设置标志
  3. 行不再以数字开头(已设置标志):重新开始(清除缓冲区和标志)
$ awk -F';' 'NF>1{print s [=10=]; p=1; next} p{s=p=""} {s=s [=10=] FS}' file
titi1;titi2;titi3;43;75;97
titi1;titi2;titi3;1;2;87
toto1;toto2;toto3;40;50;60
tata1;tata2;tata3;4;5;2
tata1;tata2;tata3;5;3;7
tata1;tata2;tata3;2;5;9

使用您的原始脚本 - 请参阅 http://awk.freeshell.org/AllAboutGetline 了解为什么不为此(或大多数其他情况)使用 getline 以及如何在适合这样做的极少数情况下正确调用 getline。

这可能对你有用 (GNU sed):

sed -r '/;/{:a;G;s/([^\n]*)\n(.*)/\n/;s/.//;s/\s*\n/;/g;n;/;/ba;x;z;x};H;d' file

使用保留 space 来存储每条记录的第一部分。当遇到记录的结尾部分时,追加保留 space,重新排列最后一部分以跟在第一部分之后,删除第一个换行符并将剩余的换行符替换为分号。打印记录,如果下一行是记录的结尾部分,则重复。否则,清除保留 space 并将当前行附加到保留 space.