【问题标题】:awk: change field separator keeping first column as isawk:更改字段分隔符,保持第一列不变
【发布时间】:2020-11-24 09:07:13
【问题描述】:

我有一个in.csv 文件,其中只有一列:

Sample
a_b_c
d_e_f
g_h_i

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

这是我目前所拥有的:

awk 'BEGIN {FS="_";OFS=","} {$1=$1}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 行可能是其他名称,或者为空,甚至不存在,但我仍然希望这样的输出......

编辑

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

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

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

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

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

最佳情况下,单行应该处理存在字段不一致的行的情况,因此从这样的文件中:

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

难道没有办法将_的行记录在一个变量中,然后用_分割变量,然后打印出用,分隔的变量及其所有组件?抱歉,我认为这会更容易......也许使用Perl 单线会更容易?抱歉,对单行词不太熟练...再次感谢!

【问题讨论】:

  • preserving the original column (renamed ID) 保留还是重命名?
  • 保留原样,使用不同的标头(但仍可以是相同的标头)
  • 签出split() 函数。 split($0,"_",array);print $0","a[0],a[1],a[2]
  • 请检查我的编辑

标签: regex awk field


【解决方案1】:

我认为没有理由更改 FS。只需打印您想要实际打印的内容,而不是使用某些默认的 awk 行为来打印 {$1=$1}1

awk '
   BEGIN {FS="_"; OFS=","}
   NR==1{print "ID,group1,group2,group3"}
   NR!=1{print $0, $1, $2, $3}
'

【讨论】:

  • 问题是我有不同数量的组的不同输入文件......我想要完全像你的答案,但如果可能的话扩展到所有情况。所以它将是 print group +1:n 其中n 是组数。然后打印$0 和所有组字段
  • @DaniCee 变量是输入参数,还是要从标头或数据行派生?每个输入文件是恒定的吗? (即,所有行是否一致?)
  • 原则上所有行都是一致的,是的,它是由_“拆分”后的结果字段数得出的
  • 我真的不希望它成为一个非常复杂的解决方案,到目前为止我所做的是在我的问题中使用单行,然后将来自in.csv 的列添加为out.csv 使用 Excel(手动更改标题名称)...只是想知道这是否可以轻松实现
  • b=$0; gsub(/_/,",",b); print $0 "," b
【解决方案2】:

您能否尝试仅在所示示例上进行以下、编写和测试。这应该适用于在https://ideone.com/fWgggq中测试过的任意数量的字段@

awk '
BEGIN{
  FS="_"
  OFS=","
  print "ID,group1,group2,group3"
}
FNR>1{
  val=$0
  $1=$1
  print val,$0
}'  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=$0                                ##Store current line into var val here.
  $1=$1                                 ##reassign first field to itself so that new OFS which is , is implemented to whole line.
  print val,$0                          ##Printing current new line here.
}'  Input_file                          ##Mentioning Input_file name here.

【讨论】:

  • 出于某种原因,这适用于具有 3 个字段的示例文件(如 a_b_c),但有更多字段(如 a_b_c_d)或更少字段(如 a_b) ,它不起作用......
  • 请检查我的编辑
  • @DaniCee,嗨,我的解决方案没有经过编码,它适用于您的任意数量的字段,请检查我在 URL ideone.com/fWgggq 中测试过的这个
  • 好的,它可以工作,我的文件以 DOS 文件结尾而不是 UNIX,我必须首先在文件上运行 dos2unix,现在 awk 单行线按预期工作......奇怪虽然
  • @DaniCee,欢迎您乐于助人,感谢您的分享,您在 dos2unix 的欢呼声和 SO 上的快乐学习中修复了它 :)
【解决方案3】:

考虑以下简短的awk 脚本,结合上述评论者的输入。它将根据第 2 行中的数据生成标题行 - 以匹配字段数

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

【讨论】:

  • 这似乎不能正常工作,你能提供完整的代码吗?因为它可能正是我正在寻找的...谢谢!
  • 请检查我的编辑
  • @DaniCee 我添加了最小的包装器来从 shell 执行程序。它可以处理您问题中的所有示例
  • 我得到以下信息:awk: calling undefined function gensub input record number 2, file in.csv source line number 5
  • @DaniCee: gensub 是标准的 awk 函数。你用什么版本的awk?解决方案适用于 Mint 19.04、awk 4.1.4
【解决方案4】:

另一个,但是,不处理标题行(atm 无论如何,留作练习等):

$ awk '
BEGIN {
    FS="_"                                # set delimiters
    OFS=","
}
{
    for(i=0;i<=NF;i++)                    # loop from 0 to get $0
        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

另一个确实处理具有可变组数的不同输入文件从第一个数据记录(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 $0
        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 '$0=$0 (gensub(/(^|_)/,",","g"))' file

【讨论】:

  • 出于某种原因,这适用于具有 3 个字段的示例文件(如 a_b_c),但有更多字段(如 a_b_c_d)或更少字段(如 a_b) ,它不起作用......
  • 请检查我的编辑
  • 好的,它可以工作,我的文件以 DOS 文件结尾而不是 UNIX,我必须首先在文件上运行 dos2unix,现在 awk 单线工作正常......奇怪虽然
  • 如果您使用 GNU awk(或 mawk 或 Busybox awk),您可以设置 RS="\r\n"RS="\r?\n"
【解决方案5】:

为了好玩,这里是另一个awk

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

【讨论】:

  • 出于某种原因,这适用于具有 3 个字段的示例文件(如 a_b_c),但现在有更多字段(如 a_b_c_d)或更少字段(如 a_b) ,它不起作用......
  • 请检查我的编辑
  • 好的,它可以工作,我的文件以 DOS 文件结尾而不是 UNIX,我必须先在文件上运行 dos2unix,现在 awk 单线工作正常......奇怪虽然
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 2015-03-05
相关资源
最近更新 更多