Can someone give me a command that would:
- move a file towards a new directory
- and leave a symlink in its old location towards its new one
mv
moves a file, and ln -s
creates a symbolic link, so the basic task is accomplished by a script that executes these two commands:
#!/bin/sh
mv -- "$1" "$2"
ln -s -- "$2" "$1"
There are a few caveats. If the second argument is a directory, then mv
would move the file into that directory, but ln -s
would create a link to the directory rather than to the moved file.
#!/bin/sh
set -e
original="$1" target="$2"
if [ -d "$target" ]; then
target="$target/${original##*/}"
fi
mv -- "$original" "$target"
ln -s -- "$target" "$original"
Another caveat is that the first argument to ln -s
is the exact text of the symbolic link. It's relative to the location of the target, not to the directory where the command is executed. If the original location is not in the current directory and the target is not expressed by an absolute path, the link will be incorrect. In this case, the path needs to be rewritten. In this case, I'll create an absolute link (a relative link would be preferable, but it's harder to get right). This script assumes that you don't have file names that end in a newline character.
#!/bin/sh
set -e
original="$1" target="$2"
if [ -d "$target" ]; then
target="$target/${original##*/}"
fi
mv -- "$original" "$target"
case "$original" in
*/*)
case "$target" in
/*) :;;
*) target="$(cd -- "$(dirname -- "$target")" && pwd)/${target##*/}"
esac
esac
ln -s -- "$target" "$original"
If you have multiple files, process them in a loop.
#!/bin/sh
while [ $# -gt 1 ]; do
eval "target=\${$#}"
original="$1"
if [ -d "$target" ]; then
target="$target/${original##*/}"
fi
mv -- "$original" "$target"
case "$original" in
*/*)
case "$target" in
/*) :;;
*) target="$(cd -- "$(dirname -- "$target")" && pwd)/${target##*/}"
esac
esac
ln -s -- "$target" "$original"
shift
done
rsync --remove-source-files
might be more informative to users who are moving a large file off a disk which may take some time.
Commented
Dec 30, 2016 at 16:17
Another caveat is that the first argument to ln -s is the exact text of the symbolic link.
-- See the new-ish and awesome ln --relative
option.
Commented
Mar 14, 2020 at 15:30
I generally use the this one-line function:
function ml() { mkdir -p "$(dirname "$1")" && rsync -aP --no-links "$1" "$2" && ln -sf "$2" "$1" }
Usage is similar to mv or cp:
ml old_file target_dir/new_file_name
Breaking it down:
mkdir -p "$(dirname "$1")"
- create the destination directory if it does not already exist
dirname "$1"
- get the directory component of the path (strip the filename)rsync -aP --no-links "$1" "$2"
- copy the file over to the destination. Replace this with mv "$1" "$2"
if both files are on the same filesystem for better performance.
-a
- preserve ownership and all other permissions. You can tune this to only preserve the components you want.-P
- show progress.--no-links
- don't copy links--this means you can run this command as many times as you want on the same file, and you won't ever lose your file by accidentally overwriting your destination file with a symlink to itself.ln -sf "$2" "$1"
- overwrite the old file with a symlink to the new file
-s
- use symbolic links-f
- overwrite the old filePut this in .sh file and make it executable(chmod +x filename
):
#!/bin/bash
mv "$1" "$2"
ln -s "$2" "$1"
Usage example:
./test.sh asdf otherdir/asdf
Note that this does no safety checks, etc. Depending on how complex is your task, this might be sufficient.
./test.sh .mozila/firefox/zotero/*/*.pdf MyBbliography/*.pdf
. The *
does not seem to be working with your test.sh
. Have you got a workaround ? Thx
Commented
Sep 7, 2015 at 12:04