Маніпулювання текстом за допомогою 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; надрукує друге поле та пробіл.
  • 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-е поле, a :, потім поточне поле та a :1.
  • print "\n" : це просто друкує остаточний новий рядок.

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/ $// видаліть простір

Отже, додавши структуру до першої частини, ми неодноразово знаходимо останній екземпляр структури та застосовуємо її до наступного числа ...

Але я згоден, що інші інструменти полегшують ...


Я чекав вашого рішення sed: D
Ravexina

: D це зайняло у мене час @Ravexina - я вважаю, що Муру може зробити чистіше
Zanna

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змінними, і ми друкуємо на другому елементі stdout (слова [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) ітераціює над третім по другим останніми полями і друкує поля у потрібному форматі (спочатку поле, потім двокрапка, потім поточне поле, потім двокрапка, нарешті 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.