4

I've been searching for this for a while and didn't get a satisfactory answer for it.

I've taken this screenshot from the output of Gnu/Linux's tree command.

enter image description here

I want to know how to draw or print lines like these, I've tried reading the source code of the tree program but didn't understand anything.

Mahmoud Farouq
  • 151
  • 1
  • 6

5 Answers5

7

You can do this by using the table of characters in terminfo(5) (man 5 terminfo) starting with "Line Graphics"

    Glyph                       ACS            Ascii     acsc     acsc
    Name                        Name           Default   Char     Value
    ────────────────────────────────────────────────────────────────────
    arrow pointing right        ACS_RARROW     >         +        0x2b
    arrow pointing left         ACS_LARROW     <         ,        0x2c
    arrow pointing up           ACS_UARROW     ^         -        0x2d
    arrow pointing down         ACS_DARROW     v         .        0x2e
    solid square block          ACS_BLOCK      #         0        0x30
    diamond                     ACS_DIAMOND    +         `        0x60
    checker board (stipple)     ACS_CKBOARD    :         a        0x61
    degree symbol               ACS_DEGREE     \         f        0x66
    plus/minus                  ACS_PLMINUS    #         g        0x67
    board of squares            ACS_BOARD      #         h        0x68
    lantern symbol              ACS_LANTERN    #         i        0x69
    lower right corner          ACS_LRCORNER   +         j        0x6a
    upper right corner          ACS_URCORNER   +         k        0x6b
    upper left corner           ACS_ULCORNER   +         l        0x6c
    lower left corner           ACS_LLCORNER   +         m        0x6d
    large plus or crossover     ACS_PLUS       +         n        0x6e
    scan line 1                 ACS_S1         ~         o        0x6f
    scan line 3                 ACS_S3         -         p        0x70
    horizontal line             ACS_HLINE      -         q        0x71
    scan line 7                 ACS_S7         -         r        0x72
    scan line 9                 ACS_S9         _         s        0x73
    tee pointing right          ACS_LTEE       +         t        0x74
    tee pointing left           ACS_RTEE       +         u        0x75
    tee pointing up             ACS_BTEE       +         v        0x76
    tee pointing down           ACS_TTEE       +         w        0x77
    vertical line               ACS_VLINE      |         x        0x78
    less-than-or-equal-to       ACS_LEQUAL     <         y        0x79
    greater-than-or-equal-to    ACS_GEQUAL     >         z        0x7a
    greek pi                    ACS_PI         *         {        0x7b
    not-equal                   ACS_NEQUAL     !         |        0x7c
    UK pound sign               ACS_STERLING   f         }        0x7d
    bullet                      ACS_BULLET     o         ~        0x7e

As an example

tput smacs
printf "%s\n%s\n" 'lqqqqqk' 'mqqqqqj'
tput rmacs

Output

┌────┐
└────┘

Observations

I notice that on my system, not all the characters listed actually map as described. For example

echo '+ , - . 0 ` a f g h i j k l m n o p q r s t u v w y x z { | } ~'
tput smacs
echo '+ , - . 0 ` a f g h i j k l m n o p q r s t u v w y x z { | } ~'
tput rmacs

+ , - . 0 ` a f g h i j k l m n o p q r s t u v w y x z { | } ~
+ , - . 0 ♦ # ° ± n ↓ ┘ ┐ ┌ └ ┼ ⎺ ⎻ ─ ⎼ ⎽ ├ ┤ ┴ ┬ ≤ │ ≥ # ≠ £ ·
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 2
    Of course, if one's terminal emulator is like mosh and mine and does not support ISO 2022 character set switching, this approach does not work. See https://unix.stackexchange.com/a/289871/5132 and https://unix.stackexchange.com/a/506140/5132 . You've also discovered the pitfall of private use character sets (which is what you are using, you will find) not being standardized. Another answer's approach of emitting UTF-8 to a UTF-8 terminal emulator is the more modern, straightforward, and standard approach. – JdeBP Dec 31 '19 at 17:46
  • I believe that if the terminfo value for smacs is undefined one is (was) supposed to fall back to the column "Ascii Default". If that were applied to mosh and your own preferred terminal you would get a moderately-acceptable alternative. The real question is how to determine if UTF-8 unicode characters can be represented on the terminal device. – Chris Davies Jan 01 '20 at 21:11
  • Falling back to the "ASCII default" does not do what the questioner actually asked for, because it does not produce the output that the questioner showed. (It's a fair guess that the questioner would not have asked how to print a plus symbol.) Emitting the Unicode box drawing characters is what the questioner asked for, and for that UTF-8 is more modern, straightforward, and standardized than using ISO 2022 to switch to a private use character set. We already have https://unix.stackexchange.com/q/322833/5132 and others, note. – JdeBP Jan 01 '20 at 23:59
  • @JdeBP indeed, falling back to the "ASCII Default" doesn't do what the questioner asked. Rather, it was a response to your criticism that the ISO 2022 character set switching wasn't supported on certain terminals, and as such it's not part of my answer. – Chris Davies Jan 02 '20 at 13:18
  • But one can print an ASCII plus sign instead. is not really a good response to Your ISO 2022 method of emitting the desired characters will not work, because ISO 2022 is passé to the point that it has been decried for over 20 years and some modern softwares do not even implement it, and what you are doing is not even a standardized use of ISO 2022. – JdeBP Jan 03 '20 at 15:45
  • @JdeBP which terminfo (or termcap) attribute allows my program to determine whether or not the target device supports UTF-8 or Unicode? – Chris Davies Jan 03 '20 at 17:13
  • I ran into problems trying to use these characters in a screen session, but some digging around showed that it (and presumably other terminals) need a tput enacs first, before tput smacs will work. These days, UTF-8 is better, though it's unclear how to know if a terminal supports it. I think the general practice is to check locale variables and see if ".UTF-8" is appended. – mulad Sep 08 '22 at 15:19
4

It requires special box-drawing characters, as commented by another user. In the terminal, it is possible to call them via the printf command. For example, to recreate the first two lines of the tree example in the question, it would look like:

printf "\x1b(0\x74\x1b(B\x1b(0\x71\x1b(B\x1b(0\x71\x1b(B info\n"
printf "\x1b(0\x78\x1b(B   \x1b(0\x6d\x1b(B\x1b(0\x71\x1b(B\x1b(0\x71\x1b(B exclude\n"

For a list of the box-drawing characters, see the wikipedia page.

  • can you please explain what does \x1b(0 mean or interpret to?, i've tried to print it alone and it changed how things viewed in my terminal, so what does it really mean? – Mahmoud Farouq Dec 31 '19 at 16:46
  • 1
    That's really a question in its own right. And we already have https://unix.stackexchange.com/q/506123/5132 and several others in that vein. – JdeBP Dec 31 '19 at 17:27
  • 1
    This should not be the accepted answer - it depends way to much on terminals caps w'o even noting it... – jerch Jan 01 '20 at 21:54
3

It just uses uni-code characters: Unicode is about 2^24 characters. It includes ASCII as its first 128 characters, then goes on to include characters from all languages (including maths) including some fictitious ones, drawing characters, emoji, and more.

You can look up the characters in the uni-code drawing characters (e.g. https://en.wikipedia.org/wiki/Box-drawing_character#Unicode ), or just copy the ones from the example.

Here are the same characters. They don't render as well on this site (probably the font). However it is the same text. If you paste back into terminal, it will look the same.

├── media
│   ├── cdrom
│   ├── other

Other answers give an alternate way to do it. I don't know the pros and cons of each. It would be nice to hear them.

1

I don't have tree installed, but I get the same effect from cfdisk. I pasted the output from cfdisk into od (octal dump). These symbols are just multibytes from my locale, which is LANG=en_GB.UTF-8.

This is a vertical bar with a branch to the right, followed by a horizontal continuous line: 342 224 234 342 224 200

This is a down/right corner, followed by the same horizontal: 342 224 224 342 224 200

Go to this URL:

www.ltg.ed.ac.uk/~richard/utf-8.cgi

and type in the octal 342 224 224

Or here:

www.obliquity.com/computer/html/unicode2500.html

You would need to Google for "convert Unicode Code Point to UTF-8".

Also try: echo -e '\0342\0224\0234\0342\0224\0200'

Paul_Pedant
  • 8,679
  • 1
    Although UTF-8 is the better way to do this, compared to ISO 2022 in other answers, this answer is somewhat lacking. It needs to explain that the terminal needs to be in UTF-8 mode. And we don't need Google. This very WWW site has answers to questions such as https://unix.stackexchange.com/q/252286/5132 . – JdeBP Dec 31 '19 at 17:54
0

Although other answers already pointed you to box drawing characters, you should be careful using hardcoded terminal sequences in your app on your own as given in those answers. Note that there might be terminals that simply dont understand these sequences and will output some rubbish instead.

If you dont understand anything of this, either restrain from using those sequences or use a library, thats capable of handling terminal awkwardities in a uniform way, like ncurses. Those libraries also have a primitive for drawing a horizontal line (with proper fallbacks).

If you are still keen to get this done yourself and without the help of a terminal DB like libterminfo, you have the following possibilities:

  • lookup whether the terminal you want to address supports all the sequences + characters to send (if not, find some replacement), repeat that for every terminal you want to support
  • if your target terminal(s) support Unicode (UTF-8) you might get away with directly inserting the corresponding Unicode chars (test whether sending U+2500 shows up correctly)
jerch
  • 164
  • Neither ncurses nor terminfo will solve this. See https://unix.stackexchange.com/q/322833/5132 . – JdeBP Jan 02 '20 at 00:11
  • @JdeBP Thats not true - ncurses has default chars for some box drawing derived from the alt charset. The other question is about requesting the active encoding of a terminal - which is not possible programmatically. – jerch Jan 02 '20 at 05:05
  • One needs to know the encoding to know "if your target terminal supports Unicode"; and for reasons already explained beneath https://unix.stackexchange.com/a/559711/5132 , the ncurses fallback is not what the questioner is asking about (given that xe almost certainly would not have asked how to emit a plus sign) and terminfo's encapsulation of ISO 2022 character set switching does not work with modern terminal emulators that do not support it. – JdeBP Jan 02 '20 at 09:48
  • 1
    @JdeBP Is this some sort of a crusade? You are commenting almost the same stuff under every answer. I am well aware of issues with character sets and Unicode, but thats not the question here (its more part of my answer). Again - if you dont know anything about terminal mechanics use a lib that tries to level out differences. This will result in better "terminal portable" code. If a "modern emulator" does not support what ncurses returns, its a problem of that emulator not implementing the interface it is mimicking. – jerch Jan 02 '20 at 11:42
  • I've explained on one answer, and pointed to that explanation from here. Repeating your error does not make it true, moreover. For reasons already explained, by Thomas Dickey, libraries will make no difference here. You should also read Markus Kuhn and others on the subject of ISO 2022, which I also pointed to. The problem is ISO 2022, not the emulators. – JdeBP Jan 03 '20 at 15:35
  • Which "error" do you mean? – jerch Jan 03 '20 at 16:06
  • 1
    @JdeBP This was a serious question, guess you like the downvote more... – jerch Jan 03 '20 at 16:51