Краща команда вставки


11

У мене є наступні два файли (я додав рядки крапками, тому кожен рядок у файлі однакової ширини, і зробив file1 усі літери, щоб зробити це більш зрозумілим).

contents of file1:

ETIAM......
SED........
MAECENAS...
DONEC......
SUSPENDISSE

contents of file2

Lorem....
Proin....
Nunc.....
Quisque..
Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

Зауважте, що file2 довший, ніж file1.

Коли я запускаю цю команду:

paste file1 file2

Я отримую цей вихід

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
    Nam......
    Vivamus..
    Curabitur
    Nullam...

Що я можу зробити, щоб результат був таким?

ETIAM...... Lorem....
SED........ Proin....
MAECENAS... Nunc.....
DONEC...... Quisque..
SUSPENDISSE Aenean...
            Nam......
            Vivamus..
            Curabitur
            Nullam...

я намагався

paste file1 file2 | column -t

але це робиться так:

ETIAM......  Lorem....
SED........  Proin....
MAECENAS...  Nunc.....
DONEC......  Quisque..
SUSPENDISSE  Aenean...
Nam......
Vivamus..
Curabitur
Nullam...

не такий некрасивий, як оригінальний вихід, але неправильно стовпчик.


2
pasteвикористовується вкладки перед рядками з другого файлу. Можливо, вам доведеться використовувати постпроцесор для відповідного вирівнювання стовпців.
unxnut

3
paste file1 file2 | column -tn?
ninjalj

чи завжди у file1 є стовпці фіксованого розміру?
RSFalcon7

@ RSFalcon7 Так, це так.
Tulains Córdova

Відповіді:


17

Припустимо, що у ваших файлах немає жодних символів,

paste file1 file2 | expand -t 13

з аргументом, -tвідповідним чином обраним для покриття бажаної максимальної ширини рядка у файлі1.

ОП додало гнучкіше рішення:

Я зробив це, щоб це працювало без магічного числа 13:

paste file1 file2 | expand -t $(( $(wc -L <file1) + 2 ))

Ввести його непросто, але його можна використовувати в сценарії.


приємно! Я не знав про розширення, перш ніж прочитав вашу відповідь :)
TabeaKischka

4

Я подумав, що awk може це зробити красиво, тому я гуглів "awk читанням входу з двох файлів" і знайшов статтю про stackoverflow, яку слід використовувати як вихідну точку.

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

awk '{if(length($0)>max)max=length($0)}
FNR==NR{s1[FNR]=$0;next}{s2[FNR]=$0}
END { format = "%-" max "s\t%-" max "s\n";
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) { printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:"" }
}' file1 file2

І ось повністю задокументована версія сказаного.

# 2013-11-05 mike@diehn.net
# Invoke thus:
#   awk -f this_file file1 file2
# The result is what you asked for and the columns will be
# determined by input file order.
#----------------------------------------------------------
# No matter which file we're reading,
# keep track of max line length for use
# in the printf format.
#
{ if ( length($0) > max ) max=length($0) }

# FNR is record number in current file
# NR is record number over all
# while they are equal, we're reading the first file
#   and we load the strings into array "s1"
#   and then go to the "next" line in the file we're reading.
FNR==NR { s1[FNR]=$0; next }

# and when they aren't, we're reading the
#   second file and we put the strings into
#   array s2
{s2[FNR]=$0}

# At the end, after all lines from both files have
# been read,
END {
  # use the max line length to create a printf format
  # the right widths
  format = "%-" max "s\t%-" max "s\n"
  # and figure the number of array elements we need
  # to cycle through in a for loop.
  numlines=(NR-FNR)>FNR?NR-FNR:FNR;
  for (i=1; i<=numlines; i++) {
     printf format, s1[i]?s1[i]:"", s2[i]?s2[i]:""
  }
}

1
+1 - це єдина відповідь, яка працює з довільним введенням (тобто з рядками, які можуть містити вкладки). Я не думаю, що це можна значно вдосконалити / покращити.
don_crissti

2

Не дуже вдале рішення, але мені вдалося це зробити, використовуючи

paste file1 file2 | sed 's/^TAB/&&/'

де TAB замінено символом вкладки.


Яка роль &&у команді sed?
coffeMug

1
Сингл &ставить те, що шукається (вкладка в цьому випадку). Ця команда просто замінює вкладку на початку двома вкладками.
unxnut

Мені довелося змінити, TABщоб \tзробити цю роботу в zsh на Ubuntu debian. І це працює лише в тому випадку, якщо файл1 має менше 15 символів
rubo77

2

У Debian і похідних columnє опція -n nomerge, яка дозволяє стовпцю робити правильну справу з порожніми полями. Внутрішньо columnвикористовує wcstok(wcs, delim, ptr)функцію, яка розбиває широкий рядок символів на лексеми, розмежовані широкими символами delimаргументу.

wcstokпочинається з пропускання широких символів delim, перш ніж розпізнати маркер. -nОпція використовує алгоритм , який не пропустити початкову крислатий символи delim.

На жаль, це не дуже портативно: -nспецифічно для Debian і columnнемає в POSIX, мабуть, це BSD.


2

Витягнення крапок, які ви використовували для оббивки:

file1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

file2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Спробуйте це:

$ ( echo ".TS"; echo "l l."; paste file1 file2; echo ".TE" ) | tbl | nroff | more

І ви отримаєте:

ETIAM         Lorem
SED           Proin
MAECENAS      Nunc
DONEC         Quisque
SUSPENDISSE   Aenean
              Nam
              Vivamus
              Curabitur
              Nullam

Це, як і інші рішення, що використовують paste, не зможе надрукувати належний вихід, якщо є будь-які рядки, що містять вкладки. +1 за те, що він інший
don_crissti

+1. Скажіть, будь ласка, як працює рішення?
Тулен Кордова

1

awkРішення , яке повинно бути досить портативним, і повинні працювати для будь-якого числа вхідних файлів:

# Invoke thus:
#   awk -F\\t -f this_file file1 file2

# every time we read a new file, FNR goes to 1

FNR==1 {
    curfile++                       # current file
}

# read all files and save all the info we'll need
{
    column[curfile,FNR]=$0          # save current line
    nlines[curfile]++               # number of lines in current file
    if (length > len[curfile])
            len[curfile] = length   # max line length in current file
}

# finally, show the lines from all files side by side, as a table
END {
    # iterate through lines until there are no more lines in any file
    for (line = 1; !end; line++) {
            $0 = _
            end = 1

            # iterate through all files, we cannot use
            #   for (file in nlines) because arrays are unordered
            for (file=1; file <= curfile; file++) {
                    # columnate corresponding line from each file
                    $0 = $0 sprintf("%*s" FS, len[file], column[file,line])
                    # at least some file had a corresponding line
                    if (nlines[file] >= line)
                            end = 0
            }

            # don't print a trailing empty line
            if (!end)
                    print
    }
}

Як ви користуєтеся цим у file1 та file2? Я подзвонив сценарій paste-awkі спробував, paste file1 file2|paste-awkі я спробував, awk paste-awk file1 file2але жоден не спрацював.
rubo77

Я отримуюawk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77

@ rubo77: awk -f paste-awk file1 file2має працювати, принаймні, для GNU awk and mawk.
ніндзя

Це працює, хоча воно дещо відрізняється від pasteтого, що між двома рядами менше місця. І якщо вхідний файл має не всі рядки однакової довжини, це призведе до вирівнювання праворуч
rubo77

@ rubo77: роздільник поля можна встановити за допомогою-F\\t
ninjalj
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.