순서에 관계없이 라인에서 여러 패턴 추출
저는 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
원하는 출력 순서와 관련 하여 표현식의 순서가 반대임을 유의하십시오 . 번호 매기기도 스크립트에 포함되어 언급 한대로 출력 행 번호를 인쇄하며 원하는 매개 변수 중 하나가 실제로 행에있는 경우에만 행을 인쇄합니다. 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
애프터 스크립트 자체 --
. 이 스크립트도 원하는 매개 변수 중 하나 이상이 실제로 행에있는 경우에만 행을 인쇄합니다.
입력에 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