Daha iyi bir yapıştırma komutu


11

Aşağıdaki iki dosya var (Ben bir dosyadaki her satır aynı genişlik ve daha net hale getirmek için file1 tüm kapaklar yapılan noktalarla çizgiler yastıklı).

contents of file1:

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

contents of file2

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

Dosya2'nin dosya1'den daha uzun olduğuna dikkat edin.

Bu komutu çalıştırdığımda:

paste file1 file2

Bu çıktıyı alıyorum

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

Çıktının aşağıdaki gibi olması için ne yapabilirim?

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

denedim

paste file1 file2 | column -t

ama bunu yapar:

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

orijinal çıktı kadar çirkin olmayan ama yine de yanlış sütun-bilge.


2
pasteikinci dosyadaki satırların önünde sekmeler kullanıyor. Sütunları uygun şekilde hizalamak için bir son işlemci kullanmanız gerekebilir.
unxnut

3
paste file1 file2 | column -tn?
ninjalj

dosya1 her zaman sabit boyutlu sütunlara sahip mi?
RSFalcon7

@ RSFalcon7 Evet, var.
Tulains Córdova

Yanıtlar:


17

Dosyalarınızda sekme karakteri bulunmadığını varsayarsak,

paste file1 file2 | expand -t 13

arg, dosya1'de -tistenen maksimum çizgi genişliğini kapsayacak şekilde uygun şekilde seçilir.

OP daha esnek bir çözüm ekledi:

Bunu büyü numarası 13 olmadan çalıştığından yaptım:

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

Yazması kolay değil, bir komut dosyasında kullanılabilir.


Güzel! Cevabınızı okumadan önce genişlemeyi bilmiyordum :)
TabeaKischka

4

Ben awk güzel yapabileceğini düşündüm, bu yüzden "iki dosyadan awk okuma girişi" googled ve bir başlangıç ​​noktası olarak kullanmak için stackoverflow hakkında bir makale bulundu .

Birincisi, yoğunlaştırılmış versiyon, sonra bunun altında tam olarak yorum yaptı. Bunun çalışması birkaç dakikadan fazla sürdü. Daha akıllı insanlar tarafından yapılan bazı geliştirmelerden memnun olurum.

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

Ve işte yukarıdakilerin tam olarak belgelenmiş versiyonu.

# 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, rasgele girdi ile (sekmeler içerebilen satırlarla) çalışan tek yanıttır. Bunun önemli ölçüde rafine / iyileştirilebileceğini düşünmüyorum.
don_crissti

2

Çok iyi bir çözüm değil ama bunu kullanarak başardım

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

burada SEKME sekme karakteri ile değiştirilir.


&&Sed komutunda rolü nedir ?
Kahve

1
Tek bir &öğe ne aranacağını koyar (bu durumda bir sekme). Bu komut, başlangıçtaki sekmeyi iki sekmeyle değiştirir.
unxnut

Ben Ubuntu debian üzerinde zsh bu işi yapmak için değiştirmek TABzorunda \tkaldı. Ve sadece dosya1 15 karakterden daha azsa çalışır
rubo77

2

Debian ve türevlerinde, sütunun boş alanlarla doğru şeyi yapmasına izin veren columnbir -n adaylama seçeneği vardır. Dahili olarak, geniş bir karakter dizesini bağımsız değişkendeki geniş karakterler tarafından ayrılmış belirteçlere ayıran işlevi columnkullanır .wcstok(wcs, delim, ptr)delim

wcstokdelimbelirteci tanımadan önce geniş karakterleri atlayarak başlar . Bu -nseçenek, başlangıçtaki geniş karakterleri atlamayan bir algoritma kullanır delim.

Ne yazık ki, bu çok taşınabilir değil: -nDebian'a özgü ve columnPOSIX'te değil, görünüşe göre bir BSD şey.


2

Dolgu için kullandığınız noktaları çıkarma:

file1:

ETIAM
SED
MAECENAS
DONEC
SUSPENDISSE

dosya2:

Lorem
Proin
Nunc
Quisque
Aenean
Nam
Vivamus
Curabitur
Nullam

Bunu dene:

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

Ve alacaksınız:

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

Bu, diğer çözümler gibi pastesekmeler içeren herhangi bir satır varsa doğru çıktıyı basamayacaktır. Farklı olmak için +1
don_crissti

+1. Çözümün nasıl çalıştığını açıklar mısınız?
Tulains Córdova

1

awkOldukça taşınabilir olması ve rastgele sayıda girdi dosyası için çalışması gereken bir çözüm:

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

Bunu dosya1 ve dosya2'de nasıl kullanıyorsunuz? Senaryoyu aradım paste-awkve denedim paste file1 file2|paste-awkve denedim awk paste-awk file1 file2ama hiçbiri işe yaramadı.
rubo77

Anladımawk: Line:1: (FILENAME=file1 FNR=1) Fatal: Division by zero
rubo77

@ rubo77: awk -f paste-awk file1 file2en azından GNU awk ve mawk için çalışmalı.
ninjalj

Bu paste, iki sıra arasında daha az boşluk olmasına rağmen biraz farklı olmasına rağmen çalışır . Ve giriş dosyasının tüm satırları aynı uzunlukta değilse, sağa hizala satırla sonuçlanır
rubo77 14

@ rubo77: alan ayırıcı ile ayarlanabilir-F\\t
ninjalj
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.