コマンドラインから2行ごとに1つにマージする方法は?


151

次の形式のテキストファイルがあります。1行目は「KEY」、2行目は「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

キーと同じ行に値が必要です。したがって、出力は次のようになります...

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

$またはのような区切り文字を使用できればそれはより良いでしょう,

KEY 4048:1736 string , 3

2つのラインを1つにマージするにはどうすればよいですか?


これを行う方法はたくさんあります!私がやったと少しベンチをprpasteawkxargssedpure bash!(xargsbashより遅い、遅いです!)
F. Hauri

回答:


182

awk:

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

出力の最後に空の行があることに注意してください。

sed:

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

カラー出力では機能しません。このQ&Aですべてを試しましたが、出力がANSIカラーの場合は何も機能しませんでした。Ubuntu 13.04でテスト
Leo Gallucci

1
@elgalu:ANSIカラーはエスケープ文字の組み合わせの集まりにすぎないため。そのような出力に対してhexeditを実行して、何があるかを確認してください。
not2qubit 14

7
このawkソリューションは、などのprintf拡張文字列%sが内で見つかると壊れる可能性があります$0。その失敗は次のように回避することができます:'NR%2{printf "%s ",$0;next;}1'
ghoti

9
グーグルするのは本当に難しいので1、閉じ中括弧の後はどういう意味ですか?
erikbwork

5
@ erikb85ここでは、stackoverflow.com
Viraj

243

paste この仕事に適しています:

paste -d " "  - - < filename

10
これは、sedもawkも使用していないにもかかわらず、提示された最良のソリューションだと思います。奇数行の入力では、ケントのawkソリューションは最後の改行をスキップし、彼のsedソリューションはその最初の行をスキップし、私のソリューションは最後の行を繰り返します。 paste一方、は完全に動作します。+1。
ghoti 2014年

8
よく使うcutけどいつも忘れてpasteます。それはこの問題のために揺れ動く。stdinからのすべての行を組み合わせる必要があり、それをで簡単に実行しましたpaste -sd ' ' -
Clint Pachl 2014

4
シンプルで美しい!
krlmlr 2014

8
-つまり、stdinを意味するので、stdin paste - -から読み取り、次にstdinから読み取るという意味です。必要な数だけそれらをスタックできます。
ThorSummoner 2016

1
はい、@ ThorSummoner ... 3行ごとに1行に貼り付けて貼り付けなければなりませんでした---完全に機能しました。
Daniel Goldfarb 2017年

35

sed、awk、grepの代替:

xargs -n2 -d'\n'

これは、N行を結合し、スペース区切りの出力のみが必要な場合に最適です。

私の最初の答えはxargs -n2、線ではなく単語で分離することでした。-d入力を任意の1文字で分割するために使用できます。


4
これは良い方法ですが、行ではなく単語で機能します。これをラインで機能させるには、次を追加します-d '\n'
Don Hatch

2
うわー、私は普通のxargsユーザーですが、これを知りませんでした。素晴らしいヒント。
Sridhar Sarnobat

1
これ大好き。とてもきれい。
Alexander Guo

28

ぶら下がること以外に犬を殺す方法はたくさんあります。[1]

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

引用符の中に好きな区切り文字を入れます。


参照:

  1. もともとは「猫に皮をむく方法がたくさんありました」が、ペットとは何の関係もない、より古く、潜在的に発生した表現に戻りました。

このソリューションが大好きです。
luis.espinal 2013年

5
猫の飼い主として、このようなユーモアはありがたいです。
witkacy26 2015年

4
@ witkacy26、心配ごとに調整された表現。
ghoti 2015年

私はこのawkソリューションが好きですが、どのように機能するのかわかりません:S
Rubendob

@Rubendob-awkは入力の各行を読み取り、それを変数に入れます$0。このgetlineコマンドは、「次の」入力行を取得してに配置し$0ます。したがって、最初のステートメントは最初の行を取得し、printコマンドは変数に保存されたものkeyを、を使用してフェッチされた行とともに、コンマを含む文字列と連結しますgetline。より明確?:)
ghoti 2017年

12

これがbashでの私の解決策です:

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

11

以前の解決策は機能するようですが、ドキュメントで単一の異常が発生した場合、出力はバラバラになります。以下は少し安全です。

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

3
なぜ安全なのですか?何をし/KEY/ますか?何をしないp終わりますか?
Stewart

/KEY/、を含む行を検索しますKEYp結果が出力されます。それが含まれている行にのみ操作を適用するため、より安全KEYです。
minghua

11

ここに別の方法があり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が示しているように、安全のためにブレースを、移植性のために括弧を追加することをお勧めします。

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

ORSOutput Record Separatorの略です。ここではNR、行番号を格納するを使用して条件をテストしています。のモジュロがNR真の値(> 0)の場合、出力フィールドセパレータをFS(フィールドセパレータ)の値に設定しRSます。これはデフォルトではスペースですが、それ以外の場合は改行である(レコードセパレータ)の値を割り当てます。

,セパレータとして追加したい場合は、以下を使用します。

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

1
間違いなく正しいアプローチなので+1ですが、レコードを印刷するデフォルトのアクションを呼び出すために評価されている条件は何でしょうか。任務は成功したのでしょうか?それは単純でORSあり、それはtrueORSがゼロまたはnull文字列ではない値を取得し、数値比較ではなく文字列である必要があると正しく推測しているため、そのように扱われていますか?それは別のものですか?私は本当にわからないので、と書いていたでしょうawk '{ORS=(NR%2?FS:RS)}1' file。移植性も確保するために、3項式を括弧で囲みました。
エド・モートン

1
@EdMortonええ、私はこの回答に対するいくつかの賛成票が安全のためにブレースを含むように更新しようとしているのを見ました。括弧も追加されます。
jaypal sing 2014

7

"ex"は、sed、awk、grepなどと同じファミリーに属するスクリプト可能なラインエディターです。あなたが探しているものかもしれません。最新のviクローン/後継者にもviモードがあります。

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

これは、一致した場合、「KEY」を実行し、各ラインのために言うjは次の行のOIN。そのコマンドが(すべての行に対して)完了したら、w riteとq uitを発行します。


4

Perlがオプションである場合は、以下を試すことができます。

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

-0TELL perlは(レコード区切りを設定するには$/)、我々は我々のマッチングパターンで複数行にまたがることができるように、nullに私はそれが実際に何を意味するのかを把握するためにmanページは少しも技術的なもの。。
シュリダールSarnobat

4

次のようにawkを使用して、2組の行を組み合わせることができます。

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

4

vimを使用した別のソリューション(参照用)。

解決策1

vim vim filenameでファイルを開き、コマンドを実行します:% normal Jj

このコマンドは非常に理解しやすいです:

  • %:すべての行について、
  • normal:通常のコマンドを実行します
  • Jj:結合コマンドを実行してから、下の行にジャンプ

その後、ファイルを保存して終了します :wq

解決策2

シェルでコマンドを実行しvim -c ":% normal Jj" filename、ファイルを保存してで終了し:wqます。


また、再マッピングされた場合のnorm!堅牢性も向上しnormalJいます。vimソリューションの場合は+1。
qeatzy 2017

@qeatzy教えてくれてありがとう それを知ってとても嬉しい。^ _ ^
2017

3

次のviコマンドを使用することもできます。

:%g/.*/j

または:%g//j、必要なのは結合を実行するための一致だけであり、null文字列は依然として有効な正規表現です。
ghoti 2014

1
@ ghoti、Vimでは、だけを使用すると//、代わりに以前の検索パターンが使用されます。以前のパターンがない場合、Vimは単にエラーを報告し、何もしません。Jdamianのソリューションは常に機能します。
Tzunghsing David Wong 2016

1
@TzunghsingDavidWong-それはvimユーザーにとって良いポインタです。私にとって便利なことに、質問もこの回答もvimについて言及していません。
ghoti 2016

3

を使用したglenn jackmanの回答のわずかなバリエーションpaste-d区切り文字オ​​プションの値に複数の文字が含まれている場合paste、文字を1つずつ循環し、-sオプションと組み合わせると、同じ入力ファイルを処理している間、そのようになります。

つまり、セパレータとエスケープシーケンスとして必要なものを使用\nして、一度に2行をマージできます。

コンマを使用:

$ 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

ドル記号:

$ 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

これができないことは、複数の文字で構成されるセパレータを使用することです。

おまけとして、pastePOSIXに準拠している場合、これはファイルの最後の行の改行を変更しないため、次のような奇数の行を持つ入力ファイルの場合

KEY 4048:1736 string
3
KEY 0:1772 string

paste 最後の行の分離文字を追加しません:

$ 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

これは

$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

2つの行を結合する必要がある場合(処理を容易にするため)に、特定の過去のデータを許可する場合、これが役立つことがわかりました

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

出力は次のようになります。

Converted_data.txt

string1=x string2=y
string3
string4

1

vimを使用する別のアプローチは次のとおりです。

:g/KEY/join

これはjoin、単語を含むすべての行に(その下の行に)aを適用KEYします。結果:

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

最も簡単な方法はここにあります:

  1. 偶数行を削除して、一時ファイルに書き込みます1。
  2. 奇数行を削除して、一時ファイルに書き込みます2。
  3. -d(スペースを削除する)を指定して貼り付けコマンドを使用して、2つのファイルを1つに結合する

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

-0ファイルを1行ずつ読み取るのではなく、ファイル全体を移動します。
pEコードをループでラップし、出力を出力します。詳細については、http://perldoc.perl.org/perlrun.htmlを参照してください。
^KEY行頭の "KEY"にマッチし、その後.*?にシーケンスの前に何でも()の貪欲でないマッチが続く

  1. \s+改行を含むあらゆる種類の1つ以上のスペース。
  2. (\d+)キャプチャし、後でとして再挿入する1つ以上の数字$1

行の終わりが続き$ます。

\K左側のすべてを置換から都合よく除外するため{ $1}、1-2シーケンスのみが置換されます。http://perldoc.perl.org/perlre.htmlを参照してください。


0

シェルスクリプトとしてのより一般的なソリューション(複数のフォローアップ行を結合できるようにします)。私は可視性が必要だったので、これはそれぞれの間に線を追加しますが、それは簡単に修正されます。この例では、「キー」行が:で終わり、他の行では終わりませんでした。

#!/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

次の行を試してください。

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

区切り文字を間に入れます

"$line1 $line2";

たとえば、区切り文字がの場合|、次のようになります。

"$line1|$line2";

この回答は、あなたの4年前に投稿されたHai Vuの回答で提供されていないものは何も追加していません。
fedorqui 'SO stop harming'

部分的に同意し、説明とより一般的なものを追加しようとします。古いファイルも編集しません。あなたの提案をありがとう
Suman

-2

次のxargsように使用できます:

xargs -a file

%cat> file abc%xargs -a file abc%Works for me
RSG

はい、それは何かを行いますが、OPが要求したものは行いません。具体的には、できるだけ多くの行を結合します。あなたは実際にあなたが望むものを得ることができましたxargs -n 2が、この答えはこれをまったく説明していません。
tripleee 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.