다음과 같은 텍스트가 있다고 가정해 보겠습니다(의 출력 objdump -d
).
0: 0f a2 cpuid
2: a9 01 00 00 00 test eax,0x1
7: 74 01 je a <myFunc+0xa>
9: c3 ret
a: 0f 0b ud2a
^ +[0-9a-f]+:
(길이를 유지하기 위해) 해당 공백 수로 텍스트를 바꾸고 싶지만오직이전 부분이 :
다른 곳에서 언급되지 않은 경우(단어, 즉 단어 경계 내에 포함됨) 예를 들어, 위의 예에서 태그 0
, 2
, 7
는 9
공백으로 대체되고 변경되지 않은 상태로 유지됩니다 a
(세 번째 줄에서 언급한 대로).
처리 후 위의 예는 다음과 같습니다.
0f a2 cpuid
a9 01 00 00 00 test eax,0x1
74 01 je a <myFunc+0xa>
c3 ret
a: 0f 0b ud2a
태그 발생 횟수를 계산한 다음 해당 횟수에 따라 줄을 처리하는 것보다 shell/vim에 더 좋은 방법이 있습니까?
현재 코드는 2300행 파일을 3분 안에 처리하는데(Intel Atom CPU에서) 너무 깁니다.
#!/bin/bash -e
if [ $# -ne 2 ]; then
echo "Usage: $0 infile outfile" >&2
exit 1
fi
file="$1"
outfile="$2"
cp "$file" "$outfile"
labelLength=$(sed -n '/^ \+\([0-9a-f]\+\):.*/{s@@\1@p;q}' "$file"|wc -c)
replacement=$(printf %${labelLength}c ' ')
sed 's@^ \+\([0-9a-f]\+\):.*@\1@' "$file" | while read label
do
if [ $(grep -c "\<$label\>" "$file") = 1 ]; then
sed -i "s@\<$label\>:@$replacement@" "$outfile"
fi
done
답변1
펄 솔루션:
$ perl -lne '$k{"$_:"}++ for split(/\b/); push @l,$_; }{
map{s/\S+:/$k{$&}<2 ? " " x length($&) : $&/e; print}@l;' file
0f a2 cpuid
a9 01 00 00 00 test eax,0x1
74 01 je a <myFunc+0xa>
c3 ret
a: 0f 0b ud2a
이는 다음과 같습니다.
#!/usr/bin/perl
use strict;
my %wordsHash;
my @lines;
## Read the input file line by line, saving each
## line as $_. This is what 'perl -n` means.
while (<>) {
## Remove trailing newlines. This is done
## by -l in the one liner.
chomp;
## Split the current line on word boundaries
my @wordsArray=split(/\b/);
## Save each word + ":" as a key in the hash %wordsHash,
## incrementing the value by one each time the word
## is seen.
foreach my $word (@wordsArray) {
$wordsHash{"$word:"}++;
}
## Add the line to the array @lines
push @lines, $_;
}
## After the file has been read. ('}{' in the one-liner)
## Iterate over each line in @lines ( map{}@l in the one-liner)
foreach my $line (@lines) {
## Grab the first set of non-whitespace
## characters until the 1st ':'
$line=~/\S+:/;
## $& is whatever was matched
my $match=$&;
## If the match was seen only once
## as a word (all will be seen at least once)
if ($wordsHash{$match}<2) {
## The replacement is as many spaces
## as $match has characters.
my $rep = " " x length($match);
## Replace it in the line
$line=~s/$match/$rep/;
}
## Print the line
print "$line\n";;
}
답변2
줄 사이를 읽으면서 점프 및 유사한 명령에서 참조하지 않는 줄의 주소를 제거하여 분해를 더 읽기 쉽게 만들고 싶다고 가정합니다. 마지막 열이 "<"로 시작하면 awk는 마지막 열의 숫자가 주소라고 가정합니다. 디스어셈블리를 한 번 읽고 배열에 있는 모든 주소를 기억한 다음 주소가 나타나지 않으면 두 번째로 줄 시작 부분의 주소를 바꿉니다.
$ objdump -d /bin/ls >/tmp/a
$ awk '
NR==FNR { if($NF ~ /</) address[$(NF-1)] = 1; next }
$1 ~ /:/ { if(!address[substr($1,1,length($1)-1)]){
i = index($0,":")
printf "%*s%s\n",i," ",substr($0,i+1)
next }
}
{ print }
' /tmp/a /tmp/a