Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash: Get modified `ls`-like output (multiple entries per line)

When you call ls without additional options on the command line, you get multiple entries per line.

Contrived example:

$ ls mydir/*.tar.gz
mydir/foo.tar.gz        mydir/bar.tar.gz        mydir/baz.tar.gz

But if you want to modify the output in some way, you get one entry per line:

$ ls mydir/*.tar.gz | sed "s/\.tar\.gz//g"
mydir/foo
mydir/bar
mydir/baz

With many entries, that becomes cumbersome -- I would like the multiple, aligned columns back.

I was trying to come up with something along the lines of find mydir -name "*.tar.gz" -printf ..., or ... -exec ..., or even (yuk!) for file in $(find ...), but anything I could come up with reeks of being really inelegant (think counter variables, taking modulo and just guessing terminal width...).

Is there some elegant way to get a couple of filenames in a multiple-column output like with vanilla ls, after having tinkered with them (via e.g. sed, basename etc.)?

like image 521
DevSolar Avatar asked Mar 10 '15 14:03

DevSolar


3 Answers

The (surprisingly named) column command will do what you want.

ls | column -c 80

This will display the output in columns for an 80-wide display. You might have to wait for the output as it has to figure out the width of the items before it can format anything.

It will work with any command.

You can find your column width with the COLUMNS environment variable (this might depend on your shell - type set to see a list of variables and figure it out), or using the tput cols command.

like image 89
rghome Avatar answered Nov 18 '22 11:11

rghome


ls -C forces the multi-column output even when the output is directed away from the console.

like image 28
rojomoke Avatar answered Nov 18 '22 13:11

rojomoke


Update and disclaimer:

  • This answer does NOT work for the OP, because he wants to modify ls output first, and then columnate it; thus, columnation must be applied in a separate pass, later, which is what the accepted answer offers via column -c <n>.
  • This answer only works if you want to use columnated ls output as is.
    • Aside from that, it provides some potentially interesting background information on how ls decides how to format its output and what max. line width it assumes.

To build on rojomoke's promising answer, which points out that -C explicitly requests multi-column output from ls:

tl;dr

To emulate ls's multi-column terminal output when sending something to a file or through a pipe using the current terminal's max. line width (number of single-character display columns), use COLUMNS=$(tput cols) ls -C; e.g.:

COLUMNS=$(tput cols) ls -C | cat  # `| cat` is an example command to show pipe behavior

Obviously, you can also pick a fixed number independently of the current terminal; e.g., COLUMNS=120 ls -C ...

Without the COLUMNS=... prefix, columnation occurs based on a max. line width of 80, irrespective of the terminal window's actual width.


Note: The following is in part speculative. Do let me know if I'm wrong.

ls, when outputting to a terminal:

  • produces columnated (multi-column) output by default (-C is implied)
  • determines the terminal's max. line width itself.[1]

By contrast, when ls's stdout is connected to a pipe or output file, it

  • defaults to line-by-line output (-1 is implied)
  • when -C is specified, it takes the line width from the COLUMNS environment variable (NOT the shell variable); if there's no such variable, the default is 80.

In bash shells, $COLUMNS is typically a shell variable set in the interactive shell only, not an environment variable - in other words: $COLUMNS is defined, but not exported.

To get ls to respect the $COLUMNS variable, you must define it as an environment variable.

The cleanest approach is to prepend an ad-hoc, command-local environment-variable definition to the invocation of ls (the general pattern is: <envVar>=<value> <commmand> ...):

Since tput cols returns the line width for the current terminal, we get:

COLUMNS=$(tput cols) ls -C ...

tput cols also works in scripts, so it's the robust approach; interactively, the seemingly pointless command COLUMNS=$COLUMNS ls -C would work too.

Note that ls will even respect the COLUMNS environment variable when outputting to the terminal.


[1] That is, it doesn't need environment variable COLUMNS, but respects it, if defined. Note that a (non-exported) shell variable named COLUMNS - as is the typical case in interactive shells - is ignored.

like image 1
mklement0 Avatar answered Nov 18 '22 11:11

mklement0