awk: изменить разделитель полей, сохранив первый столбец как есть - PullRequest
4 голосов
/ 04 августа 2020

У меня есть файл 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 может иметь другое имя или быть пустой, или даже не существует, но мне все равно нужен такой вывод ...

EDIT

После проверки всех ответов я должен уточнить здесь ввод приведенный выше файл является просто примером ... в реальных файлах, которые у меня обычно есть, более 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? Извините, я не очень разбираюсь в однострочниках ... Еще раз спасибо!

Ответы [ 5 ]

6 голосов
/ 04 августа 2020

Не могли бы вы попробовать следовать, написано и протестировано только на показанных образцах. Это должно работать с любым количеством полей, также протестированных в 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.
4 голосов
/ 04 августа 2020

Другой, который, однако, не имеет отношения к строке заголовка (atm в любом случае оставлен как упражнение et c.):

$ 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
2 голосов
/ 04 августа 2020

Ради интереса вот еще 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
2 голосов
/ 04 августа 2020

Не вижу смысла менять ФС. Просто напечатайте то, что вы хотите напечатать, вместо {$1=$1}1, используя поведение по умолчанию awk.

awk '
   BEGIN {FS="_"; OFS=","}
   NR==1{print "ID,group1,group2,group3"}
   NR!=1{print $0, $1, $2, $3}
'
1 голос
/ 04 августа 2020

Рассмотрим следующий короткий сценарий 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
...