Разделение второго столбца строки для создания нескольких строк с помощью bash oneliner - PullRequest
2 голосов
/ 10 марта 2020

У меня есть данные, разделенные табуляцией, и они выглядят так:

a   1a,2x,c1
b2  a4,4.6
3c  323

Во втором столбце есть несколько значений, разделенных запятыми. Я хочу получить этот вывод:

a   1a
a   2x
a   c1
b2  a4
b2  4.6
3c  323

Я смог сделать это с помощью этого python кода, который я написал:

import sys
f = sys.argv[1]

with open(f) as f:
    for line in f:
        line = line.strip("\n").split("\t")
        genes = line[1].split(",")
        for gene in genes:
            print(line[0],gene, sep="\t")

Я знаю, что могу сделать то же самое с bash скрипт, но я хотел бы знать, как я могу сделать это с классным bash oneliner, используя awk, sed, tr и / или cut без использования для l oop.

Я не мог t go дальше этого:

tr ',' '\n' data

Ответы [ 6 ]

4 голосов
/ 10 марта 2020

Чтобы не писать al oop, с GNU awk для нескольких символов RS:

$ awk -v RS='[,\n]' 'NF>1{k=$1} {print k, $NF}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

Для сравнения al oop более понятен, проще и будет работать с любым awk в любой оболочке на каждая UNIX коробка:

$ awk -F'[[:space:]]+|,' '{for (i=2; i<=NF; i++) print $1, $i}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

По производительности не будет существенной разницы, они оба будут работать достаточно быстро.

4 голосов
/ 10 марта 2020

РЕДАКТИРОВАТЬ: В соответствии с запросом OP без l oop будет (проверено и написано только с предоставленными образцами), (Справедливое предупреждение: gsub версия с трубкой любопытство от OP, и оно более на agile и медленнее, чем просто использование для l oop и сохранение всей обработки внутри awk):

awk '{gsub(/,/,ORS $1 OFS)} 1'  Input_file | column -t

Brief Объяснение: Использование gsub функции awk для глобальной замены всех вхождений , в каждой строке на ORS (новая строка по умолчанию это значение) $ 1 (первое поле согласно требованию OP) OFS ( пробел по умолчанию его значение). Тогда упоминание 1 напечатает отредактированную / нередактированную строку здесь. Затем передавая awk вывод команды в column команду, чтобы украсить ее вывод тем же пробелом.

Не могли бы вы попробовать следующее.

awk '{num=split($2,array,",");for(i=1;i<=num;i++){print $1,array[i]}}' Input_file
3 голосов
/ 10 марта 2020
$ awk -F'[\t,]' '{for (i=2;i<=NF;i++) printf "%s\t%s\n", $1, $i }' file
a       1a
a       2x
a       c1
b2      a4
b2      4.6
3c      323

Используйте табуляцию и запятую в качестве разделителей полей и l oop через поля, начиная со второго поля. Выведите первое поле и значение зацикленного поля, разделенное табуляцией.

3 голосов
/ 10 марта 2020
$ awk -F$'\t' '{split($2,arr,","); for(e in arr) print($1, arr[e])}' file
a 1a
a 2x
a c1
b2 a4
b2 4.6
3c 323

Если вы хотите, чтобы вкладка была между столбцом 1 и 2:

awk -F$'\t' '{split($2,arr,","); for(e in arr) print($1 "\t" arr[e])}'
a   1a
a   2x
a   c1
b2  a4
b2  4.6
3c  323
1 голос
/ 10 марта 2020

GNU sed

sed -E ':a; s/([^ ]+) *([^,]+),([^,]+)/\1 \2\n\1 \3/; ta' infile

Пояснение

Это работает путем многократного сопоставления 3 групп с круглыми скобками.

  • ([^ ]+) совпадать с первым пробелом
  • ([^,]+) соответствует до первой запятой
  • ([^,]+) соответствует до возможной второй запятой
  • \1 \2\n\1 \3 заменяет то, что соответствует первой группе за ним следует вторая группа, затем новая строка, за которой следуют первая группа и третья группа

Portable sed

parse.sed

:a
s/([^ ]+) *([^,]+),([^,]+)/\1 \2\
\1 \3/
ta

Запустите его так:

sed -Ef parse.sed infile

Вывод в обоих случаях:

a 1a
a 2x
a c1
b2 a4
b2 4.6
3c  323
0 голосов
/ 11 марта 2020

Это может работать для вас (GNU sed):

sed -E 's/^((.*\t)[^,]+),/\1\n\2/;P;D' file

Заменить первые два значения, разделенных табуляцией, после запятой первые два значения, разделенных табуляцией, символ новой строки и первое значение, за которым следует символ вкладка. Распечатайте и удалите первую строку в области шаблона и повторите.

...