2009-12-31 17 views
5

Un modo per farlo rapidamente senza utilizzare una variabile temporanea? Esiste una funzione integrata?Come scambiare i nomi dei file in Unix?

Modifica: Grazie per le risposte ragazzi. Sembra che ho bisogno di chiarire la mia domanda ma per la maggior parte voi ragazzi avete assunto correttamente: ci sono due file ei nomi dei file sono invertiti.

  • File A ha il nome B-name.file
  • File B ha il nome A-name.file

mi piacerebbe per file Un ad essere chiamato A-name.file e File B per essere chiamato B-name.file.

Sono d'accordo, la situazione non succede spesso ma è successo a me e volevo una soluzione rapida.

+0

Cosa vuoi dire scambiando i nomi dei file? Rinominare il file "A" come "B" e v.v.? – Kimvais

+0

Non esiste una funzione incorporata per JUST scambiare i nomi dei file! come hai detto puoi farlo con uno script di shell e una variabile temporanea –

+1

Hai un linguaggio di implementazione specifico o hai bisogno di un comando (shell)? Che tipo di partizione viene utilizzata? (È necessario prendere in considerazione la possibilità di lavorare su NFS?) Cosa c'è che non va nell'approvazione delle variabili temporanee? –

risposta

4

ok, domanda stupida, ma perché non si può semplicemente fare qualcosa di simile (in uno script di shell):

mv $fileA $fileA.$$ 
mv $fileB $fileA 
mv $fileA.$$ $fileB 

e sì, naturalmente, utilizza un file temporaneo, ma è più conciso delle altre risposte.

+4

Usa un nome di file temporaneo, ma almeno non c'è alcuna copia in corso. –

3

A livello di script di shell - non esiste un comando standard e il rename comporta almeno un nome di file temporaneo (attenzione ai file su diversi file system!).

A livello di codice C, non esiste una funzione standard disponibile su tutte le macchine per scambiare nomi di file - e uno dei fattori è il problema di trattare file su diversi file system.

Su un unico file system:

file1=one-file-name 
file2=tother-file 
file3=tmp.$$ 

trap "" 1 2 3 13 15 
ln $file1 $file3 
rm $file1 
ln $file2 $file1 
rm $file2 
ln $file3 $file2 
rm $file3 
trap 1 2 3 13 15 

Questo non è completamente a prova di stupido - ma è un'approssimazione semi-decente se $ file1 e $ file2 sono sullo stesso file system (mi e' ho assunto $ file3 come nome sullo stesso file system). Fissarlo per trattare tutte le verruche è ... non banale. (. Considerare $ file1 == $ file2, per esempio)

Il codice simula praticamente la chiamate di sistema che un programma C avrebbe dovuto fare - ln per mappare link() e rm per mappare a unlink(). È probabile che la nuova funzione (come in appena vent'anni) rename() possa essere utilizzata con buoni risultati, a patto di capire cosa non farà. L'utilizzo del comando mv invece di collegamento e rimozione indica che i file verranno spostati tra i file system se necessario; potrebbe essere utile - o potrebbe significare che lo spazio del tuo disco si riempie quando non hai intenzione di farlo. Anche il ripristino da un errore in parte non è del tutto banale.

1

Forse potrebbe essere fatto con:

file_B = fopen(path_b, 'r'); 
rename(path_a, path_b); 

file_B_renamed = fopen(path_a, 'w'); 
/* Copy contents of file_B into file_B_renamed */ 
fclose(file_B_renamed); 
fclose(file_B); 

Ci potrebbe essere un modo (sto cercando attraverso le specifiche POSIX a vedere, ma io non ci scommetterei su di esso) per creare un collegamento reale dal numero di inode; che finirebbe per fare qualcosa di simile

file_B = fopen(path_b, 'r'); 
rename(path_a, path_b); 
make_hardlink(file_B, path_a); 
+0

Se il file B è di diversi gigabyte, sarà terribilmente lento. Perché spostare dati arbitrari quando riesci a mescolare le voci della directory? – fennec

+0

Perché non desidera un nome file temporaneo. È un limite sciocco, ma è parte della domanda. – Tordek

+1

È vero. La mia domanda "perché spostare dati arbitrari" era perlopiù retorica; tuttavia, anche se questa è davvero una soluzione entro il limite della sciocchezza, sembra probabile che si imbatta in problemi di prestazioni, quindi probabilmente dovremmo almeno descrivere queste implicazioni e suggerire alle parti interessate che il vincolo verrà riconsiderato. :) – fennec

1

Che cosa significa "scambio nomi di file"? Stai parlando del file system o solo delle variabili nel tuo programma?

Se il tuo programma è C++, e hai due nomi di file nelle stringhe, e vuoi scambiarli, usa std :: swap.Questo cambia solo le variabili nel programma:

std::string filenameA("somefilename"); 
std::string filenameB("othername"); 

std::swap(filenameA, filenameB); 
std::cout << filenameA << std::endl; // prints "othername" 

Se si dispone di due file sul disco, e si desidera che i nomi di scambiare contenuti con l'altro, allora no, non c'è modo per facilmente che, se vuoi preservare i collegamenti fisici. Se si desidera eseguire un "salvataggio sicuro", la chiamata di sistema unix rename() comprimerà il file di destinazione con il file di origine in un'operazione atomica (come atomico che può essere supportato dal file system sottostante). Così, faresti sicuro salvare in questo modo:

std::string savename(filename); 
savename += ".tmp"; 
... write data to savename ... 
if (::rename(savename.c_str(), filename.c_str()) < 0) { 
    throw std::exception("oops!"); 
} 

Se davvero, davvero bisogno di scambiare i file su disco (ad esempio, per tenere una copia di backup), quindi immettere hard-linking. Nota: l'hard-linking non è supportato su tutti i file system (in particolare alcune condivisioni di file SMB), quindi sarà necessario implementare un backup se necessario. È necessario un file temporaneo nome, ma non un qualsiasi archivio dati temporaneo. Mentre è possibile implementarlo manualmente con link() e unlink() (ricorda: i file possono avere più collegamenti rigidi in UNIX), è più facile usare solo rename(). Tuttavia, non puoi fare uno scambio interamente atomico.

assumendo vecchiofile è "filname.dat" e newfile è "filename.dat.bak" otterrete questo:

::link(oldfile, tempfile); 
::rename(newfile, oldfile); 
::rename(tempfile, newfile); 

Se si crash dopo il link, avrete i nuovi dati in newfile e i vecchi dati in oldfile e tempfile. Se si crash dopo la prima rinomina, avrete i nuovi dati in vecchiofile, ei vecchi dati in file temporaneo (ma non newfile!)

8

Darwin/Mac OS X ha la chiamata di sistema exchangedata():

La funzione exchangedata() scambia il contenuto dei file referenziati da percorso1 e percorso2 in modo atomico. Cioè, tutti i processi concorrenti vedranno lo stato pre-scambiato o lo stato post-scambio; non possono mai vedere i file in uno stato incoerente.

Tuttavia, in realtà funziona solo su alcuni file system che lo supportano specificatamente (come HFS e HFS + di Apple), e non ho visto alcuna chiamata di sistema simile su altri sistemi. Il modo portatile per farlo è utilizzare un terzo nome di file temporaneo e l'operazione non sarà atomica.

5

Questo può essere fatto con un piccolo aiuto, basta inserire .bashrc, .zshrc o dove sono le tue configurazioni.

function swap() { mv $1 $1._tmp && mv $2 $1 && mv $1._tmp $2 } 

e usarlo come normale funzione:

$ cat a b 
Alfa 
Beta 

$ swap a b && cat a b 
Beta 
Alfa 
+0

Questa soluzione è la più semplice e la più vicina alla domanda. –