0

The question makes very little sense but lets get into it! I am working on a IRC Client in bash and I found some code and started adding it to an IRC bash bot code I have been working on. I have broken it down to simplify it. GNU bash, version 4.3.48(1)-release

#!/bin/bash
#!/user/bin/perl

function status_line() {
    echo -en '\e7' "\e[${status_line_row};0f" '\e[2K'
    echo -en "\e[4;44mSTATUS: $nick in $channel @ $channel\e[0m"
    echo -en '\e8'
}

function read_line() {
while true
do
    while read -r -t1
    do 
        status_line
        echo -en '\e[2K\r> '
        echo "PRIVMSG $channel :$REPLY" >> $input   
    done
done
}

. bot.properties
input=".bot.cfg"
echo "Starting session: $(date "+[%y:%m:%d %T]")">$log 
echo "NICK $nick" > $input 
echo "USER $user" >> $input
echo "JOIN $channel" >> $input

read_line | tail -f $input | openssl s_client -connect $server:6697 

Upon running this and typing input into terminal it does correctly input and send $REPLY to IRC but it does not correctly do status_line and echo -en '\e[2K\r> ' inside read_line. I am confused why the input is being read but the 2 lines before it aren't working. I ran bash -x on it to see this (below) after sending a msg to IRC and this doesn't even fix it without -x.

+ status_line
+ echo -en '\e7' '\e[;0f' '\e[2K'
+ echo -en '\e[4;44mSTATUS: Omen in #lair @ #lair\e[0m'
+ echo -en '\e8'
+ echo -en '\e[2K\r> '
+ read -r -t1
+ true

If you wish to see the full code it is here > https://pastebin.com/cHLiwZNf

10
  • It probably works if you remove the tail -f, right?
    – Kusalananda
    Apr 24, 2021 at 7:14
  • @Kusalananda Nope can not seem to even connect to the IRC if I remove tail -f. :/
    – Hugo Slop
    Apr 24, 2021 at 7:34
  • 1
    Oh, I see now, your read_line writes to $input. You will have to make everything write directly to openssl. The issue is with buffering. I think Linux has some sort of setbuf command that may help (to turn off buffering), but I'm currently unable to test.
    – Kusalananda
    Apr 24, 2021 at 7:43
  • 1
    You are using the wrong language for this job. Shell is good at running and orchestrating other programs that do things. It is not good at doing things itself. In this case, you should use expect instead of bash. Or, better yet, a language like perl or python which have expect-like libraries AND libraries with binding to openssl C library functions.
    – cas
    Apr 24, 2021 at 9:25
  • @Kusalananda, you're thinking of stdbuf Apr 24, 2021 at 14:49

3 Answers 3

1
function read_line() {
    ...
    while read -r -t1; do
        status_line
        echo -en '\e[2K\r> '
        echo "PRIVMSG $channel :$REPLY" >> $input   
    ...
}

read_line | tail ...

it does not correctly do status_line and echo -en '\e[2K\r> ' inside read_line. I am confused why the input is being read but the 2 lines before it aren't working.

Inside read_line, you read from stdin with read, and you print to stdout with echo. (And also to $input with the other echo, but that's not a problem.)

Noting how you're calling the read_line function as part of the pipeline, consider where the stdin and stdout of read_line are connected? What does the pipeline do to the input and output streams of the commands involved? What does tail do with its stdin?


Also note that if that > is supposed to be a prompt for the user, it should probably be printed before the read call, not after it. Either do while echo -en '\e[2K\r> '; read -r -t1; do ..., or use read -p to have it print a prompt instead. If the terminal is set up in the usual way, the screen will also scroll both when the user hits enter on the read, and when you print something. If the statusbar is at the top of the screen, it will scroll out of sight. You'll have to do something about that, and it's much easier with a pre-existing UI library like ncurses than fixing up the terminal processing by hand in the shell.

Another problem with an IRC client is that it needs to be able to read from two places at the same time: both input from the user, and data from the network. Fixing the output from openssl back to the script for processing is somewhat awkward in itself, and having to read both at the same time is worse.

The real solution to that is to use the select() system call (or poll()). Even though Bash provides access to network sockets via the /dev/tcp pseudo-files, I don't think it provides select().

So, really, there's two big reasons there to use an actual programming language and not the shell for this.

Of course, if you're only doing this as an exercise in insanity, then that's fine, but if so, I suggest getting a bit more familiar with the shell language first before tacking a problem like this. Also, if you just want to have fun with programming in a limited environment without any real-world significance, I could suggest a few games from Zachtronics for that.

0

Thanks to @Kusalananda, their answer sent me searching and I found the answer. If the answer they gave was right thanks again anyways sorry lol! Anyways adding >&2 at the end of lines I wanted to be printed to the terminal! Still working with it and making it right for my case but it works!!

function read_line() {
while true
do
    while read -r -t1 REPLY
    do 
        echo 'Hello World' >&2
        echo -en '\e[2K\r> ' >&2
        echo "PRIVMSG $channel :$REPLY" >> $input   
    done
done
}
-1

First, there's a lot to unpack here. If you haven't started using shellcheck, then you should start - it makes life much easier by highlighting defects on writes.

Second, the shell does NOT like this line: echo -en '\e7' "\e[${status_line_row};0f" '\e[2K'. I'm not sure what it's supposed to do but it causes the cursor to jump up one line and write a blank(?); that can't be what you're after. Fix this first.

Next, within the context of this block, the while true bit seems like you don't need it.

function read_line() {
while true; do <- unnecessary
    while read -r -t1
    do 
        status_line
        echo -en '\e[2K\r> '
        echo "PRIVMSG $channel :$REPLY" >> $input   
    done
done <- unnecessary
}

# This should do exactly the same thing but you have to feed the loop
while read -r -t1; do 
    status_line
    echo -en '\e[2K\r> '
    echo "PRIVMSG $channel :$REPLY" >> $input   
done < /from/file, or
done <<< "$fromVariable" (pick one from file or variable)

Lastly, here is a gist that should help to outline how colorize output much easier. I call it as a library and reuse over multiple scripts. PS: I hate the solution but it works.

1
  • Why would the shell "not like" that echo -en '\e7' ... command? The quotes are fine, giving multiple arguments to echo is fine. With -e, the \e gets turned to the ESC character, and it prints three different terminal control commands. (namely, save cursor position, move cursor, and erase line. In combination with the rest of the function it makes sense, the only thing being that the spaces added by echo between the arguments move the cursor two spaces forward from the start of the column)
    – ilkkachu
    Apr 25, 2021 at 10:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .