2

This probably seems like a strange question, but say I have a script whose output is being written out to a file like so:

myscript.sh >> /path/to/some/file

Is it possible "touch" standard out such that the modification time of the output file can be updated without actually writing anything?

Obviously if the path were being passed directly into the script I could easily do this with the touch command, but that's not the case here, instead I'm redirecting standard out to a file.

So is there a clever way to touch the destination file when using I/O redirection, or do I need to handle the output path either internally, or inside some kind of wrapper script?

  • 2
    I'm not sure I understand what you're asking here, but technically the mtime of a file opened for append is updated even if you don't write anything to it. – Satō Katsura Nov 3 '17 at 10:49
  • 1
    ... but it’s not updated after that, unless you write something to it (and printf "" doesn’t count). – Stephen Kitt Nov 3 '17 at 10:50
3

At least on Linux, /dev/stdout is a symlink in to /proc/self/fd/1, which is a symlink to the file — so touch /dev/stdout works:

user@host:~$ ( echo hi; stat -c '%y' /tmp/foo >&2; for i in 0 1 2; do sleep 2; touch /dev/stdout; stat -c '%y' /tmp/foo >&2; done ) >> /tmp/foo
2017-11-03 07:02:59.595622835 -0400
2017-11-03 07:03:01.599680859 -0400
2017-11-03 07:03:03.603738883 -0400
2017-11-03 07:03:05.611797023 -0400

On Linux, that succeeds or to does nothing silently when stdout is not a file (e.g., an anonymous pipe, e.g., ./myscript.sh | less, actually has an mtime and a socket's mtime is always 0). If stdout isn't open (which is possible, though rare), then it produces a diagnostic ("no such file or directory" on my machine). Same if stdout is to a non-writable file.

It's not at all portable, but will probably at worse produce a diagnostic and do nothing (at least as non-root, who shouldn't be able to write to /dev).

Depending on what your actual goal is, there is probably a better way. E.g., maybe you should pass the file name to the script, then it's trivial to use touch on it directly. Or if you're doing this to say "still alive!" maybe you want a separate file for that (or use a process supervisor and/or a pid file).

  • What do you mean by when stdout is not a file. AFAICT, it works for several types of file like regular, fifo, directory, device (even when deleted like in { rm a; stat -Lc %y /dev/fd/3; sleep 2; touch /dev/fd/3; stat -Lc %y /dev/fd/3; } 3> a), and fails non-silently if stdout is not open or is not writable (rare in practice). Or do you mean if stdout is a socket? – Stéphane Chazelas Nov 3 '17 at 11:34
  • @StéphaneChazelas I tried with an anonymous pipe (e.g., | cat), but I guess a socket would do the same thing. I'll note the diagnostic if closed or not writable. – derobert Nov 3 '17 at 11:57
  • it works with an anonymous pipe (try (stat -Lc %y /dev/fd/3; sleep 1; perl -e 'utime undef,undef,*STDOUT or die "$!"' >&3; stat -Lc %y /dev/fd/3) 3>&1 >&2 | cat), but for sockets, fstat() will give you 0 for mtime/ctime/atime anyway. – Stéphane Chazelas Nov 3 '17 at 12:16
  • This may be just what I need in my case (as it's Linux I'm most interested in). In this case it seems like the -c (no-create) flag can be used to make the touch safe for use with root, as creation of the non-existing file appears to be handled by the redirect itself, so -c should prevent the creation of /dev/stdout file on other systems. This also appears to be compatible with FreeBSD and therefore macOS btw. – Haravikk Nov 5 '17 at 11:16
2

You'd need to invoke the futimes()/futimens() system call.

With perl, you can invoke it with:

perl -e 'utime undef, undef, *STDOUT'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.