Perintah tempel yang lebih baik


11

Saya memiliki dua file berikut (saya mengisi garis dengan titik-titik sehingga setiap baris dalam file memiliki lebar yang sama dan membuat file1 semua huruf besar agar lebih jelas).

contents of file1:

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

contents of file2

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

Perhatikan bahwa file2 lebih panjang dari file1.

Ketika saya menjalankan perintah ini:

paste file1 file2

Saya mendapatkan hasil ini

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

Apa yang bisa saya lakukan untuk output sebagai berikut?

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

Saya mencoba

paste file1 file2 | column -t

tetapi ia melakukan ini:

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

tidak sebagus output asli tetapi salah kolom bijaksana.


2
pastemenggunakan tab di depan baris dari file kedua. Anda mungkin harus menggunakan postprocessor untuk menyelaraskan kolom dengan tepat.
unxnut

3
paste file1 file2 | column -tn?
ninjalj

apakah file1 selalu memiliki kolom ukuran tetap?
RSFalcon7

@ RSFalcon7 Ya, benar.
Tulains Córdova

Jawaban:


17

Dengan asumsi Anda tidak memiliki karakter tab di file Anda,

paste file1 file2 | expand -t 13

dengan arg yang -tsesuai dipilih untuk menutupi lebar garis maks yang diinginkan dalam file1.

OP telah menambahkan solusi yang lebih fleksibel:

Saya melakukan ini sehingga berfungsi tanpa angka ajaib 13:

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

Tidak mudah untuk mengetik tetapi bisa digunakan dalam skrip.


bagus! Saya tidak tahu tentang ekspansi sebelum saya membaca jawaban Anda :)
TabeaKischka

4

Saya pikir awk mungkin melakukannya dengan baik, jadi saya mencari "awk reading input dari dua file" di Google dan menemukan artikel tentang stackoverflow untuk digunakan sebagai titik awal.

Pertama adalah versi kental, kemudian berkomentar sepenuhnya di bawah itu. Ini membutuhkan waktu lebih dari beberapa menit untuk menyelesaikannya. Saya akan senang dengan beberapa perbaikan dari orang-orang pintar.

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

Dan di sini adalah versi yang sepenuhnya didokumentasikan di atas.

# 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 ini adalah satu-satunya jawaban yang berfungsi dengan input sewenang-wenang (yaitu dengan baris yang mungkin berisi tab). Saya tidak berpikir ini bisa diperbaiki / ditingkatkan secara signifikan.
don_crissti

2

Bukan solusi yang sangat bagus tapi saya bisa melakukannya dengan menggunakan

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

di mana TAB diganti dengan karakter tab.


Apa peran &&dalam perintah sed?
coffeMug

1
Satu &menempatkan apa yang sedang dicari (tab dalam kasus ini). Perintah ini hanya mengganti tab di awal dengan dua tab.
unxnut

Saya harus mengubah TABuntuk \tmembuat ini berfungsi di zsh di Ubuntu debian. Dan itu hanya berfungsi jika file1 memiliki kurang dari 15 karakter
rubo77

2

Pada Debian dan turunannya, columnmemiliki opsi -n nomerge yang memungkinkan kolom untuk melakukan hal yang benar dengan bidang kosong. Secara internal, columnmenggunakan wcstok(wcs, delim, ptr)fungsi, yang membagi string karakter lebar menjadi token dibatasi oleh karakter lebar dalam delimargumen.

wcstokdimulai dengan melewatkan karakter lebar delim, sebelum mengenali token. The -npilihan menggunakan algorythm yang tidak melewatkan awal lebar-karakter dalam delim.

Sayangnya, ini tidak terlalu portabel: -nspesifik untuk Debian, dan columnbukan di POSIX, ini sepertinya hal yang BSD.


2

Mengambil titik-titik yang Anda gunakan untuk mengisi:

file1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

file2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Coba ini:

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

Dan Anda akan mendapatkan:

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

Ini, seperti solusi lain yang menggunakan pasteakan gagal untuk mencetak output yang tepat jika ada garis yang mengandung tab. +1 karena berbeda
don_crissti

+1. Tolong jelaskan bagaimana solusinya bekerja?
Tulains Córdova

1

Sebuah awksolusi yang harus cukup portabel, dan seharusnya bekerja untuk jumlah sewenang-wenang file masukan:

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

Bagaimana Anda menggunakan ini pada file1 dan file2? Saya menelepon naskah paste-awkdan mencoba paste file1 file2|paste-awkdan saya mencoba awk paste-awk file1 file2tetapi tidak ada yang berhasil.
rubo77

Saya mendapatkanawk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77

@ rubo77: awk -f paste-awk file1 file2harus bekerja, setidaknya untuk awk dan mawk GNU.
ninjalj

Ini bekerja, meskipun sedikit berbeda dari pasteada sedikit ruang antara dua baris. Dan jika file input tidak memiliki semua baris dengan panjang yang sama, itu akan menghasilkan baris align-right
rubo77

@ rubo77: pemisah bidang dapat disetel dengan-F\\t
ninjalj
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.