7

有什么方法可以在不使用临时变量的情况下快速做到这一点?有内置功能吗?

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

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

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

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

4

6 回答 6

12

这可以通过小助手来完成,只需放入 .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
于 2014-06-27T17:39:13.710 回答
10

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

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

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

于 2010-01-01T00:39:01.323 回答
5

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

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

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

于 2009-12-31T22:26:05.583 回答
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()。较新的(例如,只有 20 岁的)功能rename()可能会产生良好的效果 - 只要您了解它不会做什么。使用mv命令而不是链接和删除意味着如果需要文件将在文件系统之间移动;这可能会有所帮助 - 或者这可能意味着您的磁盘空间在您不希望的情况下被填满。中途从错误中恢复也不是一件容易的事。

于 2009-12-31T22:19:32.853 回答
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);
于 2009-12-31T22:20:23.023 回答
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 文件共享)都支持硬链接,因此如果需要,您需要实施一些备份。您将需要一个临时文件,但不需要任何临时数据存储。虽然您可以使用 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!)

于 2009-12-31T22:35:55.860 回答