Lệnh dán tốt hơn


11

Tôi có hai tệp sau (Tôi đã đệm các dòng bằng dấu chấm để mỗi dòng trong tệp có cùng chiều rộng và tạo tệp1 tất cả các chữ hoa để làm cho rõ hơn).

contents of file1:

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

contents of file2

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

Lưu ý rằng file2 dài hơn file1.

Khi tôi chạy lệnh này:

paste file1 file2

Tôi nhận được đầu ra này

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

Tôi có thể làm gì để đầu ra như sau?

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

Tôi đã thử

paste file1 file2 | column -t

nhưng nó làm điều này:

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

không xấu như đầu ra ban đầu nhưng dù sao cũng sai cột.


2
pasteđang sử dụng các tab ở phía trước các dòng từ tệp thứ hai. Bạn có thể phải sử dụng một bộ xử lý sau để căn chỉnh các cột một cách thích hợp.
unxnut

3
paste file1 file2 | column -tn?
ninjalj

file1 luôn có các cột có kích thước cố định?
RSFalcon7

@ RSFalcon7 Vâng, đúng vậy.
Tulains Córdova

Câu trả lời:


17

Giả sử bạn không có bất kỳ ký tự tab nào trong tệp của mình,

paste file1 file2 | expand -t 13

với đối số -tđược chọn phù hợp để bao phủ độ rộng dòng tối đa mong muốn trong tệp1.

OP đã thêm một giải pháp linh hoạt hơn:

Tôi đã làm điều này để nó hoạt động mà không có phép thuật số 13:

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

Nó không dễ gõ nhưng có thể được sử dụng trong một tập lệnh.


đẹp! Tôi không biết về việc mở rộng trước khi đọc câu trả lời của bạn :)
TabeaKischka

4

Tôi nghĩ awk có thể làm điều đó một cách độc đáo, vì vậy tôi đã googled "awk đọc đầu vào từ hai tệp" và tìm thấy một bài viết về stackoverflow để sử dụng làm điểm bắt đầu.

Đầu tiên là phiên bản cô đọng, sau đó nhận xét đầy đủ bên dưới đó. Điều này mất hơn một vài phút để làm việc. Tôi rất vui vì một số tinh chỉnh từ những người thông minh hơn.

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

Và đây là phiên bản tài liệu đầy đủ của ở trên.

# 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 đây là câu trả lời duy nhất hoạt động với đầu vào tùy ý (nghĩa là với các dòng có thể chứa các tab). Tôi không nghĩ rằng điều này có thể được cải thiện / cải thiện đáng kể.
don_crissti

2

Không phải là một giải pháp tốt nhưng tôi đã có thể thực hiện bằng cách sử dụng

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

trong đó TAB được thay thế bằng ký tự tab.


Vai trò của &&lệnh sed là gì?
coffeMug

1
Một đơn &đặt những gì đang được tìm kiếm (một tab trong trường hợp này). Lệnh này chỉ đơn giản là thay thế tab ở đầu bằng hai tab.
unxnut

Tôi đã phải thay đổi TABđể \tlàm cho công việc này trong zsh trên Ubuntu debian. Và nó chỉ hoạt động nếu file1 có ít hơn 15 ký tự
rubo77

2

Trên Debian và các dẫn xuất, columncó một tùy chọn -n nomege cho phép cột thực hiện đúng với các trường trống. Trong nội bộ, columnsử dụng wcstok(wcs, delim, ptr)hàm, phân tách một chuỗi ký tự rộng thành các thẻ được phân tách bằng các ký tự rộng trong delimđối số.

wcstokbắt đầu bằng cách bỏ qua các ký tự rộng delim, trước khi nhận ra mã thông báo. Các -ntùy chọn sử dụng một algorythm mà không bỏ ban đầu rộng nhân vật trong delim.

Thật không may, điều này không dễ mang theo: -nđặc trưng cho Debian và columnkhông có trong POSIX, đây rõ ràng là một điều BSD.


2

Lấy ra các chấm mà bạn đã sử dụng để đệm:

tập tin1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

tập tin 2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Thử đi:

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

Và bạn sẽ nhận được:

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

Điều này, giống như các giải pháp khác sử dụng pastesẽ không in được đầu ra thích hợp nếu có bất kỳ dòng nào chứa các tab. +1 vì sự khác biệt mặc dù
don_crissti

+1. Bạn vui lòng giải thích làm thế nào các giải pháp hoạt động?
Tulains Córdova

1

Một awkgiải pháp khá di động và nên hoạt động với số lượng tệp đầu vào tùy ý:

# 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
    }
}

Làm thế nào để bạn sử dụng điều này trên file1 và file2? Tôi gọi kịch bản paste-awkvà thử paste file1 file2|paste-awkvà tôi đã thử awk paste-awk file1 file2nhưng không có gì hiệu quả.
rubo77

Tôi nhận đượcawk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77

@ rubo77: awk -f paste-awk file1 file2nên hoạt động, ít nhất là cho GNU awk và mawk.
ninjalj

Điều này hoạt động, mặc dù nó hơi khác nhau vì pastecó ít không gian giữa hai hàng. Và nếu tệp đầu vào không có tất cả các hàng có cùng độ dài, nó sẽ dẫn đến một hàng bên phải
rubo77

@ rubo77: có thể đặt dải phân cách trường bằng-F\\t
ninjalj
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.