комбінувати текстові файли в стовпці


52

У мене є два текстові файли. Перший містить вміст:

Languages
Recursively enumerable
Regular

а другий вміст:

Minimal automaton
Turing machine
Finite

Я хочу об'єднати їх в один файл з колонками. Тому я спробував, paste 1 2і його результат:

Languages   Minimal automaton
Recursively enumerable  Turing machine
Regular Finite

Однак я хотів би, щоб стовпці були добре вирівняні, такі як

Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Мені було цікаво, чи можна цього досягти без обробки вручну?


Додано:

Ось ще один приклад, де метод Брюса ледь не забиває його, за винятком невеликих нерівностей, щодо яких я цікавлюсь чому?

$ cat 1
Chomsky hierarchy
Type-0
—

$ cat 2
Grammars
Unrestricted

$ paste 1 2 | pr -t -e20
Chomsky hierarchy   Grammars
Type-0              Unrestricted
—                    (no common name)

3
Цей останній приклад, з нерівності, - це дози. Я можу дублювати його на Arch linux, pr (GNU coreutils) 8.12. Я не можу дублювати його на літньому програмному забезпеченні Slackware (11.0), у мене також є: pr (GNU coreutils) 5.97. Проблема полягає в символі "-", і він знаходиться в pr, а не вставці.
Брюс Едігер

1
Я отримую те ж саме з EM-DASH з обома prі expand... columnsуникає цього питання.
Пітер.O

Я створив висновок для більшості різних відповідей, за винятком awk + paste , який змістить стовпець, розміщений вліво праворуч, якщо лівий файл коротший, ніж будь-який t праворуч від нього. Те саме, і більше, стосується "вставити + стовпець", який також має цю проблему з порожніми рядками в лівій колоні (их) ... Якщо ви хочете переглянути всі результати разом. ось посилання: paste.ubuntu.com/643692 Я використав 4 колонки.
Пітер.О

Я просто зауважив , що - то вводить в оману з paste.ubuntu посиланням ... Я спочатку встановити вгору даних для тестування своїх сценаріїв, (і це призвело до робити інші) ... так що поля , які говорять , що , ➀ unicode may render oddly but the column count is ok безумовно , робить НЕ застосовується до wc-paste-prі wc-paste-prThey покажіть відмінності підрахунку стовпців.
Пітер.O

1
@BruceEdiger: Проблема вирівнювання виникає, коли використовуються символи, що не належать до ASCII (в його питанні ОП використовував тире (-) замість символу мінус (-)), швидше за все, через погану або відсутність обробки prмультибайта символів у поточному локалі (зазвичай UTF8).
WhiteWinterWolf

Відповіді:


68

Вам просто потрібна columnкоманда і сказати їй використовувати вкладки для розділення стовпців

paste file1 file2 | column -s $'\t' -t

Для вирішення суперечки про "порожню клітинку" нам просто потрібна -nопція column:

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -t
foo        1
2
barbarbar  3

$ paste <(echo foo; echo; echo barbarbar) <(seq 3) | column -s $'\t' -tn
foo        1
           2
barbarbar  3

На моєму стовпчику вказується -n"розширення Debian GNU / Linux". У моїй системі Fedora не виникає проблем із порожніми клітинками: вона, схоже, походить від BSD, а на сторінці "man" сказано, що "Версія 2.23 змінила параметр -s, щоб бути не жадібним"


4
glenn: Ти герой години! Я знав, що є щось подібне навколо, але я не міг це запам'ятати. Я ховався з цього питання; чекаю вас :) ... column, звичайно; як очевидно (заднім
поглядом

4
Я щойно помітив, що column -s $'\t' -tігнорує порожні комірки , в результаті чого всі наступні комірки праворуч від неї (у цій лінії) переміщуються ліворуч; тобто в результаті порожній рядок у файлі, або він коротший ... :(
Peter.O

1
@masi, виправлено
glenn jackman

-n не працює в RHEL. Чи є альтернатива?
Кошур

Нарешті я можу прокоментувати, тому хочу зазначити, що раніше я додав відповідь нижче, яка стосується проблеми Peter.O із запусками порожніх комірок за допомогою нулів.
техно

11

Ви шукаєте зручну prкоманду денді :

paste file1 file2 | pr -t -e24

"-E24" - це "зупинка вкладки розширення на 24 пробіли". На щастя, pasteставить символ-вкладку між стовпцями, щоб prможна було його розширити. Я вибрав 24, порахувавши символів у «Рекурсивно перелічувальних» та додавши 2.


Дякую! Що означає "розширення вкладки на 24 пробіли"?
Тім

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

Традиційно "табло" потрапляють кожні 8 пробілів. "123TABabc" буде надруковано символом "a" 8 ширин символів з початку рядка. Якщо встановити його на 24, то з початку початку рядка слід поставити «a» на ширину 24 знаків.
Брюс Едігер

Ви кажете, що "-e24" є "розширення вкладки зупиняється на 24 пробіли" , то чому б не використовувати expandкоманду безпосередньо paste file1 file2 | expand -t 24:?
WhiteWinterWolf

1
@Masi - моя відповідь схожа, але менш складна, що відповідь @ techno нижче. Він не викликає, sedтому існує один процес, який не запускається. prЯ думаю, він використовує давню команду, датуючи дні Unix SysV, я думаю, тому вона може існувати в більшій кількості встановлень, ніж expand. Це просто стара школа, словом.
Брюс Едігер

9

Оновлення : Тут є набагато простіший сценарій (той, що знаходиться в кінці питання) для табличного виводу. Просто передайте ім'я файлу, як ви хотіли paste... Він використовує htmlдля створення кадру, щоб він був налаштованим. Він зберігає декілька пробілів, а вирівнювання стовпців зберігається, коли він стикається з символами unicode. Однак те, як редактор чи глядач надає унікод - це зовсім інша справа ...

┌──────────────────────┬────────────────┬──────────┬────────────────────────────┐
│ Languages            │ Minimal        │ Chomsky  │ Unrestricted               │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Recursive            │ Turing machine │ Finite   │     space indented         │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ Regular              │ Grammars       │          │ ➀ unicode may render oddly │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│ 1 2  3   4    spaces │                │ Symbol-& │ but the column count is ok │
├──────────────────────┼────────────────┼──────────┼────────────────────────────┤
│                      │                │          │ Context                    │
└──────────────────────┴────────────────┴──────────┴────────────────────────────┘

#!/bin/bash
{ echo -e "<html>\n<table border=1 cellpadding=0 cellspacing=0>"
  paste "$@" |sed -re 's#(.*)#\x09\1\x09#' -e 's#\x09# </pre></td>\n<td><pre> #g' -e 's#^ </pre></td>#<tr>#' -e 's#\n<td><pre> $#\n</tr>#'
  echo -e "</table>\n</html>"
} |w3m -dump -T 'text/html'

---

Короткий огляд інструментів, представлених у відповідях (поки що).
Я досить пильно дивився на них; ось що я знайшов:

paste# Цей інструмент є загальним для всіх відповідей, представлених до цього часу. Він може обробляти декілька файлів; тому кілька колонок ... Добре! # Розмежовує кожен стовпчик за допомогою вкладки ... Добре. # Його вихід не відображається в таблиці.

Усі інструменти нижче всіх видаляють цей роздільник! ... Погано, якщо вам потрібен роздільник.

column # Він видаляє роздільник обмеження Tab, тому ідентифікатор поля є суто колонками, з якими, здається, обробляється непогано. Я нічого страшного не помітив ... # Окрім відсутності унікального роздільника, він працює чудово!

expand # Встановлено лише одну вкладку, тому вона непередбачувана за межами 2 стовпців. Вирівнювання стовпців не є точним при обробці unicode, і воно видаляє роздільник Tab, тому ідентифікація поля суто вирівнюванням стовпців

pr# Є лише одна настройка вкладок, тому вона непередбачувана за межами 2 стовпців. # Вирівнювання стовпців не є точним при обробці unicode, і воно видаляє роздільник обмежень Tab, тому ідентифікація поля суто вирівнюванням стовпців

Для мене columnце очевидний найкращий солютон як однолінійний .. Ви хочете або роздільник, або ASCII-графічне вкладення ваших файлів, читайте далі, інакше .. columnsдосить гарно:) ...


Ось сценарій, який приймає будь-яку кількість файлів і створює ASCII-таблицю, представлену в табличному форматі .. (Майте на увазі, що unicode може не відображати очікувану ширину, наприклад. ௵, який є одним символом. числа неправильні, як це відбувається у деяких згаданих вище утилітах.) ... Вихід сценарію, показаний нижче, складається з 4 вхідних файлів, названих F1 F2 F3 F4 ...

+------------------------+-------------------+-------------------+--------------+
| Languages              | Minimal automaton | Chomsky hierarchy | Grammars     |
| Recursively enumerable | Turing machine    | Type-0            | Unrestricted |
| Regular                | Finite            | —                 |              |
| Alphabet               |                   | Symbol            |              |
|                        |                   |                   | Context      |
+------------------------+-------------------+-------------------+--------------+

#!/bin/bash

# Note: The next line is for testing purposes only!
set F1 F2 F3 F4 # Simulate commandline filename args $1 $2 etc...

p=' '                                # The pad character
# Get line and column stats
cc=${#@}; lmax=                      # Count of columns (== input files)
for c in $(seq 1 $cc) ;do            # Filenames from the commandline 
  F[$c]="${!c}"        
  wc=($(wc -l -L <${F[$c]}))         # File length and width of longest line 
  l[$c]=${wc[0]}                     # File length  (per file)
  L[$c]=${wc[1]}                     # Longest line (per file) 
  ((lmax<${l[$c]})) && lmax=${l[$c]} # Length of longest file
done
# Determine line-count deficits  of shorter files
for c in $(seq 1 $cc) ;do  
  ((${l[$c]}<lmax)) && D[$c]=$((lmax-${l[$c]})) || D[$c]=0 
done
# Build '\n' strings to cater for short-file deficits
for c in $(seq 1 $cc) ;do
  for n in $(seq 1 ${D[$c]}) ;do
    N[$c]=${N[$c]}$'\n'
  done
done
# Build the command to suit the number of input files
source=$(mktemp)
>"$source" echo 'paste \'
for c in $(seq 1 $cc) ;do
    ((${L[$c]}==0)) && e="x" || e=":a -e \"s/^.{0,$((${L[$c]}-1))}$/&$p/;ta\""
    >>"$source" echo '<(sed -re '"$e"' <(cat "${F['$c']}"; echo -n "${N['$c']}")) \'
done
# include the ASCII-art Table framework
>>"$source" echo ' | sed  -e "s/.*/| & |/" -e "s/\t/ | /g" \'   # Add vertical frame lines
>>"$source" echo ' | sed -re "1 {h;s/[^|]/-/g;s/\|/+/g;p;g}" \' # Add top and botom frame lines 
>>"$source" echo '        -e "$ {p;s/[^|]/-/g;s/\|/+/g}"'
>>"$source" echo  
# Run the code
source "$source"
rm     "$source"
exit

Ось моя оригінальна відповідь (трохи оброблена замість вищезазначеного сценарію)

Використовуючи wcдля отримання ширини стовпця та sedправої панелі з видимим символом .(лише для цього прикладу) ... а потім pasteдля з'єднання двох стовпців із табличкою Tab ...

paste <(sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1) F2

# output (No trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine
Regular...............  Finite

Якщо ви хочете зафіксувати правий стовпець:

paste <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F1)-1))"'}$/&./;ta' F1 ) \
      <( sed -re :a -e 's/^.{1,'"$(($(wc -L <F2)-1))"'}$/&./;ta' F2 )  

# output (With trailing whitespace)
Languages.............  Minimal automaton
Recursively enumerable  Turing machine...
Regular...............  Finite...........

Дякую! Ви зробили досить багато роботи. Це дивовижно.
Тім

5

Ти майже там. pasteставить символ кожного вкладки між кожним стовпцем, тому все, що вам потрібно зробити, це розширити вкладки. (Я припускаю, що ваші файли не містять вкладок.) Вам потрібно визначити ширину лівого стовпця. З (досить недавніми) утилітами GNU wc -Lпоказує довжину найдовшої лінії. В інших системах зробіть перший пропуск з awk. +1Це кількість порожнього простору , яке ви хочете між колонами.

paste left.txt right.txt | expand -t $(($(wc -L <left.txt) + 1))
paste left.txt right.txt | expand -t $(awk 'n<length {n=length} END {print n+1}')

Якщо у вас є утиліта стовпців BSD, ви можете використовувати її для визначення ширини стовпців та розширення вкладок за один раз. ( є буквальним символом вкладки; під bash / ksh / zsh ви можете використовувати $'\t'замість цього, і в будь-якій оболонці, яку ви можете використовувати "$(printf '\t')".)

paste left.txt right.txt | column -s '␉' -t

У моїй версії wcкоманда повинна бути: wc -L <left.txt... тому що, коли ім'я файлу вказується як аргумент командного рядка , його ім'я виводиться в stdout
Peter.O

4

Це багатоетапність, тому це неоптимально, але тут ідеться.

1) Знайдіть довжину найдовшого рядка в file1.txt.

while read line
do
echo ${#line}
done < file1.txt | sort -n | tail -1

З вашого прикладу найдовший рядок - 22.

2) Використовуйте awk для прокладки file1.txt, додаючи кожний рядок менше 22 символів до 22 із printfзаявою.

awk 'FS="---" {printf "%-22s\n", $1}' < file1.txt > file1-pad.txt

Примітка. Для FS використовуйте рядок, який не існує в file1.txt.

3) Використовуйте пасту, як ви робили раніше.

$ paste file1-pad.txt file2.txt
Languages               Minimal automaton
Recursively enumerable  Turing machine
Regular                 Finite

Якщо це ви робите часто, це легко перетворити на сценарій.


У вашому коді, щоб знайти найдовший рядок, вам знадобиться while IFS= read -r line, інакше оболонка буде маніпулювати пробілом та зворотними косами. Але оболонка - не найкращий інструмент для цієї роботи; останні версії GNU coreutils мають wc -L(див. відповідь Фреда), або ви можете використовувати awk : awk 'n<length {n=length} END {print +n}'.
Жил 'ТАК - перестань бути злим'

4

Я не можу коментувати відповідь Глена Джекмана, тому додаю це, щоб вирішити проблему порожніх комірок, яку зазначив Петер.О. Додавання нульового знака перед кожною вкладкою усуває пробіли роздільників, які трактуються як один розрив, і вирішує проблему. (Я спочатку використовував пробіли, але використання нульових знаків усуває зайвий пробіл між стовпцями.)

paste file1 file2 | sed 's/\t/\0\t/g' | column -s $'\t' -t

Якщо нульовий знак викликає проблеми з різних причин, спробуйте будь-яку:

paste file1 file2 | sed 's/\t/ \t/g' | column -s $'\t' -t

або

paste file1 file2 | sed $'s/\t/ \t/g' | column -s $'\t' -t

І те, sedі columnінше виглядає різним у застосуванні для різних смаків та версій Unix / Linux, особливо BSD (і Mac OS X) проти GNU / Linux.


Здається, що команда sed нічого не робить. Я замінюю команду стовпця на od -cі не бачу жодних нульових байтів. Це на centos та ubuntu.
glenn jackman

1
Це працювало для мене в RedHat EL4. І sed, і колонка, здається, змінюються в часі та системі. В Ubuntu 14.4 використання \0не працювало як nullsed, але все \x0ж. Однак тоді стовпчик дав line too longпомилку. Найпростіше, здається, використовувати простір і жити з додатковим персонажем.
техно

0

Спираючись на відповідь bahamat : це можна зробити повністю awk, читаючи файли лише один раз і не створюючи жодних тимчасових файлів. Щоб вирішити проблему, як зазначено, зробіть

awk '
        NR==FNR { if (length > max_length) max_length = length
                  max_FNR = FNR
                  save[FNR] = $0
                  next
                }
                { printf "%-*s", max_length+2, save[FNR]
                  print
                }
        END     { if (FNR < max_FNR) {
                        for (i=FNR+1; i <= max_FNR; i++) print save[i]
                  }
                }
    '   file1 file2

Як і у багатьох awkсценаріях цього ілака, вищезгаданий перший читається file1, зберігаючи всі дані в saveмасиві і одночасно обчислюючи максимальну довжину рядка. Потім він зчитує file2 та друкує збережені ( file1) дані поряд з поточними ( file2) даними. Нарешті, якщо file1довше file2(має більше рядків), ми друкуємо останні кілька рядків file1 (ті, для яких у другому стовпці немає відповідного рядка).

Щодо printfформату:

  • "%-nns"друкує рядок, виправданий ліворуч у nnсимволах поля .
  • "%-*s", nnробить те ж саме - *каже йому взяти ширину поля з наступного параметра.
  • Використовуючи для , ми отримуємо два пробіли між стовпцями. Очевидно, що можна регулювати.maxlength+2nn+2

Наведений вище сценарій працює лише для двох файлів. Це може бути тривіально модифіковано для обробки трьох файлів або для обробки чотирьох файлів тощо, але це буде нудно і залишається як вправа. Однак, виявляється, не має бути важко змінити його , щоб обробити будь-яку кількість з файлів:

awk '
        FNR==1  { file_num++ }
                { if (length > max_length[file_num]) max_length[file_num] = length
                  max_FNR[file_num] = FNR
                  save[file_num,FNR] = $0
                }
        END     { for (j=1; j<=file_num; j++) {
                        if (max_FNR[j] > global_max_FNR) global_max_FNR = max_FNR[j]
                  }
                  for (i=1; i<=global_max_FNR; i++) {
                        for (j=1; j<file_num; j++) printf "%-*s", max_length[j]+2, save[j,i]
                        print save[file_num,i]
                  }
                }
    '   file*

Це дуже схоже на мій перший сценарій, за винятком

  • Він перетворюється max_lengthна масив.
  • Він перетворюється max_FNRна масив.
  • Він перетворюється saveна двовимірний масив.
  • Він читає всі файли, зберігаючи весь вміст. Потім він виписує весь вихід з ENDблоку.

Я знаю, що це питання давнє; Я просто наткнувся на це. Я згоден, що pasteце найкраще рішення; конкретно, Глена Джекмана paste file1 file2 | column -s $'\t' -t. Але я подумав, що було б цікаво спробувати покращити awkпідхід.
G-Man каже: «Відновіть Моніку»
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.