我绝对是Shell脚本的初学者。 我的任务是创建一个脚本,它将显示文件中使用的函数(调用者和被调用者)。 我用objdump,grep,awk等来获得这个输出:
000000000040090d <usage>:
000000000040095d <failure>:
400970: e8 98 ff ff ff callq 40090d <usage>
000000000040097f <strton>:
4009bc: e8 9c ff ff ff callq 40095d <failure>
00000000004009c6 <main>:
400a0e: e8 6c ff ff ff callq 40097f <strton>
400a26: e8 32 ff ff ff callq 40095d <failure>
400a41: e8 39 ff ff ff callq 40097f <strton>
400a59: e8 ff fe ff ff callq 40095d <failure>
400a9a: e8 be fe ff ff callq 40095d <failure>
400aae: e8 cc fe ff ff callq 40097f <strton>
400ac2: e8 b8 fe ff ff callq 40097f <strton>
400ad1: e8 87 fe ff ff callq 40095d <failure>
400afe: e8 fe 01 00 00 callq 400d01 <set_timeout>
400b1c: e8 3c fe ff ff callq 40095d <failure>
400b26: e8 19 00 00 00 callq 400b44 <print_fib_upto>
400b37: e8 89 00 00 00 callq 400bc5 <print_ackermann>
好的,结果应如下所示:
failure -> usage
strton -> failure
main -> failure
main -> print_ackermann
main -> print_fib_upto
main -> set_timeout
main -> strton
但我不知道如何实现它。我知道怎么用C等做,但不是这里。我认为这是正确的伪代码。
If (end of line == ">:")
caller == last column;
while (end of line == ">") {
callee == last column;
echo "$caller -> $callee"
}
谁能告诉我,怎么用BASH写这个?非常感谢,也许这是一个愚蠢的问题,但我还不知道关于shell的任何问题。
答案 0 :(得分:4)
例如:
#!/bin/bash
while read -r line
do
case "$line" in
*:) caller=$line; continue;;
*) echo "$caller $line";;
esac
done < <(sed 's/.*</</' < caldata) | sed 's/[<>]//g;s/:/ -> /' | sort -u
产生
failure -> usage
main -> failure
main -> print_ackermann
main -> print_fib_upto
main -> set_timeout
main -> strton
strton -> failure
答案 1 :(得分:2)
您可以使用awk:
awk -F'[<>:]+' 'NF==3{p=$(NF-1); next} {print p, "->", $(NF-1)}' file
failure -> usage
strton -> failure
main -> strton
main -> failure
main -> strton
main -> failure
main -> failure
main -> strton
main -> strton
main -> failure
main -> set_timeout
main -> failure
main -> print_fib_upto
main -> print_ackermann
答案 2 :(得分:2)
我认为使用awk
可能会更容易,但我将您的伪代码直接解释为bash。
while read; do # If not given an argument `read` puts the line into `REPLY`
case $REPLY in # Use a case expression for pattern matching.
*>:) caller="${REPLY##*<}"
caller="${caller%>:}" # Parameter expansions to strip off unwanted parts of the line.
;;
*>) callee="${REPLY##*<}"
callee="${callee%>}"
;;
esac
echo "$caller -> $callee"
done
答案 3 :(得分:2)
您可以试试awk
:
$ awk 'NF==2 {
gsub(/[[:punct:]]/, "", $NF)
caller = $NF
next
}
{
gsub(/[[:punct:]]/, "", $NF)
map[caller,$NF]++
}
END {
for(calls in map) {
n = split(calls, tmp, SUBSEP)
print tmp[1]" -> "tmp[2]
}
}' file
main -> printackermann
main -> settimeout
strton -> failure
main -> printfibupto
main -> failure
failure -> usage
main -> strton
或强>
$ awk '
NF==2 {
for(keys in map)
print map[keys]" -> "keys;
delete map
gsub(/[[:punct:]]/, "", $NF)
caller = $NF
next
}
{
gsub(/[[:punct:]]/, "", $NF)
map[$NF] = caller
}
END {
for(keys in map)
print map[keys]" -> "keys
}' file
failure -> usage
strton -> failure
main -> strton
main -> settimeout
main -> printackermann
main -> printfibupto
main -> failure
答案 4 :(得分:2)
整个事情可以合理地轻松完成sed
,无需使用grep
等进行预处理。以下内容并不完美(您可能需要调整正则表达式和什么是/不被视为匹配),但它应该打破它的背面。
#!/bin/bash
symbol='[_A-Za-z][_@A-Za-z0-9]\+'
objdump -d "$1" | sed -n "
/^[0-9a-f]\+ <\(${symbol}\)>:/{ # Match symbol in header
s//\1/ # Symbol only in pattern space
h # Save symbol to hold space
: loop # (until empty line)
n # Next line
/^$/!{ # If not an empty line
# Try matching 'callq' line and extract symbol:
/^[:[:space:][:xdigit:]]\+callq[^<]\+<\($symbol\)>/{
s//\1/ # Symbol only in pattern space
G # Append hold space to pattern space
# Swap words in pattern space, adding separator:
s/\($symbol\)[[:space:]]\($symbol\)/\2 -> \1/g
p # Print pattern space:
}
b loop # Try next line
}
}"
答案 5 :(得分:1)
已经回答了,但对于纯粹的bash,它可以按如下方式完成:
#!/bin/bash
while read line
do
[[ "$line" =~ \<(.*)\>\:$ ]] && caller=${BASH_REMATCH[1]}
[[ "$line" =~ \<(.*)\>$ ]] && callee=${BASH_REMATCH[1]}
[ -n $caller -a -n $callee ] && echo "$caller -> $callee";
callee=""
done < $1
[[ expr ]] &&
表示测试expr,如果为true,则运行之后的内容。 =~
是一个正则表达式匹配器,其中()
的值存储在${BASH_REMATCH[1]}
中。