如何在 Unix 中交换文件名?

发布于 2024-08-16 17:07:12 字数 281 浏览 8 评论 0原文

有什么方法可以在不使用临时变量的情况下快速完成此操作?有内置函数吗?

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

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

我希望文件 A 的名称为 A-name.file,文件 B 的名称为 B-name.file。

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

Any way to do this quickly without using a temp variable? Is there a built in function?

Edit: Thanks for the answers guys. Looks like I need to clarify my question but for the most part you guys assumed correctly: There are two files and the filenames names are reversed.

  • File A has name B-name.file
  • File B has name A-name.file

I'd like for File A to be named A-name.file and File B to be named B-name.file.

I agree, the situation doesn't happen often but it just happened to me and I wanted a quick fix.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

染墨丶若流云 2024-08-23 17:07: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

This can be done with little helper, just put in .bashrc, .zshrc or where your configs.

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

And use it as regular function:

$ cat a b
Alfa
Beta

$ swap a b && cat a b
Beta
Alfa
沧桑㈠ 2024-08-23 17:07:12

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

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

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

Darwin/Mac OS X has the exchangedata() system call:

The exchangedata() function swaps the contents of the files referenced by path1 and path2 in an atomic fashion. That is, all concurrent processes will either see the pre-exchanged state or the post-exchanged state; they can never see the files in an inconsistent state.

However it only actually works on a few filesystems that specifically support it (such as Apple's HFS and HFS+), and I haven't seen any similar system call on other systems. The portable way to do this is using a third temporary file name, and the operation will not be atomic.

云巢 2024-08-23 17:07:12

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

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

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

ok, stupid question, but why can't you simply do something like (in a shell script):

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

and yes of course it uses a temporary file, but its more concise then the other answers.

孤独患者 2024-08-23 17:07:12

在 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命令而不是link和remove意味着文件将在需要时在文件系统之间移动;这可能会有所帮助 - 或者可能意味着您的磁盘空间在您不希望的情况下被填满。从中途错误中恢复也并非完全微不足道。

At shell script level - there isn't a standard command and the rename involves at least a temporary file name (beware files on different file systems!).

At the C code level, there isn't a standard function available on all machines to swap file names - and one of the factors is the issue of dealing with files on different file systems.

On a single 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

That isn't completely fool-proof - but it is a semi-decent approximation if $file1 and $file2 are on the same file system (and I've assumed $file3 is a name on the same file system). Fixing it to deal with all the warts is ... non-trivial. (Consider $file1 == $file2, for instance.)

The code simulates pretty much the system calls that a C program would have to make - ln to map to link() and rm to map to unlink(). The newer (as in, only twenty years old) function rename() can probably be used to good effect - provided you understand what it won't do. Using the mv command instead of link and remove means that the files will be moved between file systems if needed; that might be helpful - or it might mean that your disk space fills up when you didn't intend it to. Recovering from an error part way through is not entirely trivial, either.

蓝海 2024-08-23 17:07:12

也许可以这样做:

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

Perhaps it could be done with:

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

There could be a way (I'm searching through the POSIX spec to see, but I wouldn't bet on it) to create a hardlink from the inode number; which would end up doing something like

file_B = fopen(path_b, 'r');
rename(path_a, path_b);
make_hardlink(file_B, path_a);
陈年往事 2024-08-23 17:07:12

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

如果您的程序是 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!)

What does "swap filenames" mean? Are you talking about the file system, or just variables in your program?

If your program is C++, and you have two filenames in strings, and want to swap them, use std::swap. This only changes the variables in the program:

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

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

If you have two files on the disk, and you want the names to swap content with each other, then no, there's no way to easily to that, if you want to preserve hard links. If you just want to perform a "safe save," then the unix rename() system call will clobber the destination file with the source file in an atomic operation (as atomic as can be supported by the underlying filesystem). Thus, you'd safe-save like this:

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

If you really, truly need to swap the files on disk (say, to keep a backup copy), then enter hard-linking. Note: hard-linking isn't supported on all file systems (specifically some SMB file shares), so you'll need to implement some backup if needed. You will need a temporary file name, but not any temporary data store. While you can implement it manually with link() and unlink() (remember: files can have multiple hard links in UNIX), it's easier just using rename(). You can't do a swap entirely atomically, though.

assuming oldfile is "filname.dat" and newfile is "filename.dat.bak" you'll get this:

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

If you crash after the link, you'll have the new data in newfile, and the old data in oldfile and tempfile.
If you crash after the first rename, you'll have the new data in oldfile, and the old data in tempfile (but not newfile!)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文