Лучшая команда вставки


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?
ниндзя

всегда ли file1 имеет столбцы фиксированного размера?
RSFalcon7

@ RSFalcon7 Да, это так.
Тулаинс Кордова

Ответы:


17

Предполагая, что в ваших файлах нет символов табуляции,

paste file1 file2 | expand -t 13

с -tсоответствующим образом выбранным аргументом arg, чтобы покрыть желаемую максимальную ширину линии в файле1.

OP добавил более гибкое решение:

Я сделал это, чтобы он работал без магического числа 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 на Debian Ubuntu. И это работает, только если file1 имеет менее 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 и mawk.
ниндзя

Это работает, хотя немного отличается от того, pasteчто между двумя рядами меньше места. И если входной файл имеет не все строки одинаковой длины, это приведет к выравниванию по правой строке
rubo77

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