I have a very simple chat-like tool that runs within a GNU screen session. The screen window is split, the top part is running tail -f file.txt and the bottom part is running a script with the following content:

while : ; do
        read -p "Message: " msg
        ctime=$(date +"%H:%M:%S")
        echo "[${ctime}] User: ${msg}" >> file.txt

Very simple, but gets the job done with the requirements I have. There's only one problem: When I press the ESC or any of the arrow keys, it inserts an escape-sequence, like ^[[D for example. And this messes up the file, resulting in terrible output.

So my question is simple: How can I escape the input from read so it's safe to write to the file?

I've tried echo "[${ctime}] User: ${msg}" | strings >> file.txt which made it a lot better, there were no big mess-ups anymore (e.g. nothing was overwritten or wrongly put out), but things are still not perfect (e.g. entering te^[[Dst would turn into te\n[Dst (the \n being an actual new line)).

  • @guillermochamorro Go ahead and post it, maybe me or someone else can find a way to improve on it! :)
    – confetti
    Mar 24 '20 at 19:15

How about a slightly different approach? Rather than remove the escape characters and sequences, you can allow users to use them to edit the input line with read -e.

If you want, you can take this even further by recording chat message history, like this:

read -e -p "Message: " msg
history -s "$msg"

With this, if someone makes a typo in a message, they can hit up-arrow, use left- and right-arrow to edit and fix the typo, then hit return to send the corrected message.

  • That is absolutely fantastic! I had no idea -e is a thing. This even allows ctrl+arrow-key to skip words and keys like ESC or F-keys simply don't write anything now. One question about the history thing: How exactly does that work? I've never heard about this other than related to the .bash_history | I'm marking this as the accepted answer, even though it doesn't exactly filter escape sequences like asked, this is a way more elegant and compact solution.
    – confetti
    Mar 24 '20 at 19:53
  • 1
    @confetti It's the same mechanism as bash's regular command history, but it won't be loaded from or saved in .bash_history unless you do this explicitly with history -r and history -w respectively. If you do want to save the entry history between chat sessions, you could put them at the beginning and end of the script (but I'd recommend changing the HISTFILE variable to something other than ~/.bash_history, so the chat history doesn't get mixed with your command history). Mar 24 '20 at 20:06
  • That is amazing, thanks for teaching me something new and great.
    – confetti
    Mar 24 '20 at 20:28

Strip any non-printing character from the message string:

while read -p 'Message: ' message; do
        printf '[%s] User: %s\n' "$( date +%T )"  "${message//[^[:print:]]/}"
done >>file.txt

The parameter substitution ${message//[^[:print:]]/} would expand to the value of the variable message with any non-printable character removed. In the POSIX locale, the printable characters are the alphanumerical characters, the punctuation characters, and space ([[:alnum:][:punct:] ], basically).

  • Thank you, this works. Also thanks for the %T hint! This still prints the [D part of arrow keys, I guess there's no way around this? It doesn't matter much, your answer at least doesn't insert the newlines and for me that is good enough this way.
    – confetti
    Mar 24 '20 at 19:43
  • @confetti You could possibly delete the extra characters somehow, but doing so reliably would probably require knowing more about what the escape codes special keys generate than what I do.
    – Kusalananda
    Mar 24 '20 at 19:46
  • My assumption is that it may be just plain [D as text. Since this is within a screen session, things are a bit more weird than they should be, but it's fine like this so no worries! :)
    – confetti
    Mar 24 '20 at 19:48
  • @confetti Note that the function keys generates strings like OQ or [20~, and it could so it would be difficult to remove fixed strings. But as you said, at least you now only get printable characters in the output.
    – Kusalananda
    Mar 24 '20 at 19:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.