Hi Glenn,
Glenn <[email protected]> writes:
> coreutils "ln" created bad symbolic link when I believe it should have
> failed , because the symbolic link being created already existed.
>
> Created a directory "AA"
>
> Created symbolic link "aa" to directory "./AA"
>
> Created directory "BB"
>
> Created symbolic link to "aa" to "./BB", without first deleting the
> original symbolic link "aa".
>
> Symbolic link creation of "aa" should fail on line 43 of test (ln -s
> ./BB aa), because symbolic link "aa" already exists.
> No error was reported and it created a bad symbolic link, inside
> directory "AA" as "BB" to "./BB" ( see line 53 of test),
>
> Symbolic link "aa" to directory "AA" remained.
It is not a bug, but I agree the behavior is a bit awkward.
Copying the relevant text from POSIX [1]:
SYNOPSIS
ln [-fs] [-L|-P] source_file target_file
ln [-fs] [-L|-P] source_file... target_dir
DESCRIPTION
[...]
In the second synopsis form, the ln utility shall create a new
directory entry for each source_file operand, at a destination
path in the existing directory named by target_dir. If the -s
option is specified, a symbolic link shall be created with the
contents specified by each source_file operand (which need not
name an existing file); otherwise, a hard link shall be created to
each file named by a source_file operand.
[...]
The second synopsis form shall be assumed when the final operand
names an existing directory.
> glenn@cottage:~/ZZ> ln --version
> ln (GNU coreutils) 9.6
> Copyright (C) 2025 Free Software Foundation, Inc.
> License GPLv3+: GNU GPL version 3 or later
> <https://gnu.org/licenses/gpl.html>.
> This is free software: you are free to change and redistribute it.
> There is NO WARRANTY, to the extent permitted by law.
>
> Written by Mike Parker and David MacKenzie.
>
> -------------- test follows
>
> glenn@cottage:~/ZZ> ls -lR
> .:
> total 0
> glenn@cottage:~/ZZ> mkdir AA
> glenn@cottage:~/ZZ> ls -lR
> .:
> total 0
> drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA
>
> ./AA:
> total 0
> glenn@cottage:~/ZZ> ln -s ./AA aa
> glenn@cottage:~/ZZ> ls -lR
> .:
> total 0
> lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA
> drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA
>
> ./AA:
> total 0
> glenn@cottage:~/ZZ> mkdir BB
> glenn@cottage:~/ZZ> ls -lR
> .:
> total 0
> lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA
> drwxr-xr-x 2 glenn glenn 6 Feb 27 16:13 AA
> drwxr-xr-x 2 glenn glenn 6 Feb 27 16:14 BB
>
> ./AA:
> total 0
>
> ./BB:
> total 0
> glenn@cottage:~/ZZ> *ln -s ./BB aa*
> glenn@cottage:~/ZZ> ls -lR
> .:
> total 0
> lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:13 aa -> ./AA
> drwxr-xr-x 2 glenn glenn 16 Feb 27 16:14 AA
> drwxr-xr-x 2 glenn glenn 6 Feb 27 16:14 BB
>
> ./AA:
> total 0
> *lrwxrwxrwx 1 glenn glenn 4 Feb 27 16:14 BB -> ./BB*
>
> ./BB:
> total 0
> glenn@cottage:~/ZZ>
When you invoked 'ln -s ./BB aa', "aa" was an existing directory
resolving to "AA". Therefore, the second POSIX synopsis form is used and
the "BB" symbolic link is created in "AA".
You can use the --no-target-directory option, though. I think that is
the behavior that you want [2]:
$ mkdir AA;
$ ln -s ./AA aa;
$ ln --no-target-directory -s ./BB aa
ln: failed to create symbolic link 'aa': File exists
Collin
[1] https://pubs.opengroup.org/onlinepubs/9799919799/utilities/ln.html
[2]
https://www.gnu.org/software/coreutils/manual/html_node/Target-directory.html