Работа с текстом с помощью sed

12

В настоящее время у меня есть несколько текстовых файлов с содержимым, похожим на это (со многими строками):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Я хочу изменить каждую строку, чтобы иметь следующий формат:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Есть ли способ сделать выше, используя sed? Или мне нужно прибегнуть к Python?

Занна
источник

Ответы:

22

Да, вы можете сделать это с помощью sed, но другие инструменты проще. Например:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

объяснение

AWK разделит каждую строку ввода на пробельном (по умолчанию), экономя каждое поле , как $1, $2, $N. Так:

  • printf "%s ", $2; напечатает 2-е поле и завершающий пробел.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: перебирает поля 3 до последнего поля ( NFэто число полей) и для каждого из них выводит 1-е поле a :, затем текущее поле и a :1.
  • print "" : это просто печатает последний перевод строки.

Или Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

объяснение

В -aмарке perlведет себя как awkи разделить его вход на пробельном. Здесь поля хранятся в массиве @F, что означает, что первое поле будет $F[0], второе $F[1]и т. Д. Итак:

  • print "$F[1] " : напечатать 2-е поле.
  • print "$F[0]:$_:1 " for @F[2..$#F];: перебрать поля 3 до последнего поля ( $#Fэто количество элементов в массиве @F, поэтому @F[2..$#F]принимает срез массива, начиная с 3-го элемента до конца массива) и печатать 1-е поле, а :, затем текущее поле и :1,
  • print "\n" : это просто печатает последний перевод строки.
terdon
источник
12

Вот какой ужас sed путь!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Более читабельно:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

Примечания

  • -r использовать ERE
  • s/old/new/заменить oldнаnew
  • ^([0-9]+) сохранить несколько чисел в начале строки
  • \1 обратная ссылка на первый сохраненный шаблон
  • :a пометить этот раздел сценария a
  • ( |$) либо пробел, либо конец строки
  • t проверить, была ли последняя замена успешной - если это было, то выполните следующую команду
  • aнайдите этикетку :aи сделайте это снова
  • s/ $// убрать завершающий пробел

Поэтому после добавления структуры в первую часть мы неоднократно находим последний экземпляр структуры и применяем его к следующему числу ...

Но я согласен, что другие инструменты делают это проще ...

Занна
источник
Я ждал твоего решения для седа: D
Равексина
: D это заняло у меня некоторое время @Ravexina - я думаю, что Муру может сделать чище
Занна
5

С awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

или с bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

Выход:

0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
1 564: 7: 1 564: 12: 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 
Кир
источник
5

Ну, вы можете сделать это в Sed, но Python работает также.

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Содержимое таково reformatfile.py:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

Как это работает? Там действительно ничего особенного не происходит. Мы открываем первый аргумент командной строки как файл для чтения и переходим к разбивке каждой строки на «слова» или отдельные элементы. Первые слова становятся prefпеременными, и мы печатаем второй элемент (слова [1]), заканчивающийся пробелом. Затем мы создаем новый набор «слов» через списочные выражения и работаем .join()с временным списком преф, каждого слова и строки "1". Последний шаг - распечатать их

Сергей Колодяжный
источник
4

С awk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

Это все о форматировании разделенных пробелами полей в желаемом формате:

  • printf("%s ", $2) печатает второе поле с пробелом

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) перебирает последние 3–2 поля и печатает поля в нужном формате (первое поле, затем двоеточие, затем текущее поле, затем двоеточие, наконец 1) с завершающим пробелом

  • printf("%s:%s:1\n", $1, $NF) печатает последнее поле с новой строкой

Пример:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
heemayl
источник