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.)?
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.
ls -C
forces the multi-column output even when the output is directed away from the console.
Update and disclaimer:
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>
.ls
output as is.
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:
-C
is implied)By contrast, when ls
's stdout is connected to a pipe or output file, it
-1
is implied)-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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With