順序に関係なく、行から複数のパターンを抽出します
私はUnixスクリプトを初めて使用するので、ご容赦ください。
各行のプロセスに関する情報を含むファイルが提供されます。これらのプロセスに関する特定の情報を各行から抽出する必要があります。
ファイルの例-
process1 port=1234 appID=dummyAppId1 authenticate=true <some more params>
process3 port=1244 authenticate=false appID=dummyAppId2 <some more params>
process2 appID=dummyAppId3 port=1235 authenticate=true <some more params>
必要な出力は-
1
port=1234 authenticate=true appID=dummyAppId1
2
port=1244 authenticate=false appID=dummyAppId2
3
port=1235 authenticate=true appID=dummyAppId3
各行の番号1、2、および3は、出力ファイルの行番号を示しています。
私はすでにsed
s/
コマンドを使用しようとしましたが、それは順序固有ですが、入力ファイルのパラメーターは順序に従いません-その結果、入力ファイルの一部の行がスキップされます。
これが私のコマンドです-
sed -nr 'appId/s/(\w+).*port=([^ ]+) .*authenticate=[^ ]+) .*appId=[^ ]+) .*/\2\t\3\t\4/p' | sed =
順序に関係なく、これらのパラメーターを抽出する方法を誰かに教えてもらえますか?
ありがとう!
編集1:この方法でgrepの後ろ向きゼロ幅アサーション機能を使用することができました-
grep -Po '(?<=pattern1=)[^ ,]+|(?<=pattern2=)[^ ,]+|(?<=pattern3=)[^ ,]+|(?<=pattern4=)[^ ,]+' filename
しかし、これは新しい行の各行の出力を与えるようです。
1234
true
dummyAppId1
grepを使用して1行でそれを取得する方法を理解しようとしています(つまり、X行を1にマージすることではありません)
編集2:入力のパラメーターの順序を混同
編集3:申し訳ありませんが、これについては前に説明する必要がありました-perl
作業しているマシンで制限されているようです。StephaneとSundeepが提供する回答は、ローカルでテストすると完全に機能しますが、最終的に実行するために必要なマシンでは機能しません。awk、grep、sedが主にサポートされているオプションのようです:(
回答
With awk
(でテスト済み、GNU awk
他の実装で動作するかどうかわからない)
$ cat kv.awk /appID/ { for (i = 1; i <= NF; i++) { $i ~ /^port=/ && (a = $i) $i ~ /^authenticate=/ && (b = $i) $i ~ /^appID=/ && (c = $i) } print NR "\n" a, b, c } $ awk -v OFS='\t' -f kv.awk ip.txt
1
port=1234 authenticate=true appID=dummyAppId1
2
port=1244 authenticate=false appID=dummyAppId2
3
port=1235 authenticate=true appID=dummyAppId3
と perl
$ # note that the order is changed for second line here $ cat ip.txt
process1 port=1234 authenticate=true appID=dummyAppId1 <some more params>
process3 port=1244 appID=dummyAppId2 authenticate=false <some more params>
process2 port=1235 authenticate=true appID=dummyAppId3 <some more params>
$ perl -lpe 's/(?=.*(port=[^ ]+))(?=.*(authenticate=[^ ]+))(?=.*(appID=[^ ]+)).*/$1\t$2\t$3/; print $.' ip.txt
1
port=1234 authenticate=true appID=dummyAppId1
2
port=1244 authenticate=false appID=dummyAppId2
3
port=1235 authenticate=true appID=dummyAppId3
(?=.*(port=[^ ]+))
の最初のキャプチャグループport
(?=.*(authenticate=[^ ]+))
以下のための第二の捕捉グループauthenticate
とのそうprint $.
行番号- 部分一致を回避するには
\bport
、\bappID
単語の境界で十分な場合は、などを使用します。それ以外の場合は、(?<!\S)(port=[^ ]+)
空白に基づいて制限するために使用します。
appID
または他のそのような条件を含む行のみを印刷する必要がある場合は、に変更-lpe
し-lne
てに変更print $.
します。print "$.\n$_" if /appID/
をperl
使用すると、次のようなアプローチを使用できます。
perl -lne 'my %h;
$h{$1} = $& while /(\S+?)=(\S+)/g;
print "@h{qw(port authenticate appID)}"'
キーが属性名で値がname=value
sであるハッシュテーブルを作成し、後で必要なものを出力します。
交換する$&
と$2
、あなただけの値を出力にしたい場合。
と同じawk
:
awk '
{
split("", h)
for (i = 1; i <= NF; i++)
if (n = index($i, "=")) h[substr($i, 1, n - 1)] = $i
print h["port"], h["authenticate"], h["appID"]
}'
を使用するとpcregrep
、次のことができます。
pcregrep -o1 -o2 -o3 --om-separator=' ' '(?x)
^(?=.*?\s(port=\S+))
(?=.*?\s(authenticate=\S+))
(?=.*?\s(appID=\S+))'
(3つの属性すべてが存在する必要があります)。
とsed
:
sed 'G
s/[[:space:]]\(port=[^[:space:]]*\).*\n.*/&\1/
s/[[:space:]]\(authenticate=[^[:space:]]*\).*\n.*/& \1/
s/[[:space:]]\(appID=[^[:space:]]*\).*\n.*/& \1/
s/.*\n//'
これらの最後の2つは、属性が行の最初の単語ではないことを前提としています(これは、サンプルを考えると妥当な前提のようです)。
EDIT 3に準拠して、次のように各パラメーターの式をsed
作成すれば、それでも実行できると思いますs///
。
sed -nE 's/^(.*)(appID=[^[:blank:]]+\s)(.*)$/\2\t\1\3/ s/^(.*)(authenticate=[^[:blank:]]+\s)(.*)$/\2\t\1\3/
s/^(.*)(port=[^[:blank:]]+\s)(.*)$/\2\t\1\3/
T;=
s/^(([^[:blank:]]+\s+){,3}).*/\1/
p'
s
目的の出力順序に関して、式の順序が逆になっていることに注意してください。番号付けもスクリプトに埋め込まれ、前述のように出力行番号を出力し、必要なパラメーターのいずれかが実際に行に存在する場合にのみ行を出力します。AFAIKがBSDに知られていないアトムをsed
使用しているので、GNU構文を利用していることにも注意してください。POSIX準拠の同等のものが可能かもしれませんが、さらに拡張される可能性があります。\d
sed
ただし、これはすでにひどく長く、出力するパラメーターが増えるとますます複雑になるためawk
、次のようなスクリプトの方が用途が広い可能性があります。
awk '
BEGIN {ac=ARGC; ARGC=0; OFS="\t"}
{
str=$0; NF=0
for (i=1; i<ac; i++)
if (match(str, ARGV[i]"=[^[:blank:]]*"))
$(NF+1)=substr(str, RSTART, RLENGTH)
}
NF {print ++nr; print}
' -- port authenticate appID
のawk
後にスクリプト自体への引数として、出力する正確なパラメーターとその出現順序を指定し--
ます。このスクリプトも、必要なパラメーターの少なくとも1つが実際に行に存在する場合にのみ、行を出力します。
入力にname = valueのペアがあるときはいつでも、最初にそのマッピング(f[]
)を保持する配列を作成するのが最善だと思います。次に、名前で値にアクセスできます。例:
$ awk -F'[ =]' '{ for (i=2;i<NF;i+=2) f[$i]=$i"="$(i+1)
print f["port"], f["authenticate"], f["appID"]
}' file
port=1234 authenticate=true appID=dummyAppId1
port=1244 authenticate=false appID=dummyAppId2
port=1235 authenticate=true appID=dummyAppId3
同様の問題を抱えている他のユーザーを助けることができる場合は、Rubyを使用した(詳細な)提案:
# passing the log file as parameter
lines = File.open(ARGV[0]).read.split("\n")
lines.each_with_index do |line, i|
words = line.split(' ')
output = []
puts i + 1
output << words.select { |w| w =~ /port=\d+/ }
output << words.select { |w| w =~ /authenticate=\w+/ }
output << words.select { |w| w =~ /appID=\w+/ }
puts output.join(' ')
end