This is VERY clearly a case for a recursive descent solution:
$ cat tst.awk
function descend(node) {return (map[node] in map ? descend(map[node]) : map[node])}
{ map[$1] = $2 }
END { for (key in map) print key, descend(key) }
$ awk -f tst.awk file
If infinite recursion in your input is a possibility, here;s an approach that will print as the 2nd field the last node before the recursion starts and put a "*" next to it so you know it's happening:
$ cat tst.awk
function descend(node, child, descendant) {
stack[node]
child = map[node]
if (child in map) {
if (child in stack) {
descendant = node "*"
}
else {
descendant = descend(child)
}
}
else {
descendant = child
}
delete stack[node]
return descendant
}
{ map[$1] = $2 }
END { for (key in map) print key, descend(key) }
.
$ cat file
$ awk -f tst.awk file
*
*
*
*
If you need the output order to match the input order and/or or to print duplicate lines twice, change the bottom 2 lines of the script to:
{ keys[++numKeys] = $1; map[$1] = $2 }
END {
for (keyNr=1; keyNr<=numKeys; keyNr++) {
key = keys[keyNr]
print key, descend(key)
}
}