Friday, May 15, 2009

Episode #36: File Linking

Paul pops off:

Creating links between files is a handy feature in UNIX/Linux systems. There are many instances where you need to have a copy of the file (or dirctory) in a particular location, but only want to maintain one original. For example, I was running a program to check the security of my Apache configuration file. It expected the file to exist in "/usr/local/apache2/conf/httpd.conf", but the original file was located at "/etc/httpd/conf/httpd.conf". To solve this problem I created a "soft" link as follows:

$ ln -s /etc/httpd/conf/httpd.conf /usr/local/apache2/conf/httpd.conf

The above "ln" command takes the "-s" flag to indicate a soft link, which creates a pointer to the original file. Next you specify the original file, followed by the file that will point to the original. Many will forget which one comes first (the original or the pointer), so don't forget that the original file always comes first :) Oh, and you can view the links by using the ls -l command:

$ ls -l /usr/local/apache2/conf/httpd.conf
lrwxrwxrwx 1 root root 26 Apr 21 13:57 /usr/local/apache2/conf/httpd.conf -> /etc/httpd/conf/httpd.conf

Hal chimes in:

Let me show you one more useful trick with the "ln" command. You can actually create symlinks to an entire directory of files with a single "ln" command:

# cd /usr/local/bin
# ln -s ../depot/clamav/current/bin/* .

First we "cd" to /usr/local/bin. The "ln" command creates a link to every object under /usr/local/depot/clamav/current/bin. The names of the links in /usr/local/bin will have the same name as the files under .../clamav/current/bin.

This is how I manage software that I've built from source on my systems. In fact, .../clamav/current is itself a symlink to a directory like .../clamav/0.95.1. Whenever I build the latest version of ClamAV, I install it in its own .../clamav/<vers> directory and just change the .../clamav/current symlink to point to the latest and greatest version. Since all the symlinks under /usr/local/{bin,sbin,etc,lib,include} are expressed using the .../clamav/current link, every other link in the hierarchy automatically starts pointing at the right version as soon as I change the .../clamav/current link. And it's easy to revert too, just in case the new version isn't working for some reason. Slick.

Ed responds:

Sadly, Microsoft never got around to implementing a pure-play shortcut-creating feature inside of cmd.exe. Because of that, several folks have released third-party tools that do so. Some nice ones include the NT resource kit tool simply called shortcut.exe, Pixelab's xxcopy, and NirSoft's NirCmd.

But, downloading a third-party tool isn't our way at this here blog. So, we must explore other options.

While cmd.exe itself doesn't have a feature for creating shortcuts, wscript, which is built in, does. There are many examples out on the Internet for creating shortcuts with wscript, but I've boiled them down to their bare minimum:

set WshShell = WScript.CreateObject("WScript.Shell" )
set oShellLink = WshShell.CreateShortcut( Wscript.Arguments.Named("shortcut") & ".lnk" )
oShellLink.TargetPath = Wscript.Arguments.Named("target")

The above script takes two arguments: the name of the target you want to create a shortcut to (/target:) and the shortcut name itself (/shortcut:). Note that the target could be a file or a directory. To create a shortcut using this script, we could dump all of that stuff above into a file called shortcutter.vbs, and then run it with the wscript interpreter.

"Ah... but that would be a scripting solution and not a command line," you might say. "You need to create a single command line that addresses the challenge."

Thanks for the delightful reminder. What, are you on Hal's payroll? Don't you have anything better to do with your time than taunt me? ;)

OK... I'll take your input and respond with this for a command line:

C:\> echo set WshShell = WScript.CreateObject("WScript.Shell" ) > shortcutter.vbs &
echo set oShellLink = WshShell.CreateShortcut( Wscript.Arguments.Named("shortcut") ^& ".lnk" )
>> shortcutter.vbs & echo oShellLink.TargetPath = Wscript.Arguments.Named("target")
>> shortcutter.vbs & echo oShellLink.Save >> shortcutter.vbs &
wscript shortcutter.vbs /target:[source] /shortcut:[shortcut]

It pretty much types itself, doesn't it? Easy!

Uh.... or not.

I'm simply creating the vbs script, which I'm naming shortcutter.vbs, and then invoking it to create the shortcut. I don't delete it at the end, because I want to keep it around for future uses. These things come in handy, you know.