複数行複数の文字列を1行に

複数行複数の文字列を1行に

入力(複数行):

abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456 

出力(1行から1行に文字列/正規表現を抽出):

def 345
def def 345
def def def 345

最初...

echo "abc 123" | grep -Po "\Kabc|\K123"

ただし、これにより2行が印刷されます。

abc
123

第二:

echo -ne "abc def bac 123\nabc def def bac 123\nabc def def def bac 123 123\n" | grep -Po "def|123" | paste -d ' ' - -

しかし、これは次のことを示しています。

def 123
def def
123 def
def def
123 123

私の考えでは:

def 123
def def 123
def def def 123 123

\ nを削除するためにtrを使用することはできません。 def または 345 は 1 行に複数回見つかる可能性があり、他のすべての行を削除する \n は意味がありません。列区切り記号は使用できません。

答え1

そしてperl

$ cat ip.txt
abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456 1234

$ perl -lane 'print join " ", grep { /def|123/ } @F' ip.txt
def 123
def def 123
def def def 123 1234

$ perl -lane 'print join " ", grep { $_ eq "def" || $_ eq "123" } @F' ip.txt
def 123
def def 123
def def def 123
  • -laneここでは-l、入力行から改行を削除しprint、使用時-aに再追加し、入力行を空白に自動的に分割し、結果を@F配列に保存し、-n入力行を繰り返しますが、処理後に行を自動的に印刷せずにコマンドで-e許可します。 Perlスクリプトラインを提供
  • grep { /def|123/ } @Fまたはを@F含む場合は、配列内のすべての要素をフィルタリングします。def123
    • 正規表現の代わりに文字列マッチングが必要な場合は、次のようになります。grep { $_ eq "def" || $_ eq "123" } @F
  • print join " "grepスペースを区切り文字として使用して、出力から得られた要素を印刷します。

答え2

ex次のように使用しますawk

$ cat test.txt
abc def ghi 123 345 456 
abc def def ghi 123 345 456
abc def def def ghi 123 345 456
$ printf '%s\n' 'g/^/.!awk -v ORS=" " -v RS=" " "/^(def|345)$/"' %p | ex test.txt
def 345 
def def 345 
def def def 345 
$ 

その機能は次のとおりです。

  1. ex変更、印刷、および/または保存できるバッファ(in)でファイルを読み込みます。
  2. awkスクリプトを介してバッファの各行を(個別に)フィルタリングします。
  3. バッファの内容全体を印刷します(を使用して%p)。

上記のコマンドは結果をファイルに保存しません。これを行うに%pは、に置き換えますx


詳細な説明:

exスクリプト可能なファイルエディタです。ファイル名(test.txt)を引数として受け入れ、標準入力から編集コマンドを取得します。

ここでは、使用された編集コマンドを提供しますprintf。最初のパラメータは、残りのパラメータの出力方法を制御するprintf形式文字列(この場合)です。すべてのパラメータは文字列であり、各パラメータの後に改行文字を印刷する必要があると言います。 (一重引用符は、シェルがバックスラッシュを解釈するのを避けるために存在します。私たちはシェルではなくバックスラッシュを取得したいと思います。)'%s\n'printfprintf

exを使用して2つのパラメータを送信しますprintf。ここにそれらが来る:

g/^/.!awk -v ORS=" " -v RS=" " "/^(def|345)$/"
%p

そのうちの2番目は最も簡単です。 %アドレス範囲です。これは「フルバッファ」を意味します。 p印刷コマンドです。したがって、これは「フルバッファ印刷」を意味する。

最初は少し分解が必要です。

g/.../「グローバル」コマンドです。バッファ全体で指定されたパターン(この場合は^「行の先頭」を意味する正規表現)に一致する行を検索し、その行exごとに次の編集コマンドを実行します。各行には行の先頭があるため、すべての行が一致するため、各行^で次のコマンドを別々に実行する効果があります。

次に、.「(バッファの)現在の行」を意味するアドレスがあります。命令の後に続くので、gバッファの各行を順番に参照します。

!シェルコマンドの実行に使用されます。アドレスが先頭にある場合(この場合.)、与えられた行範囲(または単一行)が与えられたシェルコマンドに提供されます。標準入力コマンドの結果(標準出力)はその行のバッファに配置されます。

つまり、.!shell-command-herein はex、いくつかの外部コマンドを介してバッファの現在のラインをフィルタリングすることを意味します。

我々はすでに、このコマンド設定がコマンドでバッファの各行を(個別に)フィルタリングする方法を扱っています。awkそれでは、コマンドを分析してみましょうawk

awk -v ORS=" " -v RS=" " "/^(def|345)$/"

awkこのフラグを使用して変数を定義できます-v。したがって、最初のいくつかのパラメータORSRS変数を単一の空白文字に設定します。

RSin はawk「レコード区切り記号」です。デフォルトでは、その値は改行文字です。設定された文字は、awk読み取り時にレコード(通常は行)を区切るために使用されます。

同様に、ORS出力レコード区切り文字は、awk出力を印刷するときにレコード(通常は行)を区切るために使用される項目を制御します。

各単語を空白文字に設定すると、行内の各単語を単一のレコードで簡単に操作できます。

次の部分は実際のawkコマンドです。 (awk独自のスクリプト言語です。) awkコマンドブロックは条件と動作で構成されています。ここで条件は/.../正規表現一致です。つまり、条件は、指定された正規表現に一致するすべてのレコード(この場合は単語)に適用されます。正規表現部分は^(文字列の始まり)、$(文字列の終わり)であり、2つの可能なパターンは括弧内にグループ化され|(パイプライン)で区切られ、これらのパターンのいずれも許容可能であることを示します。

条件の後にはアクションがないため(アクションは中括弧内に表示されますawk)、awkのデフォルトのアクション「print」は条件に一致するレコードに適用されます。 (これは、対応する行に対して一致する各レコード(単語)が印刷され、対応する出力が読み取られ、awk最初に入力されたexバッファの行位置に配置されることを意味します。)exawk

このソリューションは、すべてのパターンが完全な単語と一致するという単純化された仮定を提供します。つまり、次のパターンのいずれとも一致しないようにします。含むスペース。これは質問に提供した入力の例と一致します。

答え3

awk必要なフィールドのみを使用して維持できます。

echo -e "abc def bac 123\nabc def def bac 123\nabc def def def bac 123 123" \
  | awk -v var1="def" -v var2="123" '{
  i=0
  for (j=1; j<=NF; j++){
    if ($j==var1 || $j==var2){ $++i=$j }
    if (i!=j){ $j="" }
  }
  print
}'

これはforループ内のフィールドを反復処理するか、def123のフィールドに再割り当てします$++i=$j(インデックス0から始まるので、最初のフィールドは1、次のフィールドは2です...)。インデックスが空の場合はリセットされます。$j空の文字列($j="")の現在のフィールドiは循環インデックスではありませんj

出力:

def 123
def def 123
def def def 123 123

関連情報