如何在Unix中交换文件名?

时间:2009-12-31 22:12:19

标签: unix filenames swap

如果不使用临时变量,可以快速完成此操作吗?有内置功能吗?

编辑:谢谢你的回答。看起来我需要澄清我的问题,但大多数情况下你们假设正确:有两个文件,文件名名称相反。

  • 文件A的名称为B-name.file
  • 文件B的名称为A-name.file

我希望将文件A命名为A-name.file,将文件B命名为B-name.file。

我同意,情况不会经常发生,但它只是发生在我身上,我想快速解决。

6 个答案:

答案 0 :(得分:9)

Darwin / Mac OS X有exchangedata()系统调用:

  

exchangedata()函数以原子方式交换 path1 path2 引用的文件的内容。也就是说,所有并发进程将看到预交换状态或后交换状态;他们永远看不到处于不一致状态的文件。

然而,它实际上只适用于一些专门支持它的文件系统(例如Apple的HFS和HFS +),而且我还没有在其他系统上看到任何类似的系统调用。可执行此操作的可移植方法是使用第三个临时文件名,操作不是原子操作。

答案 1 :(得分:7)

这可以用小帮手完成,只需放入.bashrc,.zshrc或你的配置。

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

并将其用作常规功能:

$ cat a b
Alfa
Beta

$ swap a b && cat a b
Beta
Alfa

答案 2 :(得分:4)

好的,愚蠢的问题,但为什么你不能简单地做一些像(在shell脚本中):

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

当然是它使用临时文件,但它比其他答案更简洁。

答案 3 :(得分:3)

在shell脚本级别 - 没有标准命令,重命名至少包含一个临时文件名(注意不同文件系统上的文件!)。

在C代码级别,所有机器上都没有可用于交换文件名的标准功能 - 其中一个因素是处理不同文件系统上的文件的问题。

在单个文件系统上:

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

这不是完全万无一失的 - 但如果$ file1和$ file2在同一个文件系统上,那么这是一个半合适的近似值(我假设$ file3是同一文件系统上的一个名字)。修复它以处理所有的疣是...非平凡的。 (例如,考虑$ file1 == $ file2。)

代码几乎模拟了C程序必须进行的系统调用 - ln映射到link()rm映射到unlink()。如果您了解它不会做什么,那么较新的(如在二十年前)功能rename()可能会起到很好的作用。使用mv命令而不是链接和删除意味着如果需要,文件将在文件系统之间移动;这可能会有所帮助 - 或者它可能意味着当您不打算将磁盘空间填满时。从错误中恢复并不是完全无足轻重的。

答案 4 :(得分:1)

也许可以通过以下方式完成:

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);

可能有一种方法(我正在搜索POSIX规范,但我不会赌它)从inode号创建一个硬链接;最终会做类似

的事情
file_B = fopen(path_b, 'r');
rename(path_a, path_b);
make_hardlink(file_B, path_a);

答案 5 :(得分:1)

“交换文件名”是什么意思?您是在谈论文件系统,还是只是程序中的变量?

如果您的程序是C ++,并且您在字符串中有两个文件名,并且想要交换它们,请使用std :: swap。这只会改变程序中的变量:

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

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

如果磁盘上有两个文件,并且您希望名称彼此交换内容,那么不,如果您想保留硬链接,则无法轻松实现。如果您只想执行“安全保存”,那么unix rename()系统调用将使用原子操作中的源文件来破坏目标文件(原始操作可以由底层文件系统支持)。因此,你可以安全地保存:

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

如果你真的需要交换磁盘上的文件(比如保留备份副本),那么输入硬链接。注意:并非所有文件系统(特别是某些SMB文件共享)都支持硬链接,因此如果需要,您需要实施一些备份。您将需要一个临时文件 name ,但不需要任何临时数据存储。虽然您可以使用link()和unlink()手动实现它(请记住:UNIX中的文件可以有多个硬链接),但使用rename()更容易。但是,你不能完全原子地进行交换。

假设oldfile是“filname.dat”而newfile是“filename.dat.bak”你会得到这个:

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

如果在链接后崩溃,您将获得newfile中的新数据,以及oldfile和tempfile中的旧数据。 如果在第一次重命名后崩溃,你将在oldfile中拥有新数据,并在tempfile中拥有旧数据(但不是newfile!)