I do not understand why `touch -h` is not working as documented in the man page on macOS Big Sur

The default touch utility on macOS Big Sur does not respect the -h flag.

If one wants to use the -h option as claimed in the man pages of Apple, one has to use the GNU version of touch.

Is there a way to update touch to a newer version that respects what is written in its man pages?

I am using macOS Big Sur, with APFS, fully up to date as of 2021-08-15. The default touch has man pages written in 1995.

Here is an example of what happens:

$ ls -l
total 8
-rw-r--r--  1 xxx  staff  9 Aug 14 10:10 testfile
lrwxr-xr-x  1 xxx  staff  8 Aug 14 10:11 testfilelink@ -> testfile
$ touch -h testfilelink
$ ls -l
total 8
-rw-r--r--  1 xxx  staff  9 Aug 14 10:15 testfile
lrwxr-xr-x  1 xxx  staff  8 Aug 14 10:11 testfilelink@ -> testfile
$ 

It seems you have found a bug in macOS. The next-to-last major version of the operating system works according to the manual paage, but the latest version does not.

Either the manual page is outdated, or the software has a bug (most likely).

You can send feedback to Apple as a general user here:'

https://www.apple.com/feedback/macos.html

Or if you are a developer, you can use the Feedback Assistant to file a bug report as described here:

https://developer.apple.com/bug-reporting/

There's currently no newer version of touch available from Apple so you cannot as such upgrade your way out of the problem. As you indicate yourself, you can instead use the GNU version of touch to get the job done. You can find that in Homebrew by installing coreutils.

The GNU version of touch works by calling the newer futimens()/utimensat() functions (in the latter case with the flag argument set to AT_SYMLINK_NOFOLLOW to change the timestamp for the link itself).

The Catalina version of touch (287.100.2) works by calling the older lutimes() function, which explicitly sets the timestamp on the link itself. One of the core differences between the newer and older APIs are that the newer supports timestamps in nanoseconds, whereas the older has a lower resolution.

The lutimes() function on Big Sur actually doesn't implement a system call itself, but is actually contained fully in the standards library, using the setattrlist() function (which results in a system call) to actually perform the file system modification. setattrlist() is highly file system dependent (i.e. how it works on HFS+ file systems would be different from how it works on APFS file systems).

The Big Sur version of touch (321.100.11) works by calling the setattrlist() function directly, and only if that fails, fall back to lutimes. Unfortunately, it seems the programmer forgot about the need to specify that when -h is specified, the modification must take place on the link itself.

The actual bug is in line 219 of touch.c, where this line:

if (!setattrlist(*argv, &ts_req, &ts_struct, sizeof(ts_struct), 0))

should have been:

if (!setattrlist(*argv, &ts_req, &ts_struct, sizeof(ts_struct), utimes_f == lutimes ? FSOPT_NOFOLLOW : 0))

You could change that in touch.c, recompile it, and get a working binary.