1. 概要

Linuxコマンドラインを使用する場合、複数行の入力を1行に結合するのが一般的な操作です。 場合によっては、マージされた行にカスタマイズされた区切り文字を追加したいこともあります。

このチュートリアルでは、これを行うためのいくつかの方法を見ていきます。

2. 問題点

プレーンテキストの入力ファイルがあるとしましょう。

$ cat input.txt 
I came
I saw
I conquered!

ファイルには3行あり、各行に空白があります。

そして、私たちがそれらに参加したいと思うかもしれないさまざまな方法があります:

  • 区切り文字なし:私は来ました私は見ました私は征服しました!
  • 1文字の区切り文字(’、’):来た、見た、征服した!
  • 複数の文字の区切り文字(’;’):来ました;見ました; 征服しました!

このチュートリアルでは、次の方法でこれらに対処しようとします。

  • ピュアバッシュ
  • trコマンド
  • 貼り付けコマンド
  • sedコマンド
  • awkコマンド

3. ピュアバッシュ

Bashは最新のLinuxディストリビューションのデフォルトのシェルであり、 Bashソリューションは組み込みコマンドのみを使用するため、他のユーティリティに依存しません。

3.1. 区切り文字なしで、単一文字の区切り文字を使用して結合する

短いBashワンライナーは、区切り文字なしで行を結合できます。

$ (readarray -t ARRAY < input.txt; IFS=''; echo "${ARRAY[*]}")
I cameI sawI conquered!

同じスクリプトを使用するが、単一の文字’‘を IFS 変数に割り当てると、2番目の問題も解決されます。

$ (readarray -t ARRAY < input.txt; IFS=','; echo "${ARRAY[*]}")
I came,I saw,I conquered!

それでは、スクリプトがどのように機能するかを理解しましょう。 上記のワンライナーには3つのビルディングブロックがあり、それぞれについて説明します。

readarray -t ARRAY < input.txt

readarrayはBashの組み込みコマンドです。 Bashver.4で紹介されました。 readarrayは、標準入力から配列変数ARRAYに行を読み取ります。 -tオプションは、各行から末尾の改行を削除します。 その後、変数があります配列 3つの要素を含みます。

入力データは input.txt ファイル、私たちはする必要がありますファイルを標準入力にリダイレクトしますを使用して

IFS=''

IFS は特別なシェル変数であり、その名前は Internal FieldSeparatorを意味します。 IFS のデフォルト値は、スペース、タブ、および改行です。

ここでは、要件に応じて、 IFSに空または「、」の単一文字を割り当てました。

echo "${ARRAY[*]}"

$ {ARRAY [*]}は、配列変数ARRAYのすべての要素を意味します。 とともにエコーコマンド、のすべての要素配列で区切られて印刷されます IFS 変数。 つまり、必要な出力が得られます。

まだ注意すべきことがいくつかあります。

すべてのコマンドを括弧で囲みますそれの訳は (…コマンド。 ..) 現在のシェルのIFS変数が推測されないように、サブシェルでコマンドを実行します。

$ {ARRAY[*]}${ARRAY [@]} はどちらも、配列のすべての要素を示します。 それらの違いは微妙です。${ARRAY [*]}は1つの引数を作成しますが、$ARRAY[@]は別々の引数に展開されます。IFS変数は最初の引数でのみ有効になります[ X191X]

3.2. 複数文字の区切り文字で結合する

IFS変数を使用して配列出力を制御すると便利です。 ただし、複数の文字の区切り文字で要素を区切る場合、この方法は機能しません。

問題を解決する方法はいくつかあります。 すでに配列変数があるので、もう一度使用してみましょう。

$ readarray -t ARRAY < input.txt;  printf -v TXT  "%s; " "${ARRAY[@]}"; echo ${TXT%; }
I came; I saw; I conquered!

コマンドを詳しく見て、どのように機能するかを理解しましょう。

printf -v TXT  "%s; " "${ARRAY[@]}"

私たちが得た後配列による変数 readarray コマンド、組み込みを使用しましたフォーマットされた文字列を変数に保存するための-vvarオプションを指定したprintfコマンド $TXT。 

今回、 ${ARRAY[*]}の代わりに${ARRAY[@]}を使用しました。これは、複数の引数を持ち、それぞれをprintfコマンドに渡すためです。

その場合、 $TXTの値は次のようになります。 私は征服しました!; 「。 残っている唯一のタスクは、末尾の区切り文字「;」を削除することです。

echo ${TXT%; }

$ {var%substring}は文字列操作のトリックです。 $varの後ろから$substringの最短一致を削除します。 そう $ {TXT%; } 末尾の「 ; 「。

したがって、必要な出力が得られました。

4. trコマンド

tr コマンドを使用して、標準入力(stdin)から特定の文字を削除または文字を変換することができます。 

tr コマンドはstdinからのみ読み取るため、 tr を使用してファイルを処理する場合は、ファイルをstdinにリダイレクトする必要があります。

4.1. 区切り文字なしで参加する

tr コマンドは、この問題を非常に簡単な方法で解決できます。 ファイルの内容からすべての改行を削除すると、すべての行が結合されます。

$ tr -d '\n' < input.txt 
I cameI sawI conquered!

4.2. 1文字の区切り文字で結合する

すべての改行をコンマ「」に変換すれば、この問題も簡単に解決できると思うかもしれません。 やるだけやってみよう:

$ tr '\n' ',' < input.txt 
I came,I saw,I conquered!,

おっとっと! 上記の出力には末尾のコンマがあります。 これは、ファイルの最後の行が改行で終わっているためです。 残念ながら、trコマンドは末尾のコンマを削除できません。

つまり、trユーティリティだけではこの問題を解決できません。 それを解決するには、他のユーティリティの助けが必要です。

たとえば、trコマンドからの出力をsedコマンドにパイプして、末尾のコンマを改行に変更できます。

$ tr '\n' ',' < input.txt | sed 's/,$/\n/'
I came,I saw,I conquered!

4.3. 複数文字の区切り文字で結合する

tr コマンドは、単一の文字を複数の文字に変換できないため、複数の文字の区切り文字で行を結合することはできません。

5. 貼り付けコマンド

ペーストユーティリティはGNUCoreutils パッケージのメンバーであるため、すべてのLinuxディストリビューションで利用できます。

past コマンドは、ファイルの行をマージするという1つのことを実行します。 それはまさに私たちが問題を解決するために必要なものです。

5.1. 区切り文字なしで、単一文字の区切り文字を使用して結合する

pastコマンドを使用して2つの問題を解決する方法を見てみましょう。

$ paste -sd '' input.txt 
I cameI sawI conquered!

$ paste -sd ',' input.txt 
I came,I saw,I conquered!

上記の2つのコマンドでは、pastコマンドに-s-d。の2つのオプションを渡しました。

past コマンドは、複数の入力ファイルからの行をマージできます。 デフォルトでは、最初の列のエントリが最初のファイルに属し、2番目の列のエントリが2番目のファイルに属するというように、行をマージします。 -s オプションを使用すると、行を行ごとにマージできます。

また、 past コマンドに、 -d”または-d’、’。を渡すことにより、指定された区切り文字を使用してマージされた行を区切るように指示しました。

5.2. 複数文字の区切り文字で結合する

-d オプションは、結果の区切り文字を制御するためです。 この問題は、-dを複数の文字列と一緒にpasteコマンドに渡すことで解決できると期待しています。 何が起こるか見てみましょう:

$ paste -sd "@#" input.txt
I came@I saw#I conquered!

上記のテストは、複数の文字を-dオプションに渡すと、貼り付けコマンドが各文字を複数の文字の区切り文字ではなく、順番に区切り文字に変換することを示しています。ただし、これは必要なことではありません。

past コマンドは、複数文字の区切り文字で行を結合することはできません。

6. sedコマンド

sed は、強力なコマンドラインテキスト処理ユーティリティです。 ほぼ同じコードを使用して、3つの問題を解決できます。

$ sed ':a; N; $!ba; s/\n//g' input.txt 
I cameI sawI conquered!

$ sed ':a; N; $!ba; s/\n/,/g' input.txt 
I came,I saw,I conquered!

$ sed ':a; N; $!ba; s/\n/; /g' input.txt 
I came; I saw; I conquered!

簡単に言えば、 このsedワンライナーのアイデアは次のとおりです。各行をパターンスペースに追加し、最後にすべての改行を指定された文字列に置き換えます。 

それがどのように機能するかを理解しましょう:

  • :a; aと呼ばれるラベルを定義します
  • N; –次の行をsedのパターンスペースに追加します
  • $!ba; –現在の行が最後の行( $ )の場合、()ラベル:a a)にジャンプしないでください
  • s / \ n / REPLACEMENT / g –すべての改行を指定されたREPLACEMENTに置き換えます

seds/../../ g は正規表現ベースの置換であるため、3つの問題を解決するためにさまざまな置換を行うことができます。

7. awkコマンド

awk は、もう1つの優れたコマンドラインテキスト処理ツールです。

awkを使用して問題を解決するにはさまざまな方法があります。 このセクションでは、そのうちの1つを示します。

$ awk -v d="" '{s=(NR==1?s:s d)$0}END{print s}' input.txt
I cameI sawI conquered!

$ awk -v d="," '{s=(NR==1?s:s d)$0}END{print s}' input.txt
I came,I saw,I conquered!

$ awk -v d="; " '{s=(NR==1?s:s d)$0}END{print s}' input.txt
I came; I saw; I conquered!

変数dの値を必要な区切り文字で設定しただけで、同じawkコードで期待どおりの結果が得られることがわかります。

コードを詳しく見て、どのように機能するかを理解しましょう。

  • -vd =”…” –変数 d を作成したので、コードで区切り文字列をハードコーディングする必要がありません。
  • s =(NR == 1?s:sd)$ 0 –各行( $ 0 )を連結し、変数sに保存します。
  • NR == 1?s:sd –最初の行のプレフィックスとして区切り文字を追加したくないため、区切り文字を処理しました。 これは、 if(NR> 1)s =sdのコンパクトな形式です。
  • END {print s} –すべての行が処理された後、変数sENDブロックに出力します

8. 結論

この記事では、ファイル内の行を結合する問題を解決する方法について説明しました。 3つの異なるシナリオで問題を解決するために、さまざまなコマンドラインツールを使用しました。

一部のコマンドでは、次の3つのシナリオすべてを処理できないことがわかりました。

区切り文字なし 1文字の区切り文字 複数文字の区切り文字
ピュアバッシュ
tr
ペースト
sed
awk