Nandishwar Singh

Home / Learn Linux / Lesson 5

Listing files: ls, file types and timestamps

Read ls -l line by line, spot file types, and understand when a file was read, changed and modified.

Lesson
5 of 30
Time
About 35 minutes

On this page

  1. Reading ls -l
  2. The options you'll actually use
  3. Sorting by time
  4. File types
  5. Three timestamps
  6. Common mistakes
  7. Practice
  8. Cheat sheet

Reading ls -l

ls -l is the command you'll run more than any other, so it's worth being able to read every column. Here's one line:

$ ls -l /etc/hostname
-rw-r--r-- 1 root root 6 Oct 21 11:01 /etc/hostname

Left to right:

PartHereMeans
Type-A regular file. d is a directory, l a link (more below)
Permissionsrw-r--r--Who may read, write and run it (lesson 14)
Links1How many names point to this file (lesson 11)
OwnerrootThe user who owns it
GrouprootThe group it belongs to
Size6In bytes
DateOct 21 11:01When the contents last changed. Files older than six months show the year instead of the time
Name/etc/hostnameThe file

For a directory, the size is just the space its list of names takes up, usually 4096, not the size of what's inside. Lesson 25 shows how to measure that with du.

The total line at the top of a directory listing is the disk space the listed files use, counted in 1 KiB blocks. You can mostly ignore it.

Try it

  1. Who owns /etc/passwd, and how big is it?
  2. Who owns your home directory?
Answer
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 1778 Oct 21 11:33 /etc/passwd
$ ls -ld ~
drwxr-x--- 14 ubuntu ubuntu 4096 Oct 21 11:29 /home/ubuntu

root owns /etc/passwd. Your home directory belongs to ubuntu. Plain ls -l ~ would list what's inside it; -d lists the directory itself (see the next section).

The options you'll actually use

OptionDoes
-lLong format
-a / -AShow hidden files (with / without . and ..)
-hHuman sizes: 4.0K, 12M, 1.2G
-tNewest first
-rReverse whatever order you asked for
-SBiggest first
-dList a directory itself, not what's inside it
-RGo into every subdirectory too
-1One name per line
-FAdd a symbol showing each entry's type

They combine. ls -lh is the everyday version of ls -l:

$ ls -lh /var/log
total 1.9M
lrwxrwxrwx  1 root      root              39 Jul 20 16:33 README -> ../../usr/share/doc/systemd/README.logs
-rw-r--r--  1 root      root             13K Oct 21 11:21 alternatives.log
-rw-r-----  1 root      adm                0 Oct 21 11:01 apport.log
drwxr-xr-x  2 root      root            4.0K Oct 21 11:33 apt
-rw-r-----  1 syslog    adm             624K Oct 21 11:35 auth.log
...

-lS sorts by size, which is handy for finding what's filling a directory:

$ ls -lhS /var/log
total 1.9M
-rw-r-----  1 syslog    adm             624K Oct 21 11:35 auth.log
-rw-r-----  1 syslog    adm             471K Oct 21 11:35 syslog
-rw-r-----  1 syslog    adm             312K Oct 21 11:06 cloud-init.log
-rw-rw-r--  1 root      utmp            290K Oct 21 11:33 lastlog
...

-d matters when you want details about a directory instead of its contents:

$ ls -ld /etc /var/log
drwxr-xr-x 107 root root   4096 Oct 21 11:33 /etc
drwxrwxr-x  10 root syslog 4096 Oct 21 11:35 /var/log

You can list as many paths as you like in one go, and use wildcards: * matches anything, so /etc/*.conf means every name in /etc ending in .conf:

$ ls -l /etc/*.conf
-rw-r--r-- 1 root root  4027 Nov  4  2025 /etc/adduser.conf
-rw-r--r-- 1 root root  6430 Oct 21 11:03 /etc/ca-certificates.conf
-rw-r--r-- 1 root root  2967 Feb 16  2026 /etc/debconf.conf
-rw-r--r-- 1 root root  1706 Nov  4  2025 /etc/deluser.conf
-rw-r--r-- 1 root root  1289 Jan  6  2026 /etc/dhcpcd.conf
...

The shell expands the * into the matching names before ls even runs. Lesson 9 covers wildcards properly.

Try it

  1. Show /usr/bin with human sizes, biggest first, and only the first few lines.
  2. List every file in /etc whose name starts with host.
Answer
$ ls -lhS /usr/bin
total 171M
-rwxr-xr-x    1 root root     18M Sep  4 07:31  perf
-rwxr-xr-x  116 root root     11M Sep  3 08:01  coreutils
-rwxr-xr-x    1 root root    8.4M Mar 15  2026  bpftrace
-rwxr-xr-x    1 root root    7.2M Aug 20 10:41  python3.14
...
$ ls -l /etc/host*
-rw-r--r-- 1 root root  92 Apr 20  2026 /etc/host.conf
-rw-r--r-- 1 root root   6 Oct 21 11:01 /etc/hostname
-rw-r--r-- 1 root root 237 Oct 21 11:09 /etc/hosts
-rw-r--r-- 1 root root 411 Jul 20 16:36 /etc/hosts.allow
-rw-r--r-- 1 root root 711 Jul 20 16:36 /etc/hosts.deny

For the first one, ls -lhS /usr/bin | head -5 cuts it to five lines (lessons 6 and 8 explain head and |).

Sorting by time

-t puts the newest first. Add -r and the newest goes to the bottom, right above your prompt, which is where you want it when a directory has hundreds of files:

$ ls -ltr /var/log
total 1940
...
-rw-r-----  1 syslog    adm             161818 Oct 21 11:35 kern.log
-rw-r--r--  1 root      ubuntu           24576 Oct 21 11:35 wtmp.db
-rw-r-----  1 syslog    adm             481738 Oct 21 11:35 syslog
-rw-r-----  1 syslog    adm             638830 Oct 21 11:35 auth.log

ls -ltr is the classic first move when something just went wrong: the logs that changed last are the ones to read.

Try it

  1. Which file in /etc changed most recently?
Answer
$ ls -lt /etc
total 932
-rw-r--r-- 1 root root         56 Oct 21 11:33 subgid
...

The first file after total is the newest.

File types

The first character of each ls -l line tells you what kind of thing it is:

CharacterTypeExample
-Regular file/etc/hostname
dDirectory/etc
lSymbolic link (points to another path)/bin
cCharacter device (a stream of bytes)/dev/null, terminals
bBlock device (a disk)/dev/vda
sSocket (programs talk through it)/run/systemd/notify
pNamed pipeRare, lesson 8 mentions them

You can see several at once:

$ ls -ld /etc/hostname /etc /bin /dev/null /dev/vda
lrwxrwxrwx   1 root root      7 Apr 20  2026 /bin -> usr/bin
crw-rw-rw-   1 root root   1, 3 Oct 21 11:06 /dev/null
brw-rw----   1 root disk 253, 0 Oct 21 11:06 /dev/vda
drwxr-xr-x 107 root root   4096 Oct 21 11:33 /etc
-rw-r--r--   1 root root      6 Oct 21 11:01 /etc/hostname

ls -F shows the type as a symbol after each name instead: / for a directory, @ for a link, * for a file you can run, | for a pipe, = for a socket:

$ ls -F /
bin@   dev/  home/  lost+found/  mnt/  proc/  run/   snap/  sys/  usr/
boot/  etc/  lib@   media/       opt/  root/  sbin@  srv/   tmp/  var/

What's inside: the file command

Linux doesn't care about file extensions. A file called photo.txt can be a JPEG, and a program can have no extension at all. file looks at the contents and tells you what it really is:

$ file /etc/hostname /usr/bin/ls /etc /bin /dev/vda /usr/share/doc/bash/README.gz
/etc/hostname:                 ASCII text
/usr/bin/ls:                   symbolic link to ../lib/cargo/bin/coreutils/ls
/etc:                          directory
/bin:                          symbolic link to usr/bin
/dev/vda:                      block special (253/0)
/usr/share/doc/bash/README.gz: gzip compressed data, max compression, from Unix, original size modulo 2^32 4848

/usr/bin/ls turns out to be a link. On Ubuntu 26.04, ls, cat and dozens of other commands are all links to one program, the Rust coreutils, which checks the name it was started with and behaves as that command.

Use file whenever you're not sure whether something is text you can open, a program or an archive.

Try it

  1. What type of thing is /dev/null, and what's /dev/sda or /dev/vda on your machine?
  2. What kind of file is /usr/bin/tldr?
Answer
$ ls -l /dev/null /dev/vda
crw-rw-rw- 1 root root   1, 3 Oct 21 11:06 /dev/null
brw-rw---- 1 root disk 253, 0 Oct 21 11:06 /dev/vda
$ file /usr/bin/tldr
/usr/bin/tldr: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=723ebba5126c17c813e77e3e4d0ff5e036ec8585, for GNU/Linux 3.7.0, stripped

/dev/null is a character device (c) and /dev/vda a block device (b), the VM's disk. tldr is a compiled program. On an Intel or AMD PC it says x86-64 instead of ARM aarch64.

Three timestamps

Every file has three times, and ls -l shows only one of them:

Many filesystems also keep a birth time, when the file was created. stat shows them all. Make a practice directory and a file to play with (mkdir makes a directory, echo with > writes a file; lessons 7 and 8 cover both):

$ mkdir ~/ls-lab
$ cd ~/ls-lab
$ echo hello > note.txt
$ stat note.txt
  File: note.txt
  Size: 6               Blocks: 8          IO Block: 4096   regular file
Device: 253,1   Inode: 534053      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/  ubuntu)   Gid: ( 1000/  ubuntu)
Access: 2026-10-21 11:36:26.137964455 +0000
Modify: 2026-10-21 11:36:26.138028581 +0000
Change: 2026-10-21 11:36:26.138028581 +0000
 Birth: 2026-10-21 11:36:26.137964455 +0000

Now watch them move. Reading the file doesn't change mtime. Changing its permissions changes ctime only:

$ cd ~/ls-lab
$ cat note.txt
hello
$ stat --printf="access %x\nmodify %y\nchange %z\n" note.txt
access 2026-10-21 11:36:26.264965446 +0000
modify 2026-10-21 11:36:26.138028581 +0000
change 2026-10-21 11:36:26.138028581 +0000
$ chmod 600 note.txt
$ stat --printf="access %x\nmodify %y\nchange %z\n" note.txt
access 2026-10-21 11:36:26.264965446 +0000
modify 2026-10-21 11:36:26.138028581 +0000
change 2026-10-21 11:36:26.266965462 +0000

stat --printf prints only the fields you ask for: %x, %y and %z are access, modify and change, and \n starts a new line. stat -c does the same but always adds one line break at the end, which is simpler when you want a single field.

The access time moved when cat read the file, and chmod moved only the change time. Ubuntu doesn't update atime on every single read, because writing to the disk every time a file is read would slow everything down. It updates it only when the old access time is earlier than the modify or change time, or more than a day old. That rule is called relatime, and it's why atime is only a rough guide.

Changing timestamps: touch

touch sets a file's access and modify times to now. If the file doesn't exist, it creates an empty one, which is how most people use it (lesson 7). It can also set any time you like, which is useful for testing scripts that clean up "files older than 30 days":

$ cd ~/ls-lab
$ touch -d "2026-01-15 09:30" old.txt
$ touch new.txt
$ ls -l
total 4
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 new.txt
-rw------- 1 ubuntu ubuntu 6 Oct 21 11:36 note.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Jan 15  2026 old.txt

Seeing the other times with ls

ls sorts and shows mtime by default. -u switches it to atime and -c to ctime. Together with -t, they sort by that time:

$ cd ~/ls-lab
$ ls -lt
total 4
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 new.txt
-rw------- 1 ubuntu ubuntu 6 Oct 21 11:36 note.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Jan 15  2026 old.txt
$ ls -ltu
total 4
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 new.txt
-rw------- 1 ubuntu ubuntu 6 Oct 21 11:36 note.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Jan 15  2026 old.txt
$ ls -ltc
total 4
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 new.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 old.txt
-rw------- 1 ubuntu ubuntu 6 Oct 21 11:36 note.txt

Try it

  1. Make a file called report.txt dated 1 March 2026 at noon, and check it with ls -l.
  2. Which of the three times does chmod change?
  3. Print just the modify time of /etc/hostname.
Answer
$ cd ~/ls-lab
$ touch -d "2026-03-01 12:00" report.txt
$ ls -l report.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Mar  1  2026 report.txt
$ stat -c %y /etc/hostname
2026-10-21 11:01:58.431302254 +0000

chmod changes only ctime, because it changes the file's details, not its contents.

Common mistakes

Practice

  1. List /var/log with human sizes, the newest file at the bottom.
Answer
$ ls -lhtr /var/log
total 1.9M
drwxr-xr-x  2 root      root            4.0K May 29 18:58 dist-upgrade
...
-rw-r--r--  1 root      ubuntu           24K Oct 21 11:35 wtmp.db
-rw-r-----  1 syslog    adm             471K Oct 21 11:35 syslog
-rw-r-----  1 syslog    adm             624K Oct 21 11:35 auth.log
  1. In ~/ls-lab, make three files: a.txt dated 1 January 2026, b.txt dated 1 June 2026 and c.txt dated now. List them oldest first.
Answer
$ cd ~/ls-lab
$ touch -d 2026-01-01 a.txt
$ touch -d 2026-06-01 b.txt
$ touch c.txt
$ ls -ltr a.txt b.txt c.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Jan  1  2026 a.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Jun  1 00:00 b.txt
-rw-rw-r-- 1 ubuntu ubuntu 0 Oct 21 11:36 c.txt
  1. Show one line each for /etc, /etc/hostname, /bin and /dev/null, and say what type each one is from the first character.
Answer
$ ls -ld /etc /etc/hostname /bin /dev/null
lrwxrwxrwx   1 root root    7 Apr 20  2026 /bin -> usr/bin
crw-rw-rw-   1 root root 1, 3 Oct 21 11:06 /dev/null
drwxr-xr-x 107 root root 4096 Oct 21 11:33 /etc
-rw-r--r--   1 root root    6 Oct 21 11:01 /etc/hostname

d directory, - regular file, l link, c character device.

  1. Find out what kind of file /usr/share/common-licenses/GPL-3 is without opening it.
Answer
$ file /usr/share/common-licenses/GPL-3
/usr/share/common-licenses/GPL-3: ASCII text
  1. Print the size in bytes and the owner of /etc/passwd using stat -c. (%s is the size and %U the owner.)
Answer
$ stat -c "%s bytes, owned by %U" /etc/passwd
1778 bytes, owned by root
  1. Read note.txt in ~/ls-lab, then show which of its three times changed.
Answer
$ cd ~/ls-lab
$ stat --printf="access %x\nmodify %y\nchange %z\n" note.txt
access 2026-10-21 11:36:26.264965446 +0000
modify 2026-10-21 11:36:26.138028581 +0000
change 2026-10-21 11:36:26.266965462 +0000
$ cat note.txt
hello
$ stat --printf="access %x\nmodify %y\nchange %z\n" note.txt
access 2026-10-21 11:36:27.293973476 +0000
modify 2026-10-21 11:36:26.138028581 +0000
change 2026-10-21 11:36:26.266965462 +0000

Because of relatime, the access time only moves if it was earlier than the modify or change time, or more than a day old. Here it moved, because chmod had just updated the change time.

Cheat sheet

CommandWhat it does
ls -lLong listing: type, permissions, links, owner, group, size, date, name
ls -lhHuman-readable sizes
ls -la, ls -lAInclude hidden files
ls -lt, ls -ltrNewest first, newest last
ls -lSBiggest first
ls -ld dirThe directory itself
ls -F/ dir, @ link, * program
ls -lu, ls -lcShow access time, change time
file nameWhat the contents really are
stat nameEvery detail, all timestamps
stat -c %y nameJust the modify time
touch nameSet times to now (or create the file)
touch -d "2026-01-15 09:30" nameSet a specific time