39

My goal is deceptively simple (at least to me). I wish to take the output of ls -l or ls -lh and select just one field.

I am looking for this to be as bulletproof as possible, by which I mean, assume that filenames can have a variable number of spaces, not everything in the field has the same length, etc.

Bonus points for having a script that will take the name of the the field (or even just a field number), and then return the contents of the field.

I want to turn

enter image description here

into:

enter image description here

4 Answers 4

64

Try ls -l | awk '{print $7}'.

awk selects columns so it's perfect for this task.

6
  • 4
    As explained by @ormaaj: Do not rely on pieces of information being available in specific places with ls. The find command is tremendously helpful. Jul 18, 2012 at 10:02
  • @PaddyLandau How do I use the find command to print a file size in megabytes like I can with ls -l --block-size=M? Jul 4, 2019 at 18:08
  • @Benjamin — The ls command is not POSIX standard, so a script that depends on ls could break on different systems or even over time. See the answer by @ormaaj on this page for more details. One way to do what you ask is with a Bash loop. Here is an example. for FILENAME in *; do FILESIZE=$(( $( stat --format=%s "${FILENAME}" ) / 1024 )); echo "${FILENAME}" ${FILESIZE}; done. Of course, instead of echo, you'd use your processing. Jul 5, 2019 at 7:30
  • @PaddyLandau I just ended up piping the output of the find command to ... | xargs -d '\n' du -m which works just as well. Jul 5, 2019 at 16:13
  • @Benjamin You might want to look at the -exec option of find. Jul 5, 2019 at 19:08
25

Never parse ls. Use GNU find. Or if portability isn't important, stat(1).

find . -maxdepth 1 -printf '%Td\n'

For reading data other than lists of filenames line-by-line and splitting into fields, see: BashFAQ/001

There are no methods to reliably read a newline-delimited list of filenames that make sense under most circumstances.

3

You may fetch the specific column in shell like:

ls -al | while read perm bsize user group size month day time file; do echo $day; done

or awk as shown in @Corey answer, cut -c44-45 would also work after adjustment (since ls has fixed columns) , or whatever else, however the main problem is that it won't be reliable and bulletproof (e.g. on Unix it may be $6, not $7, and it changes depending on arguments) making it not machine-friendly therefore it is not recommended to parse ls command at all.

The best is to use different available commands such as find or stat, which can provide relevant options to format the output as you need. For example:

$ stat -c "%x %n" *
2016-04-10 04:53:07.000000000 +0100 001.txt
2016-04-10 05:08:42.000000000 +0100 7c1c.txt

To return column of only days of modifications, try this example:

stat -c "%x" * | while read ymd; do date --date="$ymd" "+%d"; done

It's worth to note that GNU stat could have different options to BSD stat, so it still won't be bulletproof across different operating systems.

4
  • 1
    +1 for mention that ls should not be parsed as well as for use of stat . date however is setup incorrectly. You should add --date=$ymd , since date by itself will print the current day, but the purpose here is to convert date format of the file Apr 10, 2016 at 4:37
  • The ls -la | cut -c32-33 command in total honesty is just completely unreliable, not only because of the possible pitfalls with the filenames, but simply because it depends on the usernames' length and on the files' size. The stat -c "%x" *.* command looks ok, but by using *.* you're restricing it only to filenames containing a dot. I guess the intention was to catch also hidden files; in that case you should enable globbing for dotfiles beforehand and use * instead of *.*: shopt -s dotglob; stat -c "%x" * | [...].
    – kos
    Apr 10, 2016 at 4:50
  • Ah and what Serg said also, you should add --date="$ymd" to the date command, otherwise it will print the current day of the month.
    – kos
    Apr 10, 2016 at 5:01
  • Thanks for the comments. I've addressed the issues and provided a better initial example.
    – kenorb
    Apr 10, 2016 at 12:11
0

I need to catalog my installed applications prior to backing up and upgrading and encountered this issue trying to figure out how to trim ls output, but the awk applications suggested in other Ask Ubuntu questions didn't work at all.

What worked for me were suggestions to use cut: I redirected test ls output to a text file and used a "ruler" in Sumblime Text to find the character column numbers

total 2060
         1         2         3         4         5
123456789012345678901234567890123456789012345678901234567890
-rw-r--r-- 1 root    root      291 2023-04-12 13:53 apport-gtk.desktop
-rw-r--r-- 1 root    root      277 2023-04-12 13:53 apport-kde-mime.desktop
-rw-r--r-- 1 root    root     3417 2018-04-14 03:38 assistant-qt5.desktop
-rw-r--r-- 1 root    root     1066 2022-03-07 19:03 atom.desktop
-rw-r--r-- 1 root    root     2683 2018-05-01 04:10 audio-recorder.desktop
...

and then filtered with cut -c30-, where the trailing hyphen extends rendering to the end of (variable length) lines.

$ dir -l --time-style=long-iso /usr/share/applications | cut -c30-

  291 2023-04-12 13:53 apport-gtk.desktop
  277 2023-04-12 13:53 apport-kde-mime.desktop
 3417 2018-04-14 03:38 assistant-qt5.desktop
 1066 2022-03-07 19:03 atom.desktop
 2683 2018-05-01 04:10 audio-recorder.desktop
...

This "ruler check" is needed since ls's field alignment is relative after the first access permissions field. For the specific issue here (isolating the day of month) we can identify columns 38 and 39 as our targets.

$ ls -l >> cutcheck.txt
$ cat cutcheck.txt
         1         2         3         4         5
123456789012345678901234567890123456789012345678901234567890
total 480
-rwxrwx--- 1 root plugdev    112 Mar 12 17:36 cutcheck.txt
-rwxrwx--- 1 root plugdev    112 Mar 12 17:34 cutruler.txt
drwxrwx--- 3 root plugdev  32768 Mar 10 17:53 KDEAppLaucherConfig
-rwxrwx--- 1 root plugdev 195385 Mar  2 18:02 KDEAppLaucherMenuEditorHandbk.pdf
-rwxrwx--- 1 root plugdev   2202 Mar 11 18:30 KDEAppLauncherFiles.txt
-rwxrwx--- 1 root plugdev  40346 Mar  5 21:10 KDEApps.ods
-rwxrwx--- 1 root plugdev  90993 Mar  5 20:12 KDEMenuAppsDesktopFiles.html
$ ls -l | cut -c38-39

12
12
10
 2
11
 5
 5

Once you know the "ballpark" character column values for your particular file system, you can issue succesive cut commands to isolate what you need to extract.

The stat command allows specifying particular ls/dir output fields, but the neat ls column alignment is lost because of the variable field lengths. The application of awk given in @Corey Whitaker's answer did work, but with the same loss of column alignment.

I think stat is the better choice if the intention is direct import into a spreadsheet catalog because the field codes can be separated with commas for redirection to a CSV file.

You must log in to answer this question.

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