入力(複数行):
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列セパレータを使用できません。
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
$
これは何ですか:
ex
内の)バッファーに読み取り、そこで変更、印刷、保存できます。awk
スクリプトを使用して、バッファの個々の行を(個別に)フィルタリングします。%p
を使用)。上記のコマンドは、結果をファイルに保存しません。それを行う場合は、%p
をx
に置き換えてください。
より長い説明:
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
変数を単一の空白文字に設定します。
RS
のawk
は「レコード区切り文字」です。デフォルトでは、その値は改行です。設定されている文字は、レコードが読み込まれるときに、レコード(通常は行)を区切るためにawk
が使用する文字です。
同様に、「出力レコードセパレータ」であるORS
は、レコード(通常は行)を印刷するときに、レコード(通常は行)を区切るためにawk
が使用するものを制御します。
それぞれをスペース文字に設定することで、行の各Wordを単一のレコードとして簡単に操作できます。
次の部分は実際のawk
コマンドです。 (awk
は独自のスクリプト言語です。)awk
コマンドブロックは条件とアクションで構成されます。どちらも省略できます。ここで、条件は/.../
であり、これは正規表現の一致です。つまり、この条件は、指定された正規表現に一致するすべてのレコード(この場合は単語)に適用されます。正規表現の部分は、^
(文字列の先頭)、$
(文字列の末尾)、および括弧でグループ化された2つの可能なパターンで、|
(パイプ)で区切られ、これらのパターンは受け入れられます。
条件の後にアクションがないため(アクションはawk
の場合は中括弧で囲まれます)、awkのデフォルトのアクションである「印刷」がその条件に一致するレコードに適用されます。 (これは、awk
が行の一致する各レコード(Word)を印刷し、次にex
がその出力を読み取り、それをバッファーの行の代わりに配置することを意味しますex
は最初にawk
に提供されました。)
このソリューションでは、すべてのパターンが完全な単語と照合される、つまりinclude whitespaceであるパターンとは照合されないという単純化された仮定が行われます。これは、質問で入力した例の入力と一致します。
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