คำสั่งวางที่ดีกว่า


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

ด้วย arg เพื่อ-tเลือกอย่างเหมาะสมเพื่อครอบคลุมความกว้างบรรทัดสูงสุดที่ต้องการใน file1

OP ได้เพิ่มโซลูชันที่ยืดหยุ่นมากขึ้น:

ฉันทำสิ่งนี้เพื่อให้ทำงานได้โดยไม่ต้องใช้เวทย์มนตร์หมายเลข 13:

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

มันไม่ใช่เรื่องง่ายที่จะพิมพ์ แต่สามารถใช้ในสคริปต์


ดี! ผมไม่ทราบว่าเกี่ยวกับการขยายตัวก่อนที่ผมจะอ่านคำตอบของคุณ :)
TabeaKischka

4

ฉันคิดว่า awk อาจทำได้เป็นอย่างดีดังนั้นฉันจึง googled "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ตัวเลือกใช้ algorythm delimที่ไม่ข้ามตัวอักษรกว้างในเบื้องต้น

น่าเสียดายที่นี่ไม่ใช่แบบพกพามาก: -nเป็นเฉพาะเดเบียนและ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 คุณช่วยอธิบายวิธีการแก้ปัญหาได้อย่างไร
Tulains Córdova

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
ninjalj

วิธีนี้ใช้ได้ผลแม้ว่าจะแตกต่างกันเล็กน้อยจากที่pasteนั่นมีพื้นที่น้อยกว่าระหว่างสองแถว และหากไฟล์อินพุตไม่ได้มีความยาวเท่ากันทุกแถวก็จะส่งผลให้แถวเรียงขวา
rubo77

@ rubo77: ตัวคั่นฟิลด์สามารถตั้งค่าด้วย-F\\t
ninjalj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.