10

I would like to print a check mark and a cross mark in a shell script:

#!/bin/bash

echo -e "\xE2\x9C\x94 existing"
echo -e "\xE2\x9D\x8C missing"

Why isn't this working?

11
  • can you check this post unix.stackexchange.com/questions/311904/…
    – Kamaraj
    Feb 10 '17 at 8:41
  • Still don't see what I am doing wrong... Feb 10 '17 at 8:47
  • Your two commands work perfectly fine on mksh shell, but in bash the first one outputs question mark instead of check. Might be something to do with bash's implementation of echo. You could always switch to python's print as alternative command Feb 10 '17 at 8:49
  • If I am executing the command directly in terminal it is working as expecting. But when executing this commands in a sh-script I get the pure output \xE2\x9C\x94 exiting... Feb 10 '17 at 8:51
  • In that case, how are you execiting the file ? did you put #!/bin/bash there or is it #!/bin/sh ?Can you try printf "\xE2\x9C\x94 existing" instead of echo in the script ? Feb 10 '17 at 8:53
14

As revealed by the OP in the comments, they were calling script with sh file.sh. Depending on the default shell to which /bin/sh is symlinked it might not support unicode characters.

For instance, on Ubuntu , the default shell is dash.

$ dash
$ printf "\xE2\x9C\x94 missing\n"
\xE2\x9C\x94 missing
$ echo -e "\xE2\x9C\x94"
-e \xE2\x9C\x94

The reason why it worked when you called the command in interactive shell, is because users interactive shell is by default (on Ubuntu) /bin/bash

To properly run the script, you need to either:

  • run it as ./file.sh
  • run it as argument to proper shell bash file.sh

Alternatively, one could resort to shell-independent methods:

# this printf is standalone program, not shell built-in
$ /usr/bin/printf "\xE2\x9C\x94 check mark\n"
✔ check mark

$ python -c 'print "\xE2\x9C\x94 check mark"'
✔ check mark

$ perl -e 'print "\xE2\x9C\x94 check mark"'                                                                              
✔ check mark
2
  • in Python you can provide the name of the character and skip the messy/implicit UTF-8 encoding (which I think breaks anyways in Python 3), e.g. print("\N{HEAVY CHECK MARK}")
    – Nick T
    Apr 24 '19 at 22:17
  • @NickT Does look neater. I tested that on couple different Python versions just now, it works in Python 3.7.3 but doesn't seem to work in 2.7.16 or 2.6, where as python -c 'print "\xE2\x9C\x94 check mark"' still works on 2.6 I suppose if one is using Python3 that's the way to go - otherwise using the encoding is the best way one's got Apr 24 '19 at 22:37
10

Note that \xE2\x9C\x94 is the UTF-8 encoding of the U+2714 (heavy check mark) character.

Those 3 bytes will only be displayed as a check mark if the terminal's character set is UTF-8 (and uses a font that has that character).

For terminal emulators, the charset they use will typically be the one in the locale at the time they were started. Unless you have changed the locale within the shell started in that terminal, you can tell which one it was with:

locale charmap

Several printf implementations including GNU printf and the printf builtin of zsh, bash and lksh (a more POSIX compatible variant of mksh on Debian-based systems at least) support:

$ printf '\u2714\u274c\n'
✔❌

To print those characters in the correct encoding for the locale's charset (ksh93's printf builtin also supports that \uXXXX notation but always outputs in UTF-8 regardless of the locale's charset). Shells whose printf builtin support it and have an echo that expands escape sequences (possibly with -e) typically also support \uXXXX there.

Now, AFAICT, the only two charsets where those two U+274C and U+2714 characters are available on a typical GNU system are UTF-8 and GB18030. In locales using different charsets, printf will not be able to display those characters as they simply don't exist. Depending on the implementation, printf will print \u274C literally or fail with an error.

Some shells (zsh where it originated, bash, ksh93, mksh, FreeBSD sh) also support that \uXXXX notation in their $'...' quotes.

So you can do:

echo $'\u2714\u274c'

Depending on the shell, those will be expanded to the locale's encoding that was in effect at the time the command was parsed (bash) or at the time it was run (zsh) or in UTF-8 always (ksh).

POSIXly (portably among Unix-like systems), if you want to print arbitrary sequences of bytes, you'll want to use printf and the octal notation.

\xE2\x9C\x94 (the UTF-8 encoding of U+2714) would be printed on a line portably with:

printf '\342\234\224\n'

And if you wanted it to be converted to the correct encoding for the locale, that would be:

printf '\342\234\224\n' | iconv -f UTF-8

POSIX doesn't specify which character encoding may be supported on a system, nor their name, but the above command would typically work on POSIX systems that support the UTF-8 encoding.

3

Using raw "cut and paste" Unicode we get a more readable solution:

$ echo -e '☑ done\n☒ fail\n☐ to do'
☑ done
☒ fail
☐ to do

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.