touch

Create an empty file, or move a timestamp

Updated 2026-09-04

touch does two jobs. Given a name that does not exist it creates an empty file; given one that does, it leaves the contents alone and moves the timestamps to now. Neither job is what > does, and the difference matters: a redirect empties whatever it lands on.

A file carries three times. touch sets the access time and the modification time, separately if you ask, and -d or -t puts them wherever you want rather than at the current moment. The third, the change time, records when the inode itself was last altered, and there is no flag for it: setting the other two is itself a change, so it moves to now whatever you asked for.

The usual reason to set a time deliberately is to make a reference point, which find can then compare every other file against.

Sample files used on this page

Every example below was run against these files. Recreate them to follow along.

site the tree the cp, mv and rm pages use as well; every timestamp in it is set deliberately, so one file's time can be copied onto another and the two compared

site:
total 16
drwxr-xr-x 2 user user 4096 Jun 15 10:00 assets
drwxr-xr-x 2 user user 4096 Jun 15 10:00 backups
-rw-r--r-- 1 user user   36 Jun  1 09:00 index.html
lrwxrwxrwx 1 user user    9 Jun 18 09:00 latest.css -> style.css
-rw-r--r-- 1 user user   33 Jun 20 09:00 style.css

site/assets:
total 4
-rw-r--r-- 1 user user 35 Jun 10 09:00 logo.svg

site/backups:
total 0

notes.txt one line, beside site/ rather than inside it

Rebuild before deploying.
27 outputs, collapsed by default

Creating an empty file

A name that does not exist becomes an empty regular file. A name that does exist keeps everything it had.

Create an empty file

touch report.txt && stat -c "%a %s %F %n" report.txt

Zero bytes, and a mode from your umask. %s is the size and %F the kind of thing it is, so this says the file exists and has nothing in it.

Show output
644 0 regular empty file report.txt

Create several at once

touch a.txt b.txt c.txt && ls *.txt

Every argument is a name. notes.txt was already there and appears because the glob matched it, not because touch did anything to it.

Show output
a.txt
b.txt
c.txt
notes.txt

Touch a file that already has something in it

cat notes.txt; touch notes.txt; cat notes.txt

The contents are untouched. That is the point of the name. Only the timestamps moved.

Show output
Rebuild before deploying.
Rebuild before deploying.

See what a redirect does instead

cat notes.txt; : > notes.txt; wc -c notes.txt

> file truncates to zero on the spot, before anything is written, so using it to make sure a file exists destroys the contents of one that already did. That idiom wants touch.

Show output
Rebuild before deploying.
0 notes.txt

Update a file only if it is already there

touch -c missing.txt; ls missing.txt

-c (--no-create) drops the creating behaviour and reports no error when the file is absent. Useful when the point is to move a timestamp and creating an empty file would be wrong.

Show output
ls: cannot access 'missing.txt': No such file or directory

See the mode a new file gets

umask 077; touch tight.txt; umask 022; touch loose.txt; stat -c "%a %n" tight.txt loose.txt

touch asks for 666 and the umask takes bits away, so 022 gives 644. There is no -m here as there is on mkdir: change the mode afterwards with chmod if the default is wrong.

Show output
600 tight.txt
644 loose.txt

Try a path whose directory does not exist

touch site/2026/report.csv; echo "exit $?"

touch creates a file and never a directory. mkdir -p on the parent first is the pairing a script wants.

Show output
touch: cannot touch 'site/2026/report.csv': No such file or directory
exit 1

Create a file you have no permission for

touch /etc/tips; echo "exit $?"

Write permission on the directory decides it, /etc here, and sudo is the usual answer.

Show output
touch: cannot touch '/etc/tips': Permission denied
exit 1

The three timestamps

Every file carries an access time, a modification time and a change time. touch sets the first two. None of the three is a creation time, which Linux did not record at all until recently and still does not expose through these tools.

Read all three

stat --printf "access %.19x\nmodify %.19y\nchange %.19z\n" site/index.html

The fixture set access and modify to the same moment. The change time is when the inode was last written. For this file that was when the fixture created it.

Show output

Your output will differ: the change time is whenever the file arrived on your machine

access 2026-06-01 09:00:00
modify 2026-06-01 09:00:00
change 2026-09-04 10:38:12

Move both to now

touch site/index.html && ls -l site/index.html

A bare touch on an existing file sets the access and modification times to the current moment. This is the form that makes a build system think a source file changed. ls -l shows the modification time, which is the one you nearly always mean.

Show output

Your output will differ: the timestamp is the moment you ran it

-rw-r--r-- 1 user user 36 Sep  5 07:39 site/index.html

Move only the access time

touch -a -d "2026-02-02 08:00" site/index.html && stat --printf "access %.19x\nmodify %.19y\n" site/index.html

-a leaves the modification time where it was. ls -lu reads the access time back if you would rather not run stat, and a tool that sorts by age is almost always reading the modification time instead.

Show output
access 2026-02-02 08:00:00
modify 2026-06-01 09:00:00

Move only the modification time

touch -m -d "2026-03-03 08:00" site/index.html && stat --printf "access %.19x\nmodify %.19y\n" site/index.html

-m sets the modification time instead, and it is the one you usually want. --time=atime and --time=mtime are the long spellings.

Show output
access 2026-06-01 09:00:00
modify 2026-03-03 08:00:00

Fail to set the change time

touch -d "2020-01-01" site/index.html && ls -l site/index.html && ls -lc site/index.html

ls -lc reads the change time where a plain ls -l reads the modification time. The modification time went back to 2020 and the change time went to now, because altering the inode is itself a change. Nothing sets it, and that is why an audit tool reads it.

Show output

Your output will differ: the change time is the moment you ran it

-rw-r--r-- 1 user user 36 Jan  1  2020 site/index.html
-rw-r--r-- 1 user user 36 Sep  5 07:39 site/index.html

Choosing the time

-d takes anything date understands and -t takes a fixed numeric stamp. Both beat running the command at the right moment.

Set a date and time

touch -d "2026-01-01 09:30" site/index.html && stat -c "%.19y %n" site/index.html

-d (--date) parses a wide range of formats. A date with no time gives midnight.

Show output
2026-01-01 09:30:00 site/index.html

Use a relative phrase

touch -d "2 hours ago" site/index.html && ls -l site/index.html

"yesterday", "last monday" and "+3 days" all work too. Handy for making a file old enough to be caught by a find -mtime you are testing.

Show output

Your output will differ: the timestamp is two hours before you ran it

-rw-r--r-- 1 user user 36 Sep  5 05:40 site/index.html

Use the numeric stamp instead

touch -t 202601011200 site/index.html && stat -c "%.19y %n" site/index.html

-t takes CCYYMMDDhhmm with no punctuation and no room for interpretation. Worth preferring in a script, where -d parsing a string differently on another machine is a real risk.

Show output
2026-01-01 12:00:00 site/index.html

Add seconds to the stamp

touch -t 202601011200.30 site/index.html && stat -c "%.19y %n" site/index.html

A dot and two digits after the minutes. Without them the seconds are zero.

Show output
2026-01-01 12:00:30 site/index.html

Copy the time from another file

touch -r site/style.css site/index.html && stat -c "%.19y %n" site/style.css site/index.html

-r (--reference) takes both times from the file you name. This is how you restore a timestamp you have just destroyed by editing something, provided you kept a copy.

Show output
2026-06-20 09:00:00 site/style.css
2026-06-20 09:00:00 site/index.html

Set several files to one time

touch -d "2025-04-04" site/index.html site/style.css && ls -l site/index.html site/style.css

Every argument gets the same time. Note what ls does with a date this old: past about six months it prints the year in place of the time of day, so the 00:00 is not visible here.

Show output
-rw-r--r-- 1 user user 36 Apr  4  2025 site/index.html
-rw-r--r-- 1 user user 33 Apr  4  2025 site/style.css

A symlink has timestamps of its own, and by default touch sets the ones on the file at the far end. stat is the other way round and reports on the link unless you pass -L, so the two need reading carefully together.

What it is actually for

Creating an empty file is the demonstration. Setting a time on purpose is the job.

Make a reference point for find

touch -d "2026-06-15" marker && find site -newer marker | sort

Anything modified after the marker comes back. find -newer compares against a file rather than a duration. Counting days backwards with -mtime is harder to get right.

Show output
site
site/assets
site/backups
site/latest.css
site/style.css

Touch a directory rather than a file

touch -d "2026-08-08" site/assets && stat -c "%.19y %n" site/assets site/assets/logo.svg

A directory has timestamps like anything else, and setting them says nothing about what is inside. logo.svg did not move.

Show output
2026-08-08 00:00:00 site/assets
2026-06-10 09:00:00 site/assets/logo.svg

Read a modification time without stat

date -r site/style.css "+%F"

date -r prints one file's modification time in whatever format you ask for. Shorter than a stat when a script wants the date as a string.

Show output
2026-06-20

Leave a marker that a step has run

touch .setup-done && ls -a | grep setup

A file whose contents nobody reads and whose existence is the whole message. A later run tests for it with [ -e .setup-done ] and skips the work.

Show output
.setup-done

Confirm the mode survives

chmod 400 notes.txt && touch notes.txt; echo "exit $?"; stat -c "%a %n" notes.txt

A read-only file you own can still be touched, and it keeps its mode. Permissions and timestamps are separate pieces of the inode, and touch only ever writes the second.

Show output
exit 0
400 notes.txt

Handle a name that begins with a dash

touch -- -notes.txt && ls -d -- -notes.txt

Without -- the shell hands touch a filename and touch reads it as flags. ./-notes.txt works as well.

Show output
-notes.txt