web-dev-qa-db-ja.com

複数行複数の文字列を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

Trを使用して\ nを削除することはできません。defまたは345は1行で複数回見つかり、2行ごとに削除しても意味がありません\ n列セパレータを使用できません。

2
TheAnotherWise

exawkの併用:

$ 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内の)バッファーに読み取り、そこで変更、印刷、保存できます。
  2. awkスクリプトを使用して、バッファの個々の行を(個別に)フィルタリングします。
  3. バッファの内容全体を出力します(%pを使用)。

上記のコマンドは、結果をファイルに保存しません。それを行う場合は、%pxに置き換えてください。


より長い説明:

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

ここでは、printfを使用して編集コマンドを提供します。 printfの最初の引数はフォーマット文字列で、この場合は'%s\n'です。これは、printfの残りの引数の出力方法を制御するために使用されます。すべての引数は文字列であり、それぞれの後に改行文字を出力する必要があると言います。 (単一引用符は、シェルがバックスラッシュを解釈しないようにするためのものです。シェルではなくprintfでバックスラッシュを取得する必要があります。)

exを使用してprintfに送信する2つの引数があります。はい、どうぞ:

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

これらの2番目は最も簡単です。 %はアドレス範囲です。 「バッファ全体」を意味します。 pは印刷コマンドです。つまり、これは単に「バッファ全体を出力する」ことを意味します。

最初のものはいくつかの分解を行います。

g/.../は「グローバル」コマンドです。与えられたパターン(この場合は^、 "行の開始"を意味する正規表現)に一致する行をバッファー全体で検索し、そのような各行で次のex編集コマンドを実行します。すべての行に行頭があるため、すべての行が^に一致するため、次のコマンドをすべての行で個別に実行することになります。

次に、.は、「(バッファの)現在の行」を意味するアドレスです。 gコマンドの後に指定されているため、バッファの各行を順番に参照します。

!は、シェルコマンドを実行するために使用されます。アドレス(この場合は.)が前に付いている場合、指定された行範囲(または単一行)が指定されたシェルコマンドに標準入力および結果(標準出力)でフィードされます。コマンドのその行がバッファのその行の代わりに置かれます。

つまり、ex.!Shell-command-hereは、外部コマンドを使用してバッファの現在の行をフィルタリングすることを意味します。

そのため、このコマンドセットアップがawkコマンドを使用してバッファの各行を(個別に)フィルタリングする方法について説明しました。では、そのawkコマンドを分析しましょう:

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

-vフラグを使用して、awkの変数を定義できます。したがって、最初のいくつかの引数は、ORSおよびRS変数を単一の空白文字に設定します。

RSawkは「レコード区切り文字」です。デフォルトでは、その値は改行です。設定されている文字は、レコードが読み込まれるときに、レコード(通常は行)を区切るためにawkが使用する文字です。

同様に、「出力レコードセパレータ」であるORSは、レコード(通常は行)を印刷するときに、レコード(通常は行)を区切るためにawkが使用するものを制御します。

それぞれをスペース文字に設定することで、行の各Wordを単一のレコードとして簡単に操作できます。

次の部分は実際のawkコマンドです。 (awkは独自のスクリプト言語です。)awkコマンドブロックは条件とアクションで構成されます。どちらも省略できます。ここで、条件は/.../であり、これは正規表現の一致です。つまり、この条件は、指定された正規表現に一致するすべてのレコード(この場合は単語)に適用されます。正規表現の部分は、^(文字列の先頭)、$(文字列の末尾)、および括弧でグループ化された2つの可能なパターンで、|(パイプ)で区切られ、これらのパターンは受け入れられます。

条件の後にアクションがないため(アクションはawkの場合は中括弧で囲まれます)、awkのデフォルトのアクションである「印刷」がその条件に一致するレコードに適用されます。 (これは、awkが行の一致する各レコード(Word)を印刷し、次にexがその出力を読み取り、それをバッファーの行の代わりに配置することを意味しますexは最初にawkに提供されました。)

このソリューションでは、すべてのパターンが完全な単語と照合される、つまりinclude whitespaceであるパターンとは照合されないという単純化された仮定が行われます。これは、質問で入力した例の入力と一致します。

1
Wildcard

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ループのフィールドをループし、defまたは123を次のフィールド$++i=$jに再割り当てします(インデックス0から開始するため、最初のフィールドは1で、次は2です) ...)インデックスiがループインデックスjでない場合、現在のフィールド$jを空の文字列($j="")にリセットします。

出力:

def 123
def def 123
def def def 123 123
0
Freddy