แยกหลายรูปแบบจากบรรทัดโดยไม่คำนึงถึงลำดับ
ฉันยังใหม่กับการเขียนสคริปต์ 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 (เช่นไม่ผ่านการรวมเส้น X เป็น 1)
แก้ไข 2: ผสมลำดับของพารามิเตอร์ในอินพุต
แก้ไข 3: ฉันขอโทษฉันควรจะพูดถึงเรื่องนี้ก่อนหน้านี้ - perl
ดูเหมือนว่าจะถูก จำกัด ในเครื่องที่ฉันใช้งาน แม้ว่าคำตอบของ Stephane และ Sundeep จะทำงานได้อย่างสมบูรณ์แบบเมื่อฉันทดสอบในเครื่อง แต่ก็ไม่สามารถใช้งานได้กับเครื่องที่ฉันต้องการเพื่อให้ทำงานได้ในที่สุด ดูเหมือนว่า awk, grep และ sed จะเป็นตัวเลือกที่รองรับเป็นหลัก :(
คำตอบ
ด้วย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+))'
(อันนั้นต้องมีคุณสมบัติทั้งสามอย่าง)
ด้วยsed
:
sed 'G
s/[[:space:]]\(port=[^[:space:]]*\).*\n.*/&\1/
s/[[:space:]]\(authenticate=[^[:space:]]*\).*\n.*/& \1/
s/[[:space:]]\(appID=[^[:space:]]*\).*\n.*/& \1/
s/.*\n//'
สองคำสุดท้ายนี้ถือว่าแอตทริบิวต์ไม่ใช่คำแรกของบรรทัด (ซึ่งดูเหมือนเป็นการสันนิษฐานที่สมเหตุสมผลสำหรับตัวอย่างของคุณ)
เมื่อปฏิบัติตาม 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
นิพจน์ตามลำดับเอาต์พุตที่ต้องการ การกำหนดหมายเลขยังฝังอยู่ในสคริปต์การพิมพ์หมายเลขบรรทัดเอาต์พุตตามที่คุณกล่าวถึงและจะพิมพ์บรรทัดต่อเมื่อพารามิเตอร์ที่ต้องการมีอยู่ในบรรทัด ยังทราบว่าฉันใช้ประโยชน์จาก GNU sed
ไวยากรณ์ที่คุณได้ใช้\d
อะตอมซึ่ง AFAIK จะไม่เป็นที่รู้จัก sed
BSD อาจเป็นไปได้ที่เทียบเท่ากับ POSIX แต่มีแนวโน้มที่จะขยายตัวได้มากขึ้น
อย่างไรก็ตามมันยาวมากแล้วและจะซับซ้อนขึ้นเรื่อย ๆ เมื่อเพิ่มพารามิเตอร์ในเอาต์พุตดังนั้น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
สคริปต์เองหลังจากไฟล์--
. สคริปต์นี้จะพิมพ์บรรทัดก็ต่อเมื่อมีพารามิเตอร์ที่ต้องการอย่างน้อยหนึ่งพารามิเตอร์อยู่ในบรรทัดเท่านั้น
เมื่อใดก็ตามที่มีคู่ชื่อ = ค่าในอินพุตฉันคิดว่าดีที่สุดในการสร้างอาร์เรย์ที่เก็บการแม็พนั้น ( 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
หากสามารถช่วยผู้ใช้รายอื่นที่มีปัญหาคล้ายกันได้ข้อเสนอ (verbose) โดยใช้ 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