交换文件名的最佳方式

Bin*_*abu 9 shell shell-script rename files

我需要交换两个文件(filefile_1)的文件名。我正在使用以下代码。

mv file .phfile
mv file_1 file
mv .phfile file
Run Code Online (Sandbox Code Playgroud)

这有效但非常有问题,有时甚至会导致数据丢失。有一个更好的方法吗?

小智 6

renameat2Linux 系统上的syscall 带有RENAME_EXCHANGE标志,应该完全做到这一点,是一个声称使用它的 cli 工具。

  • @Xaekai 夸大其词,不。当提出问题时,该功能甚至不存在,即使在今天,它也仅适用于 Linux,而不适用于其他操作系统,并且它需要一个您不太可能安装的实用程序。 (2认同)

Ten*_*ken 6

虽然有点晚了,但您可以使用或\xe2\x80\x94在新版和旧版 Linux 中自动名称交换文件,这两个命令在所有主要 Linux 发行版中都可用 \xe2\x80\x94 到JITtccgcc自己的低级版本使用正确的内核系统调用的工具并使用它。无需第三方工具:

\n
swapname() {\n    tcc -run - "$@" <<"CODE"\n    #include <unistd.h>\n    #include <fcntl.h> \n    #include <stdio.h>\n    #include <sys/syscall.h>\n    \n    // Ubuntu 18.04 does not define RENAME_EXCHANGE\n    // Value obtained manually from \'/usr/include/linux/fs.h\'\n    // You should switch to RENAME_EXCHANGE on modern systems\n    // Just remove the following line, then remove the `local_`\n    // prefix where it appears later in this function.\n    int local_RENAME_EXCHANGE = (1 << 1);\n    \n    int main(int argc, char **argv) {\n        if (argc != 3) { \n            fprintf(stderr, "Error: Could not swap names. Usage: %s PATH1 PATH2\\n", argv[0]);\n            return 2; \n        }\n        int r = syscall(\n            SYS_renameat2,\n            AT_FDCWD, argv[1],\n            AT_FDCWD, argv[2], \n            local_RENAME_EXCHANGE\n        );\n        if (r < 0) {\n            perror("Error: Could not swap names");\n            return 1;\n        }\n        else return 0;\n    }\nCODE\n} \n
Run Code Online (Sandbox Code Playgroud)\n

按以下方式运行此 bash 函数将干净且原子地交换文件名:

\n
swapname "/path/to/file-1" "/path/to/file-2"\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,renameat2withRENAME_EXCHANGE可能要求两个文件位于同一文件系统安装点下。请参阅手册页中的错误部分renameat2(即rename(2) ) 的错误部分以获取更多信息。

\n

使用 GCC 代替 TCC

\n

如果您更喜欢使用gcc而不是tcc,只需删除以tcc -run ...并在其位置添加以下行:

\n
( EXEC="$(mktemp)" && gcc -x c - -o "$EXEC" && "$EXEC" "$@"; rm "$EXEC" ) <<"CODE" \n
Run Code Online (Sandbox Code Playgroud)\n


Gil*_*il' 5

在传统的 Unix 系统中没有交换文件的低级方法,因此您需要使用一个中间临时名称。为了健壮性,确保临时名称不会被任何其他程序使用(所以使用mktemp),并且它与文件之一位于同一文件系统上(否则文件将被不必要地复制而不是被重命名)。

swap_files () {
  tmp_name=$(TMPDIR=$(dirname -- "$1") mktemp) &&
  mv -f -- "$1" "$tmp_name" &&
  mv -f -- "$2" "$1" &&
  mv -f -- "$tmp_name" "$2"
}
swap_files file file_1
Run Code Online (Sandbox Code Playgroud)

请注意,如果发生错误,第一个文件可能仍使用其临时名称,而第二个文件可能尚未移动,也可能尚未移动。如果您需要在中断和崩溃的情况下保持健壮性,那么具有两个临时名称的变体可能更容易从中恢复。

swap_files2 () {
  tmp_dir1=$(TMPDIR=$(dirname -- "$1") mktemp -d .swap_files.XXXXXXXXXXXX) &&
  tmp_dir2=$(TMPDIR=$(dirname -- "$2") mktemp -d .swap_files.XXXXXXXXXXXX) &&
  mv -f -- "$1" "$tmp_dir1/" &&
  mv -f -- "$2" "$tmp_dir2/" &&
  mv -f -- "$tmp_dir1/"* "$1" &&
  mv -f -- "$tmp_dir2/"* "$2" &&
  rmdir -- "$tmp_dir1" "$tmp_dir2"
}
Run Code Online (Sandbox Code Playgroud)

如果.swap_files.????????????重新启动时出现临时目录,则意味着文件交换因电源故障而中断。请注意,其中一个文件可能已经移动到位,而另一个尚未移动到位,因此此处的代码不会处理所有情况,这取决于您想要哪种恢复。

现代 Linux 内核(自 3.15 起,于 2014 年 6 月发布)具有交换文件的系统调用:renameat2(…, RENAME_EXCHANGE). 但是,它似乎没有常用的命令行实用程序。甚至最近才添加了 glibc 支持(2.28,于 2018 年 8 月发布)。


Bin*_*abu 1

这是我最终使用的:

file1=1stfile
file2=2ndfile

tempdir="$(mktemp -d)"

mv "$file1" "$tempdir/tmpfile" &&
mv "$file2" "$file1" &&
mv "$tempdir/tmpfile" "$file2" &&
rm -rf "$tempdir"
Run Code Online (Sandbox Code Playgroud)