ファイル名を変更する最良の方法

ファイル名を変更する最良の方法

file両方のファイル(および)file_1のファイル名を変更する必要があります。次のコードを使用しています。

mv file .phfile
mv file_1 file
mv .phfile file

この方法はうまくいきますが、バグが多く、時にはデータが失われる可能性があります。もっと良い方法がありますか?

答え1

これrenameat2Linuxシステムのsyscallは、このRENAME_EXCHANGEフラグを使用してトリックを実行する必要があります。これそれを使用すると主張するCLIツールです。

答え2

既存のUnixシステムではファイルを交換する低レベルの方法がないため、中間の一時的な名前を使用する必要があります。堅牢性のために、一時名が他のプログラムで使用されず(したがって使用されているmktemp)、ファイルの1つと同じファイルシステムに存在することを確認してください。それ以外の場合は、ファイル名が変更されるのではなく、不必要にコピーされます。 。

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

エラーが発生した場合、最初のファイルはまだ一時的な名前を持つことができますが、2番目のファイルは移動されたか移動されていない可能性があります。ダウンタイムとクラッシュが発生した場合に堅牢でなければならない場合、2つの個人的なバリエーションが一時的な名前で回復しやすくなります。

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"
}

再起動後も一時ディレクトリが.swap_files.????????????続くと、停電によりファイル交換が中断されたことを意味します。ファイルの 1 つは所定の位置に移動しましたが、もう 1 つは移動していない可能性があるため、ここのコードは目的のリカバリの種類に応じてすべてのケースを処理するわけではありません。

最新のLinuxカーネル(2014年6月にリリースされた3.15ベース)ファイル交換のためのシステムコールがあります。renameat2(…, RENAME_EXCHANGE)しかし、汎用コマンドラインユーティリティはないようです。 glibcのサポートも最近追加されました(2.28、2018年8月発売)。

答え3

パーティーに少し遅れたが、最新バージョンと以前のバージョンのLinuxでは、スワップファイルの名前を自動的に指定できます。tccまたは使用gcc(すべての主要なLinuxディストリビューションで利用可能)タイムリーな生産あなた自身は、正しいカーネルシステムコールを取得して使用する低レベルのツールです。サードパーティ製のツールは必要ありません。

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

次のように、このbash機能を実行すると、ファイル名がクリーンでアトミックに置き換えられます。

swapname "/path/to/file-1" "/path/to/file-2"

両方のファイルが同じファイルシステムのマウントポイントにある必要がありますrenameat2RENAME_EXCHANGEマニュアルページのエラーセクションを参照してくださいrenameat2(例:rename(2)) 詳細をご覧ください。

TCCの代わりにGCCを使用してください

gcc代わりに使用するには、tccで始まる行を削除し、tcc -run ...その場所に次の行を追加します。

( EXEC="$(mktemp)" && gcc -x c - -o "$EXEC" && "$EXEC" "$@"; rm "$EXEC" ) <<"CODE" 

答え4

これが私が使用したものです。

file1=1stfile
file2=2ndfile

tempdir="$(mktemp -d)"

mv "$file1" "$tempdir/tmpfile" &&
mv "$file2" "$file1" &&
mv "$tempdir/tmpfile" "$file2" &&
rm -rf "$tempdir"

関連情報