56

Assuming you know the target is a symbolic link and not a file, is there any difference between using rm and unlink to remove the link?

  • 3
    This is pretty well covered on ServerFault: serverfault.com/questions/38816/… – slm Aug 24 '14 at 19:21
  • @slm♦ The answers correspond to that question, but this question is different, it says: "Assuming you know the target is a symbolic link and not a file". – Stéphane Gourichon Mar 21 '15 at 20:02
  • Doesn't look like you've accepted an answer here @IQAndreas. Please do so if they helped you. – Gray Feb 22 at 17:03
57

Anytime you have these types of questions it's best to conceive of a little test to see what's actually happening. For this you can use strace.

unlink

$ touch file1
$ strace -s 2000 -o unlink.log unlink file1

rm

$ touch file1
$ strace -s 2000 -o rm.log rm file1

When you take a look at the 2 resulting log files you can "see" what each call is actually doing.

Breakdown

With unlink it's invoking the unlink() system call:

....
mmap(NULL, 106070960, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6d025cc000
close(3)                                = 0
unlink("file1")                         = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
....

With rm it's a slightly different path:

....
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
newfstatat(AT_FDCWD, "file1", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
geteuid()                               = 1000
newfstatat(AT_FDCWD, "file1", {st_mode=S_IFREG|0664, st_size=0, ...}, AT_SYMLINK_NOFOLLOW) = 0
faccessat(AT_FDCWD, "file1", W_OK)      = 0
unlinkat(AT_FDCWD, "file1", 0)          = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
...

The system calls unlink() and unlinkat() are essentially the same except for the differences described in this man page: http://linux.die.net/man/2/unlinkat.

excerpt

The unlinkat() system call operates in exactly the same way as either unlink(2) or rmdir(2) (depending on whether or not flags includes the AT_REMOVEDIR flag) except for the differences described in this manual page.

If the pathname given in pathname is relative, then it is interpreted relative to the directory referred to by the file descriptor dirfd (rather than relative to the current working directory of the calling process, as is done by unlink(2) and rmdir(2) for a relative pathname).

If the pathname given in pathname is relative and dirfd is the special value AT_FDCWD, then pathname is interpreted relative to the current working directory of the calling process (like unlink(2) and rmdir(2)).

If the pathname given in pathname is absolute, then dirfd is ignored.

  • 1
    Since it's giving AT_FDCWD, there's effectively no difference between unlink and unlinkat. – Barmar Aug 27 '14 at 21:01
  • 6
    It makes me happy to read this answer because I just "learned to fish" in a powerful, flexible way when often I get a fish or sometimes I "learn to fish" in a basic way on SO :-) – sage Jan 24 '17 at 19:21
24

POSIX specifies that the unlink utility calls the C library unlink function and nothing else. It takes no option. If you pass a valid path name to something which isn't a directory, and if you have write permissions to the directory where that object lives, then unlink will remove it.

rm is a traditional Unix command which has a bit of other functionality, and isn't quite a superset of unlink (see below).

Firstly, rm performs safety checks. If you try to rm an object to which you don't have write permissions (which are irrelevant to your ability to remove it: the containing directory's permissions are!) rm nevertheless refuses unless -f is specified. rm normally complains if the file doesn't exist, as does unlink; however with -f, rm does not complain. This is often exploited in Makefiles (clean: @rm -f $(OBJS) ...) so make clean doesn't fail when there is nothing to remove.

Secondly, rm has the -i option for interactively confirming the delete.

Thirdly, rm has -r for recursively removing a directory, which is something that unlink isn't required to do, since the C library function doesn't do that.

The unlink utility isn't exactly a stripped-down rm. It performs a subset of what rm does, but it has semantics which is a combination of rm with -f and rm without -f.

Suppose you want to just remove a regular file regardless of what its own permissions are. Furthermore, suppose you want the command to fail if the file doesn't exist, or any other reason. Neither rm file nor rm -f file meets the requirements. rm file will refuse if the file isn't writable. But rm -f file will neglect to complain if the file is missing. unlink file does the job.

unlink was probably introduced because rm is too clever: sometimes you just want the pure Unix unlink semantics: "please make this directory entry go away if directory permissions allow".

  • 2
    This is the clearest answer here. It actually gives the use case for unlink rather than just describing differences. – Wildcard Nov 28 '18 at 21:11
19

With a single file, rm and unlink do the same task, remove the file. As POSIX defined, rm and unlink both call to unlink() system call.

In GNU rm, it calls to unlinkat() system call, which is equivalent to the unlink() or rmdir() function except in the case where path specifies a relative path.

Note

On some systems, unlink can also remove directory. At least in GNU system, unlink can never delete the name of a directory.

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.