2009-07-12 26 views

risposta

41

Aggiungi questo al vostro .bashrc:

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

Se si desidera gestire il potenziale fallimento di intermedi mv operazioni, controllare Can Bal's answer.

Si prega di notare che né questa né altre risposte forniscono una soluzione atomica, perché è impossibile implementarle utilizzando syscalls Linux e/o popolari filesystem Linux. Per il kernel Darwin, selezionare exchangedata syscall.

+2

se mv "$ 2" "$ 1" non riesce, come si rileva e si esegue il rollback? –

+0

@ Michael: Se sei preoccupato di questo, allora hai delle opzioni. È possibile stampare il nome del file temporaneo nell'operazione in modo che sia possibile copiarlo da soli. O potresti anche creare un file .backup di entrambi nella directory. Hai opzioni. –

+3

Se si sta creando il file temporaneo nella stessa directory del file sorgente, si può usare 'ln' invece di' mv'. Per esempio 'ln" $ 1 "" $ tmp "; mv -f "$ 2" "$ 1"; mv "$ tmp" "$ 2" 'in questo modo se devi eseguire il rollback tutto ciò che devi fare è eliminare il file temporaneo, che è solo un collegamento a' $ 1'. Tuttavia non funzionerà per le directory (non è in grado di collegarle in modo fisso). – Haravikk

0

Sicuramente mv invece di cp?

+0

! Pensa se il vecchio è di 10 GB File – shahjapan

61
$ mv old tmp && mv curr old && mv tmp curr 

è leggermente più efficiente!

avvolti in funzione di shell riutilizzabile:

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

... e non 'mv' i tuoi file nel nirvana, se c'è un problema con' tmp'. +1 – Boldewyn

0
mv old tmp 
mv curr old 
mv tmp curr 
2

usando mv significa avere uno operazioni di meno, non c'è bisogno per la RM finale, anche mv sta solo cambiando le voci di directory in modo che non si utilizza lo spazio su disco aggiuntivo per la copia.

Temptationh è quindi per implementare una funzione di shell swap() o alcuni di questi. Se fai estrema attenzione a controllare i codici di errore. Potrebbe essere terribilmente distruttivo. Inoltre, è necessario verificare la presenza di file tmp preesistenti.

28
tmpfile=$(mktemp $(dirname "$file1")/XXXXXX) 
mv "$file1" "$tmpfile" 
mv "$file2" "$file1" 
mv "$tmpfile" "$file2" 
+3

Upmod per l'utilizzo di mktemp – Hasturkun

+3

upmod per l'utilizzo dello stesso filesystem – Cougar

19

in realtà vuoi scambiarli? penso che la sua pena ricordare che si può file sovrascritto il backup automaticamente con mv:

mv new old -b 

si otterrà:

old and old~ 

se si desidera avere

old and old.old 

puoi usare -S per cambiare ~ al suffisso personalizzato

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

utilizzando questo approccio si può effettivamente scambiare più veloci, utilizzando solo 2 mv:

mv new old -b && mv old~ new 
+2

Sembrava promettente ma purtroppo l'opzione '-b' non è disponibile su OS X Lion – bloudermilk

9

Combinando le risposte migliori, ho messo nel mio ~ /.bashrc:

function swap() 
{ 
    tmpfile=$(mktemp $(dirname "$1")/XXXXXX) 
    mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2" 
} 
2

L'idea di Hardy era abbastanza buona per me. Quindi ho provato i miei seguenti due file per scambiare "sendsms.properties", "sendsms.properties.swap". Ma una volta chiamata questa funzione come stesso argomento "sendsms.properties", questo file è stato cancellato. Evitare a questo tipo di FAIL ho aggiunto qualche linea per me :-)

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 
} 

Grazie ancora Hardy ;-)

2

Un problema che ho avuto quando utilizzando una delle soluzioni fornite qui: nomi di file otterrà acceso.

Ho incorporato l'uso di basename e dirname per mantenere intatti i nomi dei file *.

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 
} 

Ho provato questo in bash e zsh.


* Quindi, per chiarire come questo è meglio:

Se si inizia con:

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

Le altre soluzioni finirebbe con:

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

I contenuti vengono scambiati ma i nomi dei file sono rimasto. La mia soluzione rende:

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

I contenuti e le nomi sono scambiati.

+0

Nice .. Ma non funziona se i file si trovano nella stessa directory .. (Suppongo, in quel caso, l'unica cosa che ha senso è quello di scambiare il contenuto dei file, ma mantenere i nomi dei file :)) –

+0

@ HåkonHægland dovrebbe funzionare se sono nella stessa directory.L'unica avvertenza è se sono entrambi in/tmp, nel qual caso puoi semplicemente cambiare lo script per spostarli in/tmp/foo o qualcosa del genere –

4

Una versione un po 'indurito che funziona per entrambi i file e le directory:

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 
} 

Questo funziona su Linux. Non sono sicuro di OS X.

+0

Non funziona su OSX/macOS. – raskhadafi

0

Ho questo in uno script di lavoro che ho consegnato. E 'scritto come una funzione, ma si sarebbe invocarlo

d_swap lfile rfile 

La mv GNU ha la -b e l'interruttore -T. Puoi gestire le directory utilizzando l'opzione -T .

Le virgolette sono per nomi di file con spazi.

È un po 'prolisso, ma l'ho usato molte volte con file e directory. Ci possono essere casi in cui si vorrebbe rinominare un file con il nome di una directory, ma questo non è gestito da questa funzione.

Questo non è molto efficiente se tutto ciò che si vuole fare è rinominare i file (lasciandoli dove sono), meglio fare con una variabile di shell.

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 
} 

Questa funzione consente di rinominare i file. Usa un nome temporaneo (mette un punto ').'di fronte al nome) nel caso in cui i file/le directory si trovino nella stessa directory, che di solito è il caso.

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") 
} 

Questo è molto più veloce (non c'è copia, basta rinominare). È ancora più brutto. E rinominerà qualsiasi cosa: file, directory, pipe, dispositivi.

4

Questo è quello che uso come comando sul mio sistema ($HOME/bin/swapfiles). Penso che sia relativamente resistente alla cattiveria.

#!/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" 
0

Ecco uno script swap con l'errore paranoico controllo per evitare caso improbabile di un guasto.

  • se una delle operazioni ha esito negativo è segnalata.
  • il percorso del primo argomento viene utilizzato per il percorso temporaneo (per evitare lo spostamento tra i file system).
  • nel caso improbabile che la seconda mossa fallisca, il primo viene ripristinato.

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 
Problemi correlati