The shortest way to share two files in bash

Is it possible to exchange two files in bash?

Or they can be replaced shorter than this:

cp old tmp cp curr old cp tmp curr rm tmp 
+42
command-line linux bash
Jul 12 '09 at 12:01
source share
15 answers

Add this to your .bashrc:

 function swap() { local TMPFILE=tmp.$$ mv "$1" $TMPFILE mv "$2" "$1" mv $TMPFILE "$2" } 

If you want to handle the potential malfunction of intermediate operations mv , check the answer to the question "Ball" .

Note that neither this nor other answers provide an atomic solution, because it is not possible to implement such use of Linux system calls and / or popular Linux file systems. For Darwin's core, check out exchangedata syscall.

+41
Jul 13 '09 at 14:22
source share
 $ mv old tmp && mv curr old && mv tmp curr 

a bit more efficient!

Wrapped in a reusable function:

 function swap() { local TMPFILE=tmp.$$ mv "$1" $TMPFILE && mv "$2" "$1" && mv $TMPFILE $2 } 
+61
Jul 12 '09 at 12:04
source share
 tmpfile=$(mktemp $(dirname "$file1")/XXXXXX) mv "$file1" "$tmpfile" mv "$file2" "$file1" mv "$tmpfile" "$file2" 
+27
Jul 12 '09 at 12:09
source share

Do you really want to exchange them? I think it's worth noting that you can automatically back up the overwritten file using mv:

 mv new old -b 

You'll get:

 old and old~ 

If you want to have

 old and old.old 

you can use -S to change ~ your user suffix

 mv new old -b -S .old ls old old.old 

using this approach, you can exchange them faster using only 2 mv:

 mv new old -b && mv old~ new 
+19
Apr 6 2018-12-12T00:
source share

Combining the best answers, I put this in my ~ / .bashrc file:

 function swap() { tmpfile=$(mktemp $(dirname "$1")/XXXXXX) mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2" } 
+8
Jun 17 '12 at 23:05
source share

You can simply transfer them, instead of making a copy.

 #!/bin/sh # Created by Wojtek Jamrozy (www.wojtekrj.net) mv $1 cop_$1 mv $2 $1 mv cop_$1 $2 

http://www.wojtekrj.net/2008/08/bash-script-to-swap-contents-of-files/

+6
Jul 12 '09 at 12:05
source share

A somewhat hardened version that works for both files and directories:

 function swap() { if [ ! -z "$2" ] && [ -e "$1" ] && [ -e "$2" ] && ! [ "$1" -ef "$2" ] && (([ -f "$1" ] && [ -f "$2" ]) || ([ -d "$1" ] && [ -d "$2" ])) ; then tmp=$(mktemp -d $(dirname "$1")/XXXXXX) mv "$1" "$tmp" && mv "$2" "$1" && mv "$tmp"/"$1" "$2" rmdir "$tmp" else echo "Usage: swap file1 file2 or swap dir1 dir2" fi } 

This works on Linux. Not sure about OS X.

+4
Mar 21 '14 at 16:27
source share

This is what I use as a command on my system ( $HOME/bin/swapfiles ). I think it is relatively resistant to bad.

 #!/bin/bash if [ "$#" -ne 2 ]; then me=`basename $0` echo "Syntax: $me <FILE 1> <FILE 2>" exit -1 fi if [ ! -f $1 ]; then echo "File '$1' does not exist!" fi if [ ! -f $2 ]; then echo "File '$2' does not exist!" fi if [[ ! -f $1 || ! -f $2 ]]; then exit -1 fi tmpfile=$(mktemp $(dirname "$1")/XXXXXX) if [ ! -f $tmpfile ]; then echo "Could not create temporary intermediate file!" exit -1 fi # move files taking into account if mv fails mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2" 
+4
Mar 10 '15 at 0:01
source share

using mv means that you have fewer operations, there is no need for final rm, also mv only changes directory entries, so you do not use extra disk space for copying.

Temptationh then must implement the swap () command or part of it. If you really carefully check the error codes. It can be terribly destructive. You also need to check the existing tmp file.

+2
Jul 12 '09 at 12:07
source share

Hardy's idea was good enough for me. So I tried to use the following two files to exchange "sendms.properties", "sendms.properties.swap". But as soon as I called this function with the same argument "sendms.properties", this file was deleted. Avoiding this kind of FAIL, I added a few lines :-)

 function swp2file() { if [ $1 != $2 ] ; then local TMPFILE=tmp.$$ mv "$1" $TMPFILE mv "$2" "$1" mv $TMPFILE "$2" else echo "swap requires 2 different filename" fi } 

Thanks again Hardy; -)

+2
Dec 11 '12 at 2:01
source share

One of the problems that I encountered when using any of the solutions suggested here: your file names will be activated.

I have included the use of basename and dirname to keep the file names intact *.

 swap() { if (( $# == 2 )); then mv "$1" /tmp/ mv "$2" "`dirname $1`" mv "/tmp/`basename $1`" "`dirname $2`" else echo "Usage: swap <file1> <file2>" return 1 fi } 

I tested this in bash and zsh.




* So, to clarify how this is better:

If you start with:

 dir1/file2: this is file2 dir2/file1: this is file1 

other solutions will receive:

 dir1/file2: this is file1 dir2/file1: this is file2 

Content is exchanged, but file names remain. My solution does this:

 dir1/file1: this is file1 dir2/file2: this is file2 

Content and names are replaced.

+2
Feb 06 '13 at 5:06
source share

Of course mv instead of cp ?

0
Jul 12 '09 at 12:03
source share
 mv old tmp mv curr old mv tmp curr 
0
Jul 12 '09 at 12:04
source share

I have this in a working script that I delivered. It is written as a function, but you would call it

 d_swap lfile rfile 

GNU mv has -b and -T. You can handle directories using the -T switch.

Quotation marks are for filenames with spaces.

This is a bit verbose, but I have used it many times with both files and directories. There may be times when you want to rename a file with the name specified in the directory, but this is not handled by this function.

This is not very effective if all you want to do is rename the files (leaving them where they are), which is best done using a shell variable.

 d_swap() { test $# -eq 2 || return 2 test -e "$1" || return 3 test -e "$2" || return 3 if [ -f "$1" -a -f "$2" ] then mv -b "$1" "$2" && mv "$2"~ "$1" return 0 fi if [ -d "$1" -a -d "$2" ] then mv -T -b "$1" "$2" && mv -T "$2"~ "$1" return 0 fi return 4 } 

This function will rename files. It uses the name temp (it puts a dot “.” Before the name) just in case the files / directories are in the same directory, which usually happens.

 d_swapnames() { test $# -eq 2 || return 2 test -e "$1" || return 3 test -e "$2" || return 3 local lname="$(basename "$1")" local rname="$(basename "$2")" ( cd "$(dirname "$1")" && mv -T "$lname" ".${rname}" ) && \ ( cd "$(dirname "$2")" && mv -T "$rname" "$lname" ) && \ ( cd "$(dirname "$1")" && mv -T ".${rname}" "$rname" ) } 

It is much faster (no copying, just renaming). It is even uglier. And he will rename anything: files, directories, channels, devices.

0
Jun 28 '14 at 17:59
source share

Here is a swap script with a paranoid error check to avoid the unlikely event of a crash.

  • if any of the operations failed.
  • The path for the first argument is used for the temporary path (to avoid moving between file systems).
  • in the unlikely event that the second move fails, the first is restored.

Script:

 #!/bin/sh if [ -z "$1" ] || [ -z "$2" ]; then echo "Expected 2 file arguments, abort!" exit 1 fi if [ ! -z "$3" ]; then echo "Expected 2 file arguments but found a 3rd, abort!" exit 1 fi if [ ! -f "$1" ]; then echo "File '$1' not found, abort!" exit 1 fi if [ ! -f "$2" ]; then echo "File '$2' not found, abort!" exit 1 fi # avoid moving between drives tmp=$(mktemp --tmpdir=$(dirname $1)) if [ $? -ne 0 ]; then echo "Failed to create temp file, abort!" exit 1 fi # Exit on error, mv $1 $tmp if [ $? -ne 0 ]; then echo "Failed to to first file '$1', abort!" rm $tmp exit 1 fi mv $2 $1 if [ $? -ne 0 ]; then echo "Failed to move first file '$2', abort!" # restore state mv $tmp $1 if [ $? -ne 0 ]; then echo "Failed to move file, (unable to restore) '$1' has been left at '$tmp'!" fi exit 1 fi mv $tmp $2 if [ $? -ne 0 ]; then # this is very unlikely! echo "Failed to move file, (unable to restore) '$1' has been left at '$tmp', '$2' as '$1'!" exit 1 fi 
0
Jun 11 '17 at 7:46 on
source share



All Articles