Move Files Without Breaking Stuff
Linux or Mac OS X only: The Command-Line Fu web site writes up a quick-and-dirty trick to move files and create a symlink in a single command—so you can move files without breaking anything.
The principle is simple: if you want to move a file or folder to another location without breaking anything, you can simply create a symlink from the original location to the new location. For instance, if you wanted to move SomeFile from the default location to the /other/location/ folder, you would move the file and then use the ln command to create a symlink:
ln -s SomeFile /other/location/SomeFile
The Command-Line Fu site simplifies it down into a single command—just add the following to your bashrc file to create an entirely new command that performs the same steps:
function lmv(){ [ -e $US1 -a -e $US2 ] && mv $US1 $US2 && ln -s $US2/$(basename $US1) $(dirname $US1); }
Once you’ve enabled the new command, you can use it to move a file or folder and symlink at the same time with the following syntax:
lmv SomeFile /another/folder
The only issue with this command is that it’s still a little buggy—trying to use it for files with spaces doesn’t work, you can’t rename the file while moving, and it breaks if you use a trailing slash—if you’ve got the geek skills to make the command better, or can think of a better solution, we’re all ears in the comments.
Relocate a file or directory, but keep it accessible [Command-Line Fu]
- Next Post: Living Large In A NYC Shoebox »
- « Previous Post: TimeLeft Masters Your Time With Countdowns And Clocks
Comments (AU Comments | US Comments)
Umm quicksilver anyone?....Just saying
dman0586
@gmerin: We aren't making a copy here, we're creating a logical connection - a pointer, if you please. Editing either of these locations once alters the results on both the softlink and the actual inode-based file.
A softlink is sorta like the old MS-Doc 'subst' command - if that helps anyone understand it better. Except softlinks work with files and directories, not just directories. softlink = 'ln -s' to me.
TheFu
@Brad: Your shell is a very personal thing. Each behaves a little differently, but usually close enough that most things work the same as in other shells.
I've been using tcsh for 15 years. More and more I find myself in bash by default. Bash has adopted many of the things that made tcsh so great and avoided some of the issues any csh-based shell has, especially in scripts.
Understanding that shells inherit legacy techniques from either Bourne shell (sh) or csh (csh) will get you started. Bourne shell families use 'something=what-ever-you-like; export something' and csh derivatives use 'setenv something=what-ever-you-like' stanzas. Aliases are similarly slightly different.
sh, ksh, bash are in a family.
csh, tcsh are in the other family.
I don't use ash or zsh enough to know where they fall.
There are other shells - psh (I think this is perl based), but I've never found a use for them even though I program in perl extensively.
You may find this helpful: [tldp.org] - the Advanced Bash-Scripting Guide.
TheFu
why not use tar or cpio like everyone has been doing for the last 30 years?
gmerin
@riedel:
so as to not allocate an inode, or because you can't allocate the inode (because the target isn't on the local system)?
gmerin
@jasonp: I haven't had any need to quote my variables in bash, but haven't tried in other terminals, I'll have to test that.
Brandon Rosenberg
Hard links would be the best way to achieve a move while keeping the original file intact, but they come with two major limitations:
- You can't use them on folders
- You can't span file systems
Ryan Nixon
hard links cannot point to another filesystem though
webwesen
A more correct way to do this is probably to use a hardlink:
ln src /dest
Although it depends on exactly why you want to move the file and maintain a link to it in the old location.
czarandy
this not an atomic operation. Why not use hardlinks (and then maybe replace by a soft link)?
To handle spaces, you need to set the env variable IFS to be something other than " "
function lmv(){ export IFSOLD=$IFS; IFS="
";[ -e $1 -a -e $2 ] && mv $1 $2 && ln -s $2/$(basename $1) $(dirname $1); export IFS=$IFSOLD; }
Please note: Use a hard enter after IFS=" - I believe you can use an escape character, but explicitly hitting enter after the quotes works for me. After the lmv function is called, it is good practice to set it back to the previous IFS. Hope this helps.
I guess that putting double quotes around variables should fix the problem with space in file path. It should look like: function lmv(){ [ -e "$1" -a -e "$2" ] && mv "$1" "$2" && ln -s "$2"/$(basename "$1") $(dirname "$1"); } Someone, please verify. I don't have access to bash shell at the moment.
MacauleyNinja
function lmv(){ [ -e "$1" -a -e "$2" ] && mv "$1" "$2" && ln -s "${2%/}/$(basename "$1")" "${1%/}"; } @ Brandon Rosenberg The move would work, but the sym link would be wrong - you need something like ln -s $2/$1 $1 but that would only work if both $1 and $2 were in the current directory.
SusitaHeretic
@Brad:
Aliases are easy. They take the form of
alias cmd="some other command"
You can use them a shortcut for a hard to remember command (ie change nano to edit if you're new to linux and can't remember the name of your text editor) bind shorter commands (I use 'apti' instead of 'sudo apt-get install' all the time to ease up on wrist pain) or even use them to set defaults (I ensure my emacs always runs in a terminal with the no window option by aliasing it to 'emacs -nw').
.bashrc is a script that is run whenever you start your shell (assuming you're using a bash shell). If you add your aliases to it they'll be set up each time you run bash. Or you can create a new file called .alias to hold all your aliases. Make bash run that file whenever you start a shell by adding 'source ~/.alias' to your .bashrc.
The prompt is a little more complicated to get right and there's a lot better documentation out there. For this and other config options I highly recommend reading through dotfiles.org. Skip anything that isn't well documented.
valadil
I use this with cryptfs. I have things like .mozilla in my encrypted filesystem. I just link .mozilla in my home dir to the encrypted version. You could use this for private keys, etc.
This will most likely work for files with spaces in the name if you quote the arguments: function lmv(){ [ -e "$1" -a -e "$2" ] && mv "$1" "$2" && ln -s "$2/$(basename $1)" "$(dirname $1)"; } The double quotes prevent variable expansion from being split on whitespace and turned into multiple arguments to the commands.
AlvinaSimpson
@Brandon Rosenberg: I think you'll need quotes around the variables to make sure filenames with spaces are handled properly:
-----
#!/bin/sh
mv "$1" "$2"
ln -s "$2" "$1"
-----
-Jason
jasonp
@Brandon Rosenberg: Actually what you have is a bit wrong. Try this: lmv foo /some/dir
mv foo /some/dir
ln -s /some/dir foo
You can see you are linking the dest dir and not the moved file!
Also you might want to quote those to handle spaces, and perhaps do a simple check to enforce the script has 2 arguments.
@Brad: hope this helps
[linuxcommand.org]
@PsyberS: the idea is to move the file yet keep from breaking things that might depend on the old location -- thus, it's a move AND symlink. the script is to create an alias which performs both actions -- it moves the file and creates a symlink in its place.
planetarian
@Phoshi: Check out NTFSLink on the linked page. I used to use it all the time; it even makes it obvious in explorer which files are links, and provides simple context menu entries to create and manipulate links.
planetarian
Thank you for posting this. I've been trying to come up with a way to do something like this with Apple Automator, but have been unsuccessful so far.
I'm using it as a means of "Archiving" the larger parts of my iTunes library to a network drive. I'm looking forward to giving this one a try.
ECN2
maybe it's just because I just finished the chapter on scripts, but wouldn't a ...
#!/bin/sh
mv $1 $2
ln -s $2 $1
#
do the job? just save that in a text file (named 'lmv' or whatever you like), add the execute permission, and throw it into your /bin folder to have a link/replacement command. syntax is "lmv currentfile newloacation"
Brandon Rosenberg
Wouldn't it be easier to just do:
ln -s SomeFile /another/folder/SomeFile
I suppose it depends on your exact needs but this solves the various problems mentioned with the script.
PsyberS
@Phoshi: Technically, with the right utilities, you can have symlinks in Windows. You should be aware, however, that Windows wasn't designed around it, and Explorer can exhibit very odd behavior when symlinks are involved.
If you're feeling brave, go here:
[www.shell-shocked.org]
However, I really wouldn't recommend that anybody actually use this in a production environment.
HeartBurnKid: Agent of R.O.A.C.H.
@nukee: Crap, I completely misunderstood this. It's been a long day in the lab. Consider my first comment moot.
nukee
LH editors here's a request for a how-to:
Teach me how to pimp my shell!
I want to learn what the bashrc file is, how to make aliases, change my prompt....all that cool stuff.
I've looked at tutorials and read manuals but it's all a bit confusing so I just stick with what I'm given.
Thanks!!!!!
Brad
I've always wanted something like this for Windows, :(
I'm not a savant when it comes to this stuff. But for me, allowing FTP through the firewall and using SCP would seem far easier when working with two unix/linux boxes.
nukee
@rubbsdecvik
Because using a link will leave your system more clean, and if its a file you modify, you would have to constantly modify multiple files multiple times.
Its the difference in Windows of copying a folder or making a shortcut.
If it's on the same file system, what is the difference between this and cp? I know that may be a dumb question, but wouldn't it just link the new inodes to the same files? You would loose minimal space. I could be wrong. In fact I'm guessing I am.
@Brandon Rosenberg: You probably have not tested your scripts with arguments containing whitespace and thus have not run into this problem yet.
Yes, you should be quoting your variables.
"trying to use it for files with spaces doesn't work"
I haven't tried this yet but would it work to put the folder in single ''s? That usually identifies the whole string as one part.
OneEyedOdin
@TheMightyBuzzard: Yes it would, and stop posting before you've had enough caffeine.
TheMightyBuzzard
@AlvinaSimpson: it wouldn't prevent the directory '/home/foo/bar baz/' used as the target directory from showing up as $2='/home/foo/bar' $3='baz/' though.
TheMightyBuzzard
@PsyberS: It wouldn't really make much difference unless you were moving it from one filesystem to another. That's a good enough reason for this command to exist though.
TheMightyBuzzard
Note: Do'nt use this with running applications!
The script doe'snt use an atomic operation, which means, betwenn the time where the file is moved and the time where the link is created, any running programm can try to access the file on the old place, and run into problems.
Tempura
or just don't use spaces in file/directory names. god made the _ for a reason.
chrizzle
@riedel:
In certain cases you would not be able to use a hard link, ie, across file systems. I think you are over thinking this.
@TheMightyBuzzard: Actually you were correct in your first post. If someone called the command like this: lmv /media/home/ /home/foo/bar baz/
It would need to be either:
lmv /media/home/ "/home/foo/bar baz/" or
lmv /media/home/ /home/foo/bar\ baz/
Luckily for us lazy Linux geeks the latter is automagically put in if you use tab auto-completion. You could check for this error with the following code:
if [ -n "$3" ]; then
echo "This script only takes 2 arguments"
fi
Of course this would mean turning it into a full blown script instead of just a .bashrc hack.
@OneEyedOdin: Or you can put a \ before each space
eg.
sda/home/User/important\ documents
OneEyedOdin