Filehandles, files, directories

utime#

Set access and modification times on a list of files.

utime applies the numeric access time ATIME and modification time MTIME — both Unix epoch seconds — to every file named in LIST. It is the Perl-level interface to the utime(2) and utimes(2) system calls, and the tool behind the Unix touch(1) command. The inode change time (ctime) of each file is set to the current time as a side effect — this is a kernel invariant, not a Perl choice, and cannot be suppressed.

Synopsis#

utime ATIME, MTIME, LIST
utime undef, undef, LIST      # set both to "now" (since 5.8.0)

What you get back#

The number of files whose timestamps were successfully updated. Any file that could not be touched is silently skipped in the count; $! reflects the error from the last failing file only. Check the return value against scalar @files when you need to detect partial failure:

my @files = ("a", "b", "c");
my $n = utime $atime, $mtime, @files;
warn "only $n of ", scalar @files, " files updated: $!"
    if $n != @files;

Arguments#

  • ATIME — new access time, in seconds since the Unix epoch. Must be numeric; non-numeric scalars are coerced the usual way and a string like "now" becomes 0 (midnight 1970-01-01).

  • MTIME — new modification time, same format.

  • LIST — one or more filenames. On systems that support futimes(2) (Linux does), filehandles may appear in LIST as well. Filehandles must be passed as globs (*FH) or glob references (\*FH); a bareword in that position is parsed as a filename, not a handle.

The undef, undef form#

Since Perl 5.8.0, passing undef for both ATIME and MTIME calls utime(2) with a null second argument. The kernel then sets both times to the current wall-clock time, and — crucially — this form succeeds on files the caller does not own, provided the caller has write permission on the file. The owner-or-root requirement only applies to the explicit-timestamp form.

for my $file (@ARGV) {
    utime undef, undef, $file
        or warn "Couldn't touch $file: $!";
}

This is the form the real touch(1) command uses internally, and is the right choice for a generic “touch” script.

Permissions#

Two distinct rules, depending on which form you use:

Form

Who may succeed

utime $atime, $mtime, ...

File owner, or root

utime undef, undef, ...

Anyone with write permission on the file

A non-owner attempting the explicit-timestamp form gets EPERM in $! and the file is left untouched (it still counts as a failure, not a silent no-op).

Examples#

Plain Unix-style touch on a list of files you own:

my $now = time;
utime $now, $now, @ARGV;

Generic touch that also works on files you merely have write access to:

for my $file (@ARGV) {
    utime undef, undef, $file
        or warn "Couldn't touch $file: $!";
}

Copy timestamps from one file to another using stat:

my @st = stat $src or die "stat $src: $!";
utime $st[8], $st[9], $dst
    or die "utime $dst: $!";

Touch an already-open filehandle on Linux (needs futimes(2)):

open my $fh, ">>", "log" or die $!;
utime undef, undef, *$fh;       # glob deref, not bareword

Set a file one hour into the past, useful for build-system tests:

my $t = time - 3600;
utime $t, $t, "stale.tmp";

Edge cases#

  • Mixed undef and number: passing undef for only one of the first two arguments does not trigger the “both-undef” path. The undef is coerced to 0 (epoch midnight 1970-01-01), the other value is used verbatim, and an uninitialized warning fires under use warnings. This is almost never what you want.

    use warnings;
    utime undef, time, $file;       # atime = 1970-01-01, warning
    
  • Non-numeric timestamps: ATIME and MTIME must be numeric. String values are coerced — "1700000000" works; "yesterday" silently becomes 0.

  • ctime always moves: there is no way to preserve a file’s inode change time across a utime call. If you need that, you need filesystem-level tools or a snapshot, not Perl.

  • Filehandle in LIST: must be a glob (*FH) or glob reference (\*FH). A bareword like FH in that position is parsed as the filename "FH", and a string scalar holding a handle name is likewise treated as a filename.

  • Non-existent file: counts as a failure and does not increment the return value; $! is set to ENOENT (No such file or directory).

  • Partial failure: the count reflects successes only. To know which file failed, loop over the list one at a time.

  • NFS clock skew: the server’s clock is authoritative, not the client’s. A noticeable difference between the two shows up as timestamps that do not match what time on the client returned.

  • Sub-second precision: Perl’s utime operates on whole seconds. Filesystems and kernels that support nanosecond timestamps will see the fractional component zeroed. Reach for Time::HiRes::utime (XS) if you need better resolution.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • stat — read the timestamps utime writes; elements 8 (atime), 9 (mtime), and 10 (ctime) of the returned list

  • time — current epoch seconds, the usual source for both ATIME and MTIME

  • open and close — filehandles that utime can touch on systems with futimes(2)

  • $! — system error from the last failing file when the return count is less than scalar @files

  • utime(2), utimes(2), futimes(2) — the underlying system calls