파일 이름을 바꾸는 가장 좋은 방법

파일 이름을 바꾸는 가장 좋은 방법

file두 파일( 및 ) file_1의 파일 이름을 바꿔야 합니다 . 다음 코드를 사용하고 있습니다.

mv file .phfile
mv file_1 file
mv .phfile file

이 방법은 작동하지만 버그가 많고 때로는 데이터 손실이 발생할 수도 있습니다. 더 좋은 방법이 있나요?

답변1

이것renameat2Linux 시스템의 syscall은 이 RENAME_EXCHANGE플래그를 사용하여 트릭을 수행해야 합니다.이것그것을 사용한다고 주장하는 CLI 도구입니다.

답변2

기존 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

오류가 발생하면 첫 번째 파일은 여전히 ​​임시 이름을 가질 수 있지만 두 번째 파일은 이동되었거나 이동되지 않았을 수 있습니다. 가동 중단 및 충돌 발생 시 견고해야 하는 경우 임시 이름이 두 개인 변형이 복구하기 더 쉬울 수 있습니다.

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.????????????지속 되면 정전으로 인해 파일 교환이 중단되었음을 의미합니다. 파일 중 하나는 제자리로 이동했지만 다른 하나는 이동하지 않았을 수 있으므로 여기의 코드는 원하는 복구 종류에 따라 모든 경우를 처리하지는 않습니다.

최신 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"

두 파일이 모두 동일한 파일 시스템 마운트 지점에 있어야 할 수도 renameat2있습니다 . RENAME_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"

관련 정보