awk:更改字段分隔符,保持第一列不变

awk: change field separator keeping first column as is

我有一个只有一列的 in.csv 文件:

Sample
a_b_c
d_e_f
g_h_i

我想将字段分隔符从 _ 更改为 , 并打印单独的字段,但将输入列保留在输出文件的第一列中。原则上想用awk

这是我目前拥有的:

awk 'BEGIN {FS="_";OFS=","} {=}1' in.csv > out.csv

这给了我这个

Sample
a,b,c
d,e,f
g,h,i

我怎样才能像这样输出它,同时保留原始列(重命名 ID)?

ID,group1,group2,group3
a_b_c,a,b,c
d_e_f,d,e,f
g_h_i,g,h,i

请注意,输入的字段数是可变的,输入的 Sample 行可能是其他名称,或者为空,甚至 non-existent,但我仍然希望这样的输出。 ..

编辑

检查所有答案后,我必须在这里澄清上面的输入文件只是一个例子......我的真实文件通常有超过3个字段,由_分隔(但我没有事先知道有多少)和无数行,但是我会尝试确保给定文件中的所有行在要“拆分”的字段数上是一致的。

当我的文件每行要拆分的字段多于或少于 3 个时,下面的答案似乎不起作用,如果可能的话,我需要一个更通用的 one-liner。

目前,为了简单起见,我宁愿不对 header 行做任何事情,让它保持原样。

这意味着对于另一个示例:

Some_header
a_b_c_1
d_e_f_2
g_h_i_3

我想得到这个:

Some_header
a_b_c_1,a,b,c,1
d_e_f_2,d,e,f,2
g_h_i_3,g,h,i,3

最理想的情况是,one-liner 应该处理存在具有不一致字段的行的情况,因此来自这样的文件:

Some_header
a_b_c
d_e_f_2
g_h_i_3_4

我想得到这个:

Some_header
a_b_c,a,b,c
d_e_f_2,d,e,f,2
g_h_i_3_4,g,h,i,3,4

有没有办法在变量中记录_的行,然后用_分割变量,然后打印变量及其所有组件用[=分隔20=]?抱歉,我认为这会更容易...也许使用 Perl one-liner 会更容易?抱歉,对 one-liner 不是很熟练...再次感谢!

我看不出有什么理由改变 FS。只需打印您真正想要打印的内容,而不是使用某些默认 awk 行为 {=}1

awk '
   BEGIN {FS="_"; OFS=","}
   NR==1{print "ID,group1,group2,group3"}
   NR!=1{print [=10=], , , }
'

您能否尝试仅在显示的样本上进行以下、编写和测试。这应该适用于任何数量的字段,也在 https://ideone.com/fWgggq

中对其进行了测试
awk '
BEGIN{
  FS="_"
  OFS=","
  print "ID,group1,group2,group3"
}
FNR>1{
  val=[=10=]
  =
  print val,[=10=]
}'  Input_file

说明: 为以上添加详细说明。

awk '                                   ##Starting awk program from here.
BEGIN{                                  ##Starting BEGIN section of program from here.
  FS="_"                                ##Setting field separator as _ here,
  OFS=","                               ##Setting OFS as comma here.
  print "ID,group1,group2,group3"       ##Printing header as per OP requirement here.
}
FNR>1{                                  ##Checking condition if this is greater than 1st line then do following.
  val=[=11=]                                ##Store current line into var val here.
  =                                 ##reassign first field to itself so that new OFS which is , is implemented to whole line.
  print val,[=11=]                          ##Printing current new line here.
}'  Input_file                          ##Mentioning Input_file name here.

考虑以下简短的 awk 脚本,结合上面评论者的意见。它将根据第 2 行中的数据生成 header 行 - 以匹配字段数

awk '
NR > 1 {
    n=split([=10=], a, "_") ;
    if (NR == 2 ) { printf "ID" ; for (i=1 ; i<=n ; i++) printf ",group%d", i ; printf "\n" }
    v=[=10=]
    sub("_", ",", v)
    print [=10=] "," v
}' filename.txt

另一个不处理 header 行(无论如何,留作练习等):

$ awk '
BEGIN {
    FS="_"                                # set delimiters
    OFS=","
}
{
    for(i=0;i<=NF;i++)                    # loop from 0 to get [=10=]
        printf "%s%s",$i,(i==NF?ORS:OFS)  # print dealing with OFS and EOL
}' file

输出:

Sample,Sample
a_b_c,a,b,c
d_e_f,d,e,f
g_h_i,g,h,i

还有一个处理 具有可变组数的不同输入文件 从第一个数据记录中选取 header 计数 (NR==2) :

$ awk '
BEGIN {
    FS="_"                                # set delimiters
    OFS=","
}
NR>=2 {                                   # process only data records, not header
    if(NR==2)                             # create the header
        for(i=0;i<=NF;i++)
            printf "%s%s",(i==0?"ID":"group" i),(i==NF?ORS:OFS)
    for(i=0;i<=NF;i++)                    # loop from 0 to get [=12=]
        printf "%s%s",$i,(i==NF?ORS:OFS)  # print dealing with OFS and ORS
}' file

输出:

ID,group1,group2,group3
a_b_c,a,b,c
d_e_f,d,e,f
g_h_i,g,h,i

最后是一个使用 GNU awk 的短片:

$ awk '[=14=]=[=14=] (gensub(/(^|_)/,",","g"))' file

为了好玩这里还有一个awk:

awk 'NR==1{print "ID,group1,group2,group3"; next}
{s=[=10=]; gsub(/^|_/, ","); print s [=10=]}' file
ID,group1,group2,group3
a_b_c,a,b,c
d_e_f,d,e,f
g_h_i,g,h,i