Unix filesystem basics: symlink example

I can see some of you have arrived to my Unix file types post looking for an example of using symlinks in Unix. Today I would like to give you a quick introduction into Unix symlinks.

What is symlink?

Symlink is a short name for symbolic link (sometimes also referred as soft link) is a special type of file in Unix, which references another file or directory. Symlink contains the name for another file and contains no actual data. To most commands, symlinks look like a regular file, but all the operations (like reading from a file) are referred to the file the symlink points to.

How to create a Unix symlink

Just to give you an example, here’s how a typical symlink can be created and verified.

First, we create a new text file called /tmp/file.1:

greys@ubuntu:~$ echo "Hello from file 1" > /tmp/file.1
greys@ubuntu:~$ cat /tmp/file.1
Hello from file 1

Now, we create a symlink called /tmp/file.2, which points to the original file of ours. We use the standard Unix ln command, first specifying the target file (the real file we want our symlink to point to), then specify the name of our symbolic link:

greys@ubuntu:~$ ln -s /tmp/file.1 /tmp/file.2

If we look at both files, here’s what we see:

greys@ubuntu:~$ ls -al /tmp/file*
-rw-r--r-- 1 greys greys 18 2008-02-07 22:22 /tmp/file.1
lrwxrwxrwx 1 greys greys 11 2008-02-07 22:23 /tmp/file.2 -> /tmp/file.1

If you notice, the /tmp/file.2 has an “l” in the long-format output of theĀ ls command, which confirms it’s a symbolic link. You also can see right away where this symlink points to.

Just to confirm the typical behaviour of a symlink, here’s what happens when we try to show the contents of the /tmp/file.2: we see the contents of the file it points to, /tmp/file.1:

greys@ubuntu:~$ cat /tmp/file.2
Hello from file 1

How to remove a symlink

Guess what happens when you remove a symlink? Actually, not much. You only remove the symlink file itself, not the data file it refers to. Here’s what I mean:

greys@ubuntu:~$ rm /tmp/file.2
greys@ubuntu:~$ ls -al /tmp/file*
-rw-r--r-- 1 greys greys 18 2008-02-07 22:22 /tmp/file.1

While we’re at it, I would also like to explain what an orphan symlink is: it’s a symbolic link which points nowhere, because the original target file it used to point to doesn’t exist anymore.

Here is how an orphan symlink looks. First off, we recreate the symlink and verify it points to /tmp/file.1 once again:

greys@ubuntu:~$ ln -s /tmp/file.1 /tmp/file.2
greys@ubuntu:~$ ls -al /tmp/file*
-rw-r--r-- 1 greys greys 18 2008-02-07 22:22 /tmp/file.1
lrwxrwxrwx 1 greys greys 11 2008-02-07 22:38 /tmp/file.2 -> /tmp/file.1

Now, we simply rename the /tmp/file.1 file to /tmp/file.3:

greys@ubuntu:~$ mv /tmp/file.1 /tmp/file.3

This, naturally, makes /tmp/file.2 an orphan symlink which points to the old /tmp/file.1, but there isn’t a file like this anymore. Attempt to show the contents of /tmp/file.2 will thus fail:

greys@ubuntu:~$ ls -al /tmp/file*
lrwxrwxrwx 1 greys greys 11 2008-02-07 22:38 /tmp/file.2 -> /tmp/file.1
-rw-r--r-- 1 greys greys 18 2008-02-07 22:22 /tmp/file.3
greys@ubuntu:~$ cat /tmp/file.2
cat: /tmp/file.2: No such file or directory

See also:




How To Find Out a File Type and Permissions in Perl

A few months ago, I’ve given a really simple example of using Perl for parsing directory trees in Unix. If you looked closer at it, you would have noticed that the script was working fine, but showing file modes as strange large numbers which didn’t look like the usual file permissions you would expect. Today I’m going to explain why this happens, and show you how to find out a user type in Perl.

lstat and stat functions, return, among other things, the file mode value. While it looks confusing initially, it is in fact quite simply a combination field, which includes both the file type and all the permissions for it. If you print this field as a single decimal number, you will not recognize it, but if you simply convert it to octal, you will immediately start seeing the pattern:

Mysterious mode 33261 from the example below becomes 100755 when converted into octal, and you can easily see then the permission part of it: 0755.

In order to find out a file type in Perl, you need to be able to separate the type part from the permissions part of the file mode value. You can do this manually, but applying the bitmasks, but the easier way is to use the Fcntl ‘:mode’ Perl module.

Fcntl’:mode’ module contains all the constants and functions you need to extract the file type and permissions without complicating the task too much.

S_IMODE is the function which helps you extract the permissions:

        $permissions = S_IMODE($mode);

and S_IFMT is the function which extracts the file type:

        $filetype = S_IFMT($mode);

Now, obviously these extracted values are just the raw numbers, that’s why you’ll likely want to convert the permissions into octal, and to somehow connect the file type with the common symbolic notation, that’s exactly what I’m doing in today’s code example.

Here’s how it looks (I have the empty lines commented out just so that the code can be correctly shown in WordPress):

#!/usr/bin/perl
use Fcntl ':mode';
#
if ($ARGV[0] ne "") {
        $filename = $ARGV[0];
} else {
        print "Please specify a file!\n";
        exit;
}
#
if (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat($filename)) {
        $user = getpwuid($uid);
        $group = getgrgid($gid);
#
        $ftypes[S_IFDIR] = "d";
        $ftypes[S_IFCHR] = "c";
        $ftypes[S_IFBLK] = "b";
        $ftypes[S_IFREG] = "-";
        $ftypes[S_IFIFO] = "p";
        $ftypes[S_IFLNK] = "l";
        $ftypes[S_IFSOCK] = "s";
#
        $permissions = sprintf "%04o", S_IMODE($mode);
        $filetype = S_IFMT($mode);
#
        $ftype = $ftypes[$filetype];
#
        print "File: $filename\n";
	printf "File mode: %o (%d)\n", $mode, $mode;
        printf "File type: %s (%o)\n", $ftype, $filetype;
        print "File permissions: $permissions\n";
        print "File size: $size\n";
        print "File owner user: $user\n";
        print "File group: $group\n";
} else {
        print "Please specify an EXISTING file!\n";
}

And this is how it works:

root@ubuntu:/usr/include# /tmp/stat.pl /tmp/stat.pl
File: /tmp/stat.pl
File mode: 100755 (33261)
File type: - (100000)
File permissions: 0755
File size: 918
File owner user: greys
File group: greys

See also:




How to Compare Text Files Using diff

If you need to compare two text files in Unix, you’re mostly likely to use the diff command.

Today I’ll talk about the simplest scenario: you want to compare two files and understand if there are any differences.

Suppose you have two files in /tmp directory:
/tmp/1.txt:

aaa bbb ccc ddd eee fff ggg

and /tmp/2.txt:

bbb
c c
ddd
eee
fff
ggg
hhh

I have deliberately created them so short and simple – this way it’s easier to explain how the comparison works. If there are no differences between the files, you will see no output, but if two text files are indeed different, all the text mismatches will be highlighted using the standard diff output:

$ diff /tmp/1.txt /tmp/2.txt
1d0
< aaa
3c2
< ccc
---
> c c
7a7
> hhh

Lines like “1d0 and “3c2 are the coordinates and types of the differences between the two compared files, while lines like “< aaa” and “> hhh” are the differences themselves.

Diff change notation includes 2 numbers and a character between them. Characters tell you what kind of change was discovered:

d – a line was deleted
c – a line was changed
a – a line was added

Number to the left of the character gives you the line number in the original (first) file, and the number to the right of the character tells you the line number in the second file used in comparison.

So, looking at the two text files and the diff output above, you can see what happened:

This means that 1 line was deleted. < aaa suggests that the aaa line is present only in the original file:

1d0
< aaa

And this means that the line number 3 has changed. You can see how this confirms that in the first file the line was “ccc”, and in the second it now is “c c”.

3c2
< ccc
---
> c c

Finally, this confirms that one new line appeared in the second file, it’s “hhh” in the line number 7:

7a7
> hhh

That’s all you need to know to start playing with text comparisons yourself. Stay tuned for more!

Useful command line options for diff

Here are just some of the options, I won’t demonstrate them but simply wanted you to know what’s possible:

-i option – allows you to ignore case when comparing lines (aaa will equal AaA, etc)
-r option – recursively compare directories (and any files found there)
-s option – report identical files (you can script around this if your task is to confirm which files are identical)

Related books

If you want to learn more, here’s a great book:

unix-power-tools
Unix Power Tools