17

While creating a bash script, I found that this code ls puts all files on one line:

pi@raspberrypi:~/ptlrestinterface$ ls
update.sh   web.config   MyApp.runtimeconfig.json   

still ls | head -n1 still prints only the first file:

pi@raspberrypi:~/ptlrestinterface$ ls | head -n1
update.sh

I would expect it to output the entire line, not the first file.

While piping the output through hexdump, I saw ls always put out a 0a after each entry, however on the console, it places them next to each other.

Apparently ls has some knowledge of the console (or the console of ls). How does this work, and where could I find documentation on this behaviour?

2
  • 1
    @Gribouillis My bad. The point is not about the -t option. This also works without any options (I updated the answer). My point is that the |head -n1 takes the first filename, and not the entire line. Commented Apr 29 at 13:52
  • try : ls -C | head -n 1 # should work?. ls | something # it notices stdout is not a terminal but another command, so it will default to one entry per line. ls -C : will force multiple entry per lines. Commented May 2 at 11:11

2 Answers 2

32

It calls the isatty() libc function on its stdout file descriptor to determine whether the output goes to a terminal or not.

Internally, isatty() attempts to use one of the ioctl() calls that are specific to tty devices – most likely ioctl(TCGETS) (which retrieves the generic tty settings that the stty command shows); if it succeeds, then the file is a tty device.

$ python
>>> sys.stdout.isatty()
True

This affects both whether ls will columnate its output by default, and whether it will use colors by default, and some other things such as whether file names will be quoted.

If stdout is indeed a terminal, then ls uses another tty-specific ioctl(TIOCGWINSZ) to retrieve its dimensions (which on modern systems have been stored there by the terminal emulator) to find out how many columns it can fit. The stty size command also gets those; try running it under strace.

Full-screen programs additionally react to the SIGWINCH signal which the kernel sends every time the terminal emulator updates the dimensions of the tty, as an indication that they need to redraw everything in the new size.

3
  • Thanks, do you perhaps also have a reference to documentation about this? The manpage mentions some about formatting for terminal, but not that it will output per line when it is not a terminal. Commented Apr 30 at 9:09
  • 1
    For the Linux version it's in the coreutils texinfo page info ls – see 3rd paragraph. For most BSDs, it's in the regular manpage under the -C option and a bit lower under the -1 option. The POSIX specification (also included with Linux as the ls(1p) manpage man 1p ls) requires "The default format shall be to list one entry per line to standard output; the exceptions are to terminals or[…]" Commented Apr 30 at 9:49
  • 2
    FWIW, in a script, one could similarly call test -t 1 to see if stdout is connected to a terminal. (Or test -t 0 or tty -s to check the same for stdin, though that may not be as useful)
    – ilkkachu
    Commented May 1 at 17:11
10

For a less technical explanation, observe what happens when you try these two commands:

$ ls
update.sh   web.config   MyApp.runtimeconfig.json

$ ls | cat
update.sh
web.config
MyApp.runtimeconfig.json

Piping through cat "does nothing", except that ls "notices" that it's output is being piped somewhere. So it changes its behavior accordingly.

Once you understand what ls is outputting, it's straightforward what happens when it passes through head -n1.

3
  • 7
    True. Using cat as a relay is a standard method to get output specific to non-terminals. The opposite is harder. Commented Apr 30 at 6:49
  • 2
    This tells what ls does, but that appears to be what they already knew. This doesn't tell how ls knows to do what it does, which was what they asked.
    – ilkkachu
    Commented May 1 at 17:09
  • @ilkkachu is correct, and I noticed this after I wrote it. But about a third of the audience is finding it helpful (as I did when I figured this out) so I'll leave it. Also the o.p. didn't seem quite sure just what ls was outputting so this helps to clear that up.
    – gbarry
    Commented May 1 at 23:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .