저는 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의 너비가 0인 뒷모습 어설션 기능을 이런 식으로 사용할 수 있었습니다.
grep -Po '(?<=pattern1=)[^ ,]+|(?<=pattern2=)[^ ,]+|(?<=pattern3=)[^ ,]+|(?<=pattern4=)[^ ,]+' filename
그러나 이것은 새 줄의 각 줄에 대한 출력을 제공하는 것 같습니다.
1234
true
dummyAppId1
grep을 사용하여 한 줄에 배치하는 방법을 알아내려고 합니다(즉, X 줄을 1로 병합하지 않음).
편집 2: 입력의 매개변수 순서를 혼동함
편집 3: 죄송합니다. 앞서 언급했어야 했는데, perl
제가 작업하는 컴퓨터에서는 제한적인 것 같습니다. Stephane과 Sundeep이 제공한 답변은 로컬에서 테스트했을 때 완벽하게 작동했지만 궁극적으로 실행하는 데 필요한 컴퓨터에서는 작동하지 않았습니다. awk, grep 및 sed가 주요 지원 옵션인 것 같습니다.
답변1
사용 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 $.
줄 번호에 대해- 부분 일치를 피하려면 단어 경계가 충분할 경우 etc를 사용하십시오
\bport
.\bappID
그렇지 않으면(?<!\S)(port=[^ ]+)
공간 기반 제한이 사용됩니다.
포함된 행만 인쇄해야 하거나 appID
그러한 조건이 다른 경우 다음 -lpe
으로 변경 -lne
하고 다음 print $.
으로 변경하십시오.print "$.\n$_" if /appID/
답변2
의 경우 perl
다음과 같이 사용할 수 있습니다.
perl -lne 'my %h;
$h{$1} = $& while /(\S+?)=(\S+)/g;
print "@h{qw(port authenticate appID)}"'
키가 속성 이름이고 값이 s인 해시 테이블을 만들고 name=value
원하는 것을 인쇄할 수 있습니다.
값만 출력하고 싶다면 $&
로 바꾸세요.$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//'
마지막 두 개는 속성이 줄의 첫 번째 단어가 아니라고 가정합니다(이는 샘플에서 합리적인 가정처럼 보입니다).
답변3
편집 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
원하는 출력 순서를 기준으로 표현식의 역순을 확인하세요. 번호 매기기는 스크립트에도 포함되어 있습니다. 언급한 대로 줄 번호를 인쇄하고 필요한 인수 중 하나가 실제로 줄에 존재하는 경우에만 줄을 인쇄합니다. 또한 여러분 sed
은 \d
BSD가 sed
. 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
.--
답변4
유사한 문제를 겪고 있는 다른 사용자에게 도움이 된다면 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