102

Lets say when I do ls command the output is:

file1 file2 file3 file4

Is it possible to display only a certain column of output, in this case file2? I have tried the following with no success:

echo ls | $2

Basically all I want to do is echo only the second column, in this case, I want to echo:

file2
4
  • 4
    What is a situation in which you really want a column of ls output?
    – michas
    Nov 18, 2013 at 7:43
  • 16
    As a general rule, you should never parse ls. There are almost always better ways of getting the info you need.
    – terdon
    Nov 18, 2013 at 15:43
  • 1
    I wouldn't do that stranger :P (sorry I allways liked that phrase) the number of columns depend on the names files and folders have, so 2nd column may be the one you want, or may not, or may be in one ls and after creating some files/folders it may no longer be what you want. As terdon pointed out there sure are better ways to get what you want
    – YoMismo
    Mar 16, 2015 at 16:28
  • grep -l "" * (small hack)
    – Olav
    Oct 14, 2021 at 20:21

11 Answers 11

139

The following command will format the ls output into one column:

ls -1 /directory
6
  • 3
    But it doesn't give you just the one column the OP is interested in.
    – Anthon
    Mar 16, 2015 at 15:45
  • 2
    To make it correct : ls -1 /directory | head -2 | tail -1 or ls -1 /directory | perl -ne 'print if $. == 2'
    – MAQ
    Feb 10, 2016 at 18:47
  • 14
    The answer I was looking for, but not for the question of OP. Feb 1, 2017 at 8:56
  • 2
    It's the answer to what you would assume the overview question in the title means, and it has lots of votes and good answers, therefore is the top google hit, which keeps reinforcing that feedback loop. Aug 4, 2020 at 17:26
  • 2
    worth pointing out that -1 is minus digit-one not minus lower-case-L, as it can be a bit difficult to tell the difference when viewing it
    – Kip
    Apr 25, 2022 at 15:47
45

The most reliable way to do this is to put the files into an array, and get the second one, which avoids having to do any parsing at all:

files=(*)
printf '%s\n' "${files[1]}"

The order in which you get back the files depends on the value of LC_COLLATE. As such, you might want to set LC_COLLATE=C first, if you want a "standard" sorting in all corner cases.

3
  • 1
    Note that array indices start at 1 in sensible shells (zsh, yash, fish, csh, tcsh, rc, es...) and at 0 in ksh and bash. For a solution portable to all Bourne-like shells, you could use: set -- *; printf '%s\n' "$2" instead. Feb 22, 2016 at 15:36
  • @StéphaneChazelas At least for this question, the tags include "bash", so this answer is specific to that shell.
    – Chris Down
    Feb 22, 2016 at 18:19
  • ls -C | awk '{ print $1 } looks give same output ... why $2 ?
    – boctulus
    Jan 17, 2019 at 4:55
18

You would need to add -C as ls uses single-column mode when the output is not a terminal. awk then prints the second column:

ls -C | awk '{print $2}'
2
  • This may not do what you want it to if your filenames contain spaces or other whitespace characters.
    – Kusalananda
    Oct 24, 2020 at 15:18
  • 1
    Parsing ls is anyway not they way to go to fetch a filename for exactly this reason, but the question was about the 2nd column of that output. In general, find to the rescue!
    – tkrennwa
    Oct 25, 2020 at 13:29
11

I think you are looking for

ls -1

It won't show permission, owner, group, date, size... but simple file name in single column.

2
  • 1
    Not only is this answer wrong, but it's been given before. Jun 15, 2019 at 6:18
  • 3
    Although I realize this is wrong for the original question, it is what I was looking for. So, thank you. Oct 10, 2019 at 0:06
2

As already has been mentioned - using columns for parsing ls output is not very robust because ls breaks the lines according to your file name lengths and terminal width. It often is interesting though to display a single column of a list of elements. You can do this using the cut command:

echo file1 file2 file3 file4 | cut -d" "  -f2

will display

file2

Note that -d allows you to select the column delimiter, so for example with

cut -d, -f2

you can display the second column of a comma separated list.

0
2

I just stumbled on this that seems to work:

(base) balter@winmac:~/winhome/gdsc_bq_etl/large_files/harmonized$ ls | cat
cell_lines_metadata.tsv
drug_metadata.tsv
gdsc1_anova_all_tcga_results.tsv
gdsc1_anova_pancancer_results.tsv
gdsc1_drug_ic50_resuults.tsv
gdsc2_anova_all_tcga_results.tsv
gdsc2_anova_pancancer_results.tsv
gdsc2_drug_ic50_results.tsv
genetic_features_all_tcga_metadata.tsv
genetic_features_all_tcga_results.tsv
genetic_features_pancancer_metadata.tsv
genetic_features_pancancer_results.tsv
1
  • This works perfectly regardless of terminal width and filename/foldername length. If you want only directories, use ls -d */ | cat.
    – matty
    Feb 21 at 14:01
1

The only way works for me is adding "-al". That is:

ls -C -al | awk '{ print $1 }'

Because -al add more columns like user, permissions, etc.

3
  • 1
    I don't think there's an -all flag. I think that should just be -al which is combining -a for including files and folders that start with . (normally hidden) and -l which is "Long Format" and puts one entry per line, including file size and permissions.
    – Patrick
    Oct 23, 2020 at 11:47
  • 1
    ammended. Thanks Oct 24, 2020 at 14:28
  • Doesn't the -l option override -C? Then what is the question you're answering? The question here is how to get the second filename.
    – Kusalananda
    Oct 24, 2020 at 15:22
0
num_chars=54 # offset
ls -ltr | cut -c$num_chars-

change the number of characters (54) as necessary so you only get the data you want.

2
  • 2
    to clarify, using 54- tells cut to every character starting at position 54; it doesn't stop at "human-recognized" column
    – Jeff Schaller
    May 20, 2016 at 16:12
  • It's not a bad idea..just needs to be refined.
    – prusswan
    Aug 15, 2016 at 10:23
0

Suppose if you want to display only the particular filename under current path use below command

command:

find path -maxdepth 1 -iname "filename"|sed "s/\.\///g"
0

This is tricky, because parsing ls -C depends on:

  1. filename length, (which itself depends on the specified file list passed to ls -C)
  2. terminal width.
  3. the fact that ls -C output is columnar, but might be configured to varying numbers of spaces as field separators.
  4. the possibility of whitespace in filenames, (the display of which depends upon the particular ls display options chosen).

For now, let's start with the easier problem, (i.e. not #4 above), supposing that there's no whitespace in the filenames. To print column #2, this works:

COLUMNS=$COLUMNS ls -C | tr -s ' ' '\t' | cut -f2

Unrolling that:

  • COLUMNS=$COLUMNS guarantees that the column length fed to | remains consistent with the current terminal display. Changing this to an arbitrary value works too, so COLUMNS=50 or COLUMNS=70 would cause ls -C to rearrange things according to those widths.

    Another method would be to use the -w switch, e.g.:

    ls -w${COLUMNS} -C | tr -s ' ' '\t' | cut -f2
    
  • tr makes the field separators consistent: a single tab between each column.

  • cut outputs the desired column.

1
  • Whitespace method pending...
    – agc
    Jun 15, 2019 at 8:09
0

To me the best answer is

$ ls -1 ../* | tr -s '\n' '\n'
../main.go
../main_test.go
../abort:
main.go
../beego:
beego
conf
controllers
go.mod
go.sum
lastupdate.tmp
main.go
routers
views
../bucket:
main.go
main_test.go
../c:
../cc:
main.go
main_test.go

It is the only command I found that supports multi directory traversing without blank lines and that outputs exactly 1 column like OP asked, and i needed to count them using wc!

You must log in to answer this question.

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