I have a number of *.s assembly files. I would like to count how many instructions they contain. For example, to count all mul instructions I do:
cat *.s | grep -P '\t'"mul" | wc -l
which gives me a compound number of all mul instructions.
I would like to have an output like:
mul: 893
add: 12054
sub: 2356
...
The problem is 开发者_高级运维that there is no table of instructions supported by a target platform. Each target platform has different set of instructions. The supported instructions must be deduced from existed assembly files. Can I do it in Bash only without Perl/Python foundry?
You can get all of them like this:
grep -hP '^[ \t]+[a-z]*' *.s | sed 's/^[ \t]\+\([a-z]*\).*/\1/' | sort | uniq -c
Edit: I changed this to work with the example provided by SiegeX.
Output:
1 call
6 mov
1 push
1 ret
2 syscall
2 xor
Assuming your instructions are all indented from the other code by use of white space (tab is OK), then this awk
1-liner will work:
awk '/^[[:space:]]+/{a[$1]++}END{for(op in a){print op,a[op]}}' /path/to/*.s
Input
$ cat asm.s
section .text
global _start, write
write:
mov al,1 ;write syscall
syscall
ret
_start:
mov rax,0x0a68732f6e69622f
push rax
xor rax,rax
mov rsi,rsp
mov rdi,1
mov rdx,8
call write
exit: ; just exit not a function
xor rax,rax
mov rax,60
syscall
Output
$ awk '/^[[:space:]]+/{a[$1]++}END{for(op in a){print op,a[op]}}' ./*.s
push 1
xor 2
ret 1
mov 6
syscall 2
call 1
Bash 4 without any external utilities:
declare -A c;while IFS=$'\n' read -r t;do [[ ! $t =~ ^[[:space:]] ]] && continue;w=($t);((c[${w[0]}]++));done<asm.s;for op in ${!c[@]};do echo $op ${c[$op]};done
To do multiple files, wrap the while
loop in a for f in *.s
loop with the while's done
like this: done < "$f"
or do the while's done
like this: done < <(cat *.s)
instead of the for f
.
Slightly shorter to count the first word on each line without regard to indentation:
declare -A c;while read -ra t;do [[ $t ]]&&((c[${t[0]}]++));done<asm.s;for op in ${!c[@]};do echo $op ${c[$op]};done
Still, it's longer than the other answers.
Just for the hell of it, SiegeX's awk answer in Perl
perl -n '/^\s+(\w+)/&&$a{$1}++;END{print map{"$_ $a{$_}\n"}keys%a}' ./*.s
Because "anything you can do I can do in a manner which more closely resembles line noise" (-:
grep -Eo '\w+' /source.file | uniq -c
精彩评论