12

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?

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
user3142695
  • 1,529
  • 7
  • 20
  • 34
  • can you check this post http://unix.stackexchange.com/questions/311904/how-do-i-print-an-ascii-character-by-different-code-points-in-bash – Kamaraj Feb 10 '17 at 08:41
  • Still don't see what I am doing wrong... – user3142695 Feb 10 '17 at 08: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 – Sergiy Kolodyazhnyy Feb 10 '17 at 08: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`... – user3142695 Feb 10 '17 at 08: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 ? – Sergiy Kolodyazhnyy Feb 10 '17 at 08:53
  • As you can see in the post I am using `#!/bin/bash` and it doesn't make a difference using `printf` instead of `echo` – user3142695 Feb 10 '17 at 08:55
  • Also, small correction - the echo command that was outputting question mark for me had `\x93`. My own mistake there. Works fine from script and from interactive shell – Sergiy Kolodyazhnyy Feb 10 '17 at 08:56
  • I put this code into a sh-file, give it `chmod +x file.sh` and execute it by `sh file.sh` – user3142695 Feb 10 '17 at 08:57
  • @user3142695 well, there is your problem ! `sh` is a symlink to default shell , which is different on most systems. For example, on Ubuntu it's a symlink to `/bin/dash` . – Sergiy Kolodyazhnyy Feb 10 '17 at 08:58
  • Ok, I am using ubuntu. But changing to `/bin/dash` still keeps the same problem. :-( – user3142695 Feb 10 '17 at 09:02
  • 1
    @user3142695 You're not supposed to change to `dash`. It doesn't have support for what you're doing. See my answer. I need to sleep, so we can continue this discussion in about 7 hours – Sergiy Kolodyazhnyy Feb 10 '17 at 09:03
  • how do you make it green color – shorif2000 Apr 07 '22 at 11:45

3 Answers3

16

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
Sergiy Kolodyazhnyy
  • 16,187
  • 11
  • 53
  • 104
  • 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 – Sergiy Kolodyazhnyy Apr 24 '19 at 22:37
12

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.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
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
JJoao
  • 11,887
  • 1
  • 22
  • 44