Read the /help about the n and t switches, which should always be used unless you want it to try to evaluate $word and %word within the text file, and want it to check if line 1 is a number which replaces the true number of lines in a file. Also, what you're doing is telling it to search the entire file beginning at line %d for a match with *. Instead, just read the line text verbatim.
The echo was just a debugging tool to show yourself why something isnt working, and can be removed when you solve that issue. The echo would have shown you that, not only was the %a not defined correctly, but the # is not filled with a value either. Just like in the ON QUIT event, the ON NICK event does not define a channel name. ON NICK is triggered for any nick changing nick in any of your channels. If you want this to ban people from all channels, then you need to include a layer where you ban that person from each channel they share with you.
The /break is just a quick way to immediately exit the while() loop, and is the equivalent of jumping to the line following the while { } code, and you want it to break ONLY if there is a ban.
Since BAN can only be done by a nick who is halfop or higher, you should check if you've got the correct status in that channel, as well as whether that nick is in that channel too. I assume you don't want to get in a war by kicking them, including not wanting to kick yourself, so you want to make sure the newnick is either a regular user or a voice in that channel. Assuming the channelname is #foo:
on *:NiCK:{
if ( (!$nick(#foo,$newnick,rv)) || (!$nick(#foo,$me,oh&~))) return
var %d $lines(bads.txt)
while (%d >= 1) {
if ($read(bads.txt, nt,%d) isin $newnick) {
ban -ku77200 #foo $newnick kick message goes here
Break
}
dec %d
}
else { dec %d }
}
}