104

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
John
  • 3,599
  • 4
    What is a situation in which you really want a column of ls output? – michas Nov 18 '13 at 07: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 '13 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 '15 at 16:28
  • grep -l "" * (small hack) – Olav Oct 14 '21 at 20:21

11 Answers11

143

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

ls -1 /directory
SharpC
  • 359
ch0ps
  • 1,473
  • 3
    But it doesn't give you just the one column the OP is interested in. – Anthon Mar 16 '15 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 '16 at 18:47
  • @KWubbufetowicz ls | sed -n '1p;q' seems simpler – FelixJN Feb 22 '16 at 14:46
  • 14
    The answer I was looking for, but not for the question of OP. – Michael Lang Feb 01 '17 at 08: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. – squarecandy Aug 04 '20 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 '22 at 15:47
46

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.

Chris Down
  • 125,559
  • 25
  • 270
  • 266
  • 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. – Stéphane Chazelas Feb 22 '16 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 '16 at 18:19
  • ls -C | awk '{ print $1 } looks give same output ... why $2 ? – boctulus Jan 17 '19 at 04:55
19

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}'
tkrennwa
  • 3,525
  • 1
  • 15
  • 17
  • This may not do what you want it to if your filenames contain spaces or other whitespace characters. – Kusalananda Oct 24 '20 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 '20 at 13:29
12

I think you are looking for

ls -1

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

muru
  • 72,889
Niti
  • 589
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.

A Roebel
  • 123
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
abalter
  • 261
  • This works perfectly regardless of terminal width and filename/foldername length. If you want only directories, use ls -d */ | cat. – matty Feb 21 '23 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.

  • 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 '20 at 11:47
  • 1
  • 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 '20 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.

prusswan
  • 135
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.

agc
  • 7,223
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!

mh-cbon
  • 143