Home > Enterprise >  echo not printing \033 correctly in pipeline started by os.system()
echo not printing \033 correctly in pipeline started by os.system()

Time:07-03

In bash (as started by Python), I want to print this string \033[31m so that I can use a pipe | operator after it, followed by a command to copy that string to the clipboard. This means that in practice, I'm trying to run something like:

os.system('echo \\033[31m | xsel -ib')

...but the xsel -ib part is working fine, so this question is focused specifically on the behavior of echo.


Most of my attempts have been similar to:

echo -e \\033[31m

I have tried it with single quotes, double quotes, no quotes, removing the -e flag, etc. The closest I got was:

echo -n "\\ 033[31m"

which prints this string \ 033[31m

I don't want that space between \ and 0

-n flag is used to not append a new line after the printed string


I use Ubuntu 20.04, and xsel is a selection and clipboard manipulation tool for the X11 Window System (which Ubuntu 20.04 uses).

CodePudding user response:

echo is the wrong tool for the job. It's a shell builtin, and one for which the POSIX sh standard explicitly does not guarantee portable behavior for when escape sequences (such as \033) are present. system() starts /bin/sh instead of bash, so POSIX behavior -- not that of your regular interactive shell -- is expected.

Use subprocess.run() instead of os.system(), and you don't need echo in the first place.

If you want to put an escape sequence into the clipboard (so not \033 but instead the ESC key that this gets converted to by an echo with XSI extensions to POSIX):

# to store \033 as a single escape character, use a regular Python bytestring
subprocess.run(['xsel', '-ib'], input=b'\033[31m')

If you want to put the literal text without being interpreted (so there's an actual backslash and an actual zero), use a raw bytestring instead:

# to store \033 as four separate characters, use a raw string
subprocess.run(['xsel', '-ib'], input=rb'\033[31m')

For a more detailed description of why echo causes problems in this context, see the excellent answer by Stephane to the Unix & Linux Stack Exchange question Why is printf better than echo?.


If you for some reason do want to keep using a shell pipeline, switch to printf instead:

# to store \033 as four separate characters, use %s
subprocess.run(r''' printf '%s\n' '\033[31m' | xsel -ib ''', shell=True)
# to store \033 as a single escape character, use %b
subprocess.run(r''' printf '%b\n' '\033[31m' | xsel -ib ''', shell=True)
  • Related