0

I'm making a simple irc bot with netcat. The first part is:

#! /bin/bash
# nc -c ./connect.sh irc.rizon.net 6667
tee /dev/stderr | {
        sleep 5
        echo "USER testbot * * :test"
        echo "NICK testbot"
        read line ; echo $line | sed  -e "s/^PING/PONG/"
        sleep 3
        echo "JOIN #channel"
} | tee /dev/stderr

Running this on a nc instance connecting to my own nc server works exactly as expected, however connecting to irc.rizon.net gives:

~/programs/testbot# nc -c ./connect.sh irc.rizon.net 6667
:irc.mufff.in NOTICE * :*** Looking up your hostname...
:irc.mufff.in NOTICE * :*** Checking Ident
:irc.mufff.in NOTICE * :*** Couldn't look up your hostname
:irc.mufff.in NOTICE * :*** No Ident response
USER testbot * * :test
NICK testbot
:irc.mufff.in NOTICE connect.sh file1.txt file2.png file3.py :*** Looking up your hostname...
PING :29785872
:irc.mufff.in 451 testbot :You have not registered
JOIN #channel
:irc.mufff.in 451 testbot :You have not registered

...where connect.sh file1.txt file2.png file3.py is the list of files in the directory I ran the commmand in.

is_trout
  • 1
  • 1
  • 2
    You should quote variables: `echo "$line"` to prevent bash from interpreting wildcards and stuff. – LMC Apr 24 '22 at 16:02
  • 1
    @LMC thanks this helped, and from it I managed to work out the other problem too. I'll post an answer. – is_trout Apr 24 '22 at 19:51

1 Answers1

0

There were two issues. One, I thought read read from the last stdin entry, but it starts at the beginning, meaning that the $line was actually :irc.mufff.in NOTICE * :*** Looking up your hostname..., not PING :29785872.

As pointed out in a comment there were no quotation marks around my variable meaning that bash interpreted stuff in it. I don't know exactly how that lead to listing directory contents but that was the issue. The new code goes:

#! /bin/bash
# nc -c ./connect.sh irc.rizon.net 6667
tee /dev/stderr | {
        sleep 5
        echo "USER testbot * * :test"
        echo "NICK testbot"
        sleep 3
        echo "JOIN #channel"


        while read line; do
                case "$line" in
                        PING*)
                        echo "$line" | sed  -e "s/^PING/PONG/"
                        ;;
                esac
        done&
} | tee /dev/stderr

Which works

is_trout
  • 1
  • 1
  • Consider `while IFS= read -r line; do` to keep your line exactly as-is, without munging backslashes or leading or trailing whitespace. And as a rule, avoid starting shell pipelines inside an inner loop -- starting an external tool like `sed` has a high startup cost, even though those tools are fast to run after they've finished starting up. If you're only doing a line at a time, it's much more efficient to use bash itself for your string-manipulation logic. – Charles Duffy Apr 24 '22 at 20:06
  • f/e, `echo "${line/#PING/PONG}"` will do the same thing you're using `sed` for now -- see https://wiki.bash-hackers.org/syntax/pe#search_and_replace – Charles Duffy Apr 24 '22 at 20:06
  • In this case, I don't know why you need a loop at all. Why not just let your whole stream feed into `sed -e 's/^PING/PONG/'` so that one copy of `sed` handles _all_ your input lines, instead of starting a new copy of `sed` for each line of input you read? – Charles Duffy Apr 24 '22 at 20:08
  • ...beyond that, what's the point of putting the `while read` loop in the background? You can't _really_ background it while it's behaving as a pipeline element (if you want what it writes to reach the last `tee /dev/stderr`, then `tee` can't exit until after the `while read` loop does, so the command as a whole can't be considered complete, so what was the point of the `&`?) – Charles Duffy Apr 24 '22 at 20:09