linuxbashterminal

How to move a file while making the original one a symlink to the new location?


Suppose I have a file and a folder in my working directory:

>> tree
.
├── my-file
└── my-folder

To move my-file to my-folder and preserve a my-file in the original location with a symlink to the new location I have to do it in a few steps:

Move:

>> mv my-file my-folder
>> tree
.
└── my-folder
    └── my-file

Create symbolic link in the original location:

>> ln -s my-folder/my-file
>> tree
.
├── my-file -> my-folder/my-file
└── my-folder
    └── my-file

Is there a way to do it in one step? (and for multiple files?)

Something like the following:

>> tree
.
├── my-file
└── my-folder

>> move-and-create-symlink-command-??
>> tree
.
├── my-file -> my-folder/my-file
└── my-folder
    └── my-file

Solution

  • One way as mentioned in the comment by pmf; if you want the linking to only execute if the moving succeeded, connect both using &&. Other way is you can always create your custom commands whenever you want. You can create a custom shell function to perform the move and symlink creation in one step. Add the following function to your shell configuration file (e.g., ~/.bashrc or ~/.zshrc generally for MAC):

    move_and_symlink() {
      mv "$1" "$2"
      ln -s "${2%/}/$1" .
    }
    

    Then, source your shell configuration file to make the new function available:

    source ~/.bashrc  # or source ~/.zshrc based on your requirement
    

    Now, you can use the move_and_symlink command to move a file and create a symlink in one step:

    tree
    .
    ├── my-file
    └── my-folder
    
    move_and_symlink my-file my-folder
    tree
    .
    ├── my-file -> my-folder/my-file
    └── my-folder
        └── my-file
    

    The above custom function takes two arguments: the source file and the destination directory. It first moves the file to the destination directory using mv and then creates a symlink to the new location in the original location with ln -s.

    EDIT(For operating on multiple files h/t tripleee):

    The above function can be amended for handling multiple files:

    move_and_symlink() {
      # Get the last argument as the destination directory
      dest_dir="${!#}"
      
      # Iterate through all arguments except the last one (the source files)
      for ((i=1; i<$#; i++)); do
        src_file="${!i}"
        mv "$src_file" "$dest_dir"
        ln -s "$dest_dir/$src_file" .
      done
    }
    
    # Usage: move_and_symlink file1 file2 file3 destination_folder
    

    Reference