Komut satırından her iki satırı nasıl birleştirirsiniz?


151

Aşağıdaki biçime sahip bir metin dosyam var. İlk satır "KEY" ve ikinci satır "VALUE".

KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

Anahtar olarak aynı satırda değer gerekir. Yani çıktı böyle görünmeli ...

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

$Veya gibi bir sınırlayıcı kullanabilirsiniz daha iyi olacaktır ,:

KEY 4048:1736 string , 3

İki satırı nasıl birleştiririm?


Bunu yapmanın bir çok yolu var! Ben yaptığım çok az tezgah pr, paste, awk, xargs, sedvepure bash ! ( bashxargs daha yavaş, daha yavaş !)
F. Hauri

Yanıtlar:


182

awk:

awk 'NR%2{printf "%s ",$0;next;}1' yourFile

Not, çıktı sonunda boş bir çizgi var.

sed:

sed 'N;s/\n/ /' yourFile

Renkli çıktı ile çalışmaz. Bu soru-cevap üzerinde her şeyi denedim ve çıktı ansi renkli olduğunda hiçbir şey işe yaramadı. Ubuntu üzerinde test edildi 13.04
Leo Gallucci

1
@elgalu: Çünkü ANSI renkleri sadece bir kaçış karakteri kombinasyonudur. Neye sahip olduğunuzu görmek için böyle bir çıktıya bir hexedit yapın.
not2qubit

7
Bu awk çözümü, printfgibi genişletme dizeleri %sbulunursa kırılabilir $0. Bu başarısızlık şu şekilde önlenebilir:'NR%2{printf "%s ",$0;next;}1'
ghoti

9
Google için gerçekten zor olduğu 1için kapanış ayracı sonrası ne anlama geliyor?
erikbwork


243

paste bu iş için iyidir:

paste -d " "  - - < filename

10
Ne sed ne de awk kullanmasına rağmen sunulan en iyi çözüm olduğunu düşünüyorum. Garip sayıda satır olan girişte, Kent'in awk çözümü son satırsonunu atlar, sed çözümü son satırını bütünüyle atlar ve çözümüm son satırı tekrarlar. pasteÖte yandan, mükemmel davranır. +1.
ghoti

8
Sık kullanıyorum cutama daima unutuyorum paste. Bu sorun için sallanıyor. Stdin'in tüm hatlarını birleştirmem gerekiyordu ve kolayca yaptım paste -sd ' ' -.
Clint Pachl

4
Basit ve güzel!
krlmlr

8
yani -stdin demek, yani stdin paste - -okumak, sonra stdin okumak, beklediğim kadar çoğunu istifleyebilirsiniz.
ThorSummoner

1
Evet, @ThorSummoner ... Her üç satırı tek bir satıra yapıştırmak zorunda kaldım ve - - - yapıştırdım ve mükemmel çalıştı.
Daniel Goldfarb

35

Sed, awk, grep alternatifi:

xargs -n2 -d'\n'

Bu, N satırına katılmak istediğinizde ve yalnızca alanla sınırlı çıktıya ihtiyacınız olduğunda en iyisidir.

Benim asıl cevabım xargs -n2satırlardan ziyade kelimelere ayrılmaktı. -dgirişi herhangi bir tek karaktere bölmek için kullanılabilir.


4
Bu güzel bir yöntem, ama çizgiler üzerinde değil, kelimeler üzerinde çalışıyor. Çizgiler üzerinde çalışması için ekleyebilir-d '\n'
Don Hatch

2
Vay canına, ben normal bir xargskullanıcıyım ama bunu bilmiyordum. Harika bir ipucu.
Sridhar Sarnobat

1
Bunu seviyorum. Çok temiz.
Alexander Guo

28

Bir köpeği öldürmenin asmaktan daha fazla yolu vardır. [1]

awk '{key=$0; getline; print key ", " $0;}'

İstediğiniz sınırlayıcıyı tırnak içine alın.


Referanslar:

  1. Başlangıçta "Kedinin derisini almanın birçok yolu", evcil hayvanlarla hiçbir ilgisi olmayan daha eski ve potansiyel olarak ortaya çıkan bir ifadeye geri döndü.

Bu çözümü seviyorum.
luis.espinal

5
Bir kedi sahibi olarak bu tür mizahı takdir etmiyorum.
witkacy26

4
@ witkacy26, Endişenize göre düzeltilmiş ifade.
ghoti

Bu awk çözümü seviyorum ama nasıl çalıştığını anlamıyorum: S
Rubendob

@Rubendob - awk her girdi satırını okur ve değişkene yerleştirir $0. getlineKomut ayrıca giriş ve içinde bunu yerleştirir "sonraki" satırını yakalar $0. Böylece ilk ifade ilk satırı alır ve print komutu, değişkene kaydedilenleri keyvirgül içeren bir dizeyle ve kullanılarak getirilen satırla birleştirir getline. Daha net? :)
ghoti

12

İşte bash benim çözüm:

while read line1; do read line2; echo "$line1, $line2"; done < data.txt

11

Önceki çözümlerin işe yarayacağı görünse de, belgede tek bir anormallik oluşursa çıktı parçalara ayrılır. Aşağıda biraz daha güvenli.

sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt

3
Neden daha güvenli? Ne yapar /KEY/? pSonunda ne yapar ?
Stewart

/KEY/doğrultusunda arar KEY. pbaskılar dışarı sonuçlanır. daha güvenlidir çünkü işlemi sadece içinde a KEYolan satırlara uygular .
minghua

11

İşte ile başka bir yol awk:

awk 'ORS=NR%2?FS:RS' file

$ cat file
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

$ awk 'ORS=NR%2?FS:RS' file
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

Ed Morton tarafından yorumlarda belirtildiği gibi, güvenlik için parantez ve taşınabilirlik için parens eklemek daha iyidir.

awk '{ ORS = (NR%2 ? FS : RS) } 1' file

ORSÇıkış Kayıt Ayırıcı anlamına gelir. Burada yaptığımız NR, satır numarasını saklayan bir koşulu test etmektir. Modulo NRdeğeri gerçek bir değer (> 0) ise, Çıktı Alanı Ayırıcısını FSvarsayılan olarak boşluk olan (Alan Ayırıcı) değerine ayarlarız , aksi RStakdirde satırsonu olan (Kayıt Ayırıcı) değerini atarız .

,Ayırıcı olarak eklemek isterseniz , aşağıdakileri kullanın:

awk '{ ORS = (NR%2 ? "," : RS) } 1' file

1
Kesinlikle doğru yaklaşım bu yüzden +1 ama merak ediyorum kayıt yazdırma varsayılan eylemini çağırmak için değerlendiriliyor koşul nedir. Görev başarılı oldu mu? Basitçe ORSve trueORS sıfır olmayan bir değer veya boş bir dize aldığından ve sayısal karşılaştırma yerine bir sokma olması gerektiğini doğru tahmin eden awks olduğu için tedavi ediliyor mu? Başka bir şey mi? Gerçekten emin değilim ve bu yüzden yazmış olurdum awk '{ORS=(NR%2?FS:RS)}1' file. Taşınabilirliği sağlamak için üçlü ifadeyi parantez içine aldım.
Ed Morton

1
@EdMorton Evet, bu cevapta birkaç upvotes gördüm, güvenlik için parantezleri içerecek şekilde güncellemek üzereydim. Parens de ekleyeceğiz.
jaypal singh

7

"ex" sed, awk, grep, vb. ile aynı ailede olan bir script yazı editörüdür. Bence aradığınız şey bu olabilir. Birçok modern vi klonu / halefi de bir vi moduna sahiptir.

 ex -c "%g/KEY/j" -c "wq" data.txt

Bu eşleşirse "KEY" Bir gerçekleştirin her hat için diyor j aşağıdaki satırın oin. Bu komut tamamlandıktan sonra (tüm satırlara karşı), w rite ve q uit yazın.


4

Perl bir seçenekse deneyebilirsiniz:

perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt

Does -0söyle perl (kayıt ayırıcı ayarlamak için $/)bizim eşleştirme desende birden fazla satıra böylece null bana bu pratikte ne anlama geldiğini anlamaya yönelik Elyordamsayfalarının biraz fazla teknik vardır..
Sridhar Sarnobat

4

Awk komutunu 2 çift satırı birleştirmek için kullanabilirsiniz:

awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \
     END {if (length(line)) print line;}' flle

4

Vim kullanan başka çözümler (sadece referans için).

Çözüm 1 :

Dosyayı vim'de açın vim filename, ardından komutu yürütün:% normal Jj

Bu komutun anlaşılması çok kolaydır:

  • %: tüm satırlar için,
  • normal: normal komutu yürüt
  • Jj: Join komutunu yürütün, ardından aşağıdaki satıra geçin

Bundan sonra dosyayı kaydedin ve :wq

Çözüm 2 :

Komutu kabukta yürütün vim -c ":% normal Jj" filename, ardından dosyayı kaydedin ve ile çıkın :wq.


Ayrıca norm!daha sağlam olması normaldurumunda daha sağlam J. Vim çözümü için +1.
qeatzy

@qeatzy Bunu bana öğrettiğiniz için teşekkür ederim. Bunu bilmek çok güzel. ^ _ ^
Jensen

3

Aşağıdaki vi komutunu da kullanabilirsiniz:

:%g/.*/j

Ya da :%g//jtek ihtiyacınız birleştirmenin yürütülmesi için bir eşleşme ve boş bir dize hala geçerli bir normal ifade olsa bile.
ghoti

1
@ghoti, In Vim, sadece //kullanırken bunun yerine önceki arama kalıbı kullanılır. Önceden bir düzen yoksa, Vim sadece bir hata rapor eder ve hiçbir şey yapmaz. Jdamian'ın çözümü her zaman çalışır.
Tzunghsing David Wong

1
@TzunghsingDavidWong - vim kullanıcıları için iyi bir işaretçi. Handily benim için ne soru ne de bu cevap vim'den bahsetti.
ghoti

3

Glen jackson'un aşağıdakileri kullanarak cevabında küçük bir değişiklik varpaste : -dayırıcı seçeneğinin değeri birden fazla karakter içeriyorsa paste, karakterler arasında birer birer geçiş yapar ve -sseçeneklerle birlikte aynı giriş dosyasını işlerken bunu yapmaya devam eder.

Bu \n, her seferinde iki satırı birleştirmek için ayırıcı artı kaçış dizisi olarak istediğimiz her şeyi kullanabileceğimiz anlamına gelir .

Virgül kullanma:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string,1
KEY 4192:1349 string,1
KEY 7329:2407 string,2
KEY 0:1774 string,1

ve dolar işareti:

$ paste -s -d '$\n' infile
KEY 4048:1736 string$3
KEY 0:1772 string$1
KEY 4192:1349 string$1
KEY 7329:2407 string$2
KEY 0:1774 string$1

Ne bu olamaz isimli birden karakterden oluşan bir ayırıcı kullanıyoruz.

Bonus olarak, eğer pastePOSIX uyumluysa, bu dosyadaki son satırın yeni satırını değiştirmez, bu nedenle aşağıdaki gibi tek sayıda satıra sahip bir girdi dosyası için

KEY 4048:1736 string
3
KEY 0:1772 string

paste son satırdaki ayırma karakterine yapışmaz:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string

1
nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename

Bu şu şekilde okunur

$0 ~ /string$/  ## matches any lines that end with the word string
printf          ## so print the first line without newline
getline         ## get the next line
printf "%s\n"   ## print the whole line and carriage return

1

İki satırı birleştirmem gerektiğinde (daha kolay işlem için), ancak belirli verileri aşmasına izin verdiğimde, bunun yararlı olduğunu buldum

data.txt

string1=x
string2=y
string3
string4
cat data.txt | nawk '$0 ~ /string1=/ { printf "%s ", $0; getline; printf "%s\n", $0; getline } { print }' > converted_data.txt

çıktı sonra şöyle görünür:

converted_data.txt

string1=x string2=y
string3
string4

1

Vim kullanan bir başka yaklaşım:

:g/KEY/join

Bu join, içindeki kelimeyi içeren tüm satırlara (altındaki satıra) uygulanır KEY. Sonuç:

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

0

En basit yol burada:

  1. Çift satırları kaldırın ve geçici dosyaya yazın 1.
  2. Tek satırları kaldırın ve geçici bir dosyaya yazın 2.
  3. -D ile paste komutunu kullanarak iki dosyayı bir araya getirin (alanı silmek anlamına gelir)

sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2

0
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt

-0satır satır okumak yerine tüm dosyayı kaldırır;
pEkodu loop ile sarar ve çıktıyı yazdırır, http://perldoc.perl.org/perlrun.html adresindeki ayrıntılara bakın ;
^KEYsatırın başlangıcında "KEY" ile eşleşir, ardından .*?diziden önce herhangi bir şeyin ( ) açgözlü olmayan eşleşmesi gelir

  1. \s+satır kesmeleri dahil her türlü bir veya daha fazla boşluk ;
  2. (\d+)yakaladığımız ve daha sonra tekrar eklediğimiz bir veya daha fazla basamak $1;

ardından satır sonu $.

\Ksol tarafındaki her şeyi değiştirmeden hariç tutar, bu nedenle { $1}yalnızca 1-2 dizinin yerini alır, bkz. http://perldoc.perl.org/perlre.html .


0

Kabuk betiği olarak daha genel bir çözüm (birden fazla takip hattının birleştirilmesine izin verir). Bu, her birine bir çizgi ekler, çünkü görünüme ihtiyacım vardı, ancak bu kolayca çözülebilir. Bu örnek, "anahtar" satırın nerede bittiği ve başka hiçbir satırın bitmediği yerdir.

#!/bin/bash
#
# join "The rest of the story" when the first line of each   story
# matches $PATTERN
# Nice for looking for specific changes in bart output
#

PATTERN='*:';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN)
                echo ""
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                echo ""
                ;;

        *)      LINEOUT="$LINEOUT $line"
                ;;
    esac        
done

-1

Aşağıdaki satırı deneyin:

while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file

Sınırlayıcıyı arasına koy

"$line1 $line2";

örneğin, sınırlayıcı ise |:

"$line1|$line2";

Bu cevap, Hai Vu'nun sizden 4 yıl önce yayınlanan cevabında belirtilmeyen hiçbir şey eklemiyor .
fedorqui 'SO

Kısmen katılıyorum, açıklama eklemeye çalışıyorum ve daha genel Eski dosyayı da düzenlemeyecek. Öneriniz için teşekkürler
Suman

-2

Bunun gibi kullanabilirsiniz xargs:

xargs -a file

% cat> dosya abc% xargs -a dosya abc% Benim için çalışıyor
RSG

Bir şey yapar , evet, ama OP'nin istediği gibi değil. Özellikle, mümkün olduğunca çok sayıda çizgiyi birleştirir. Aslında istediğini alabilirsin xargs -n 2ama bu cevap bunu hiç açıklamıyor.
üçlü
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.