하드 링크가 있는 cp의 동작에 놀랐습니다.

하드 링크가 있는 cp의 동작에 놀랐습니다.

나는 하드 링크의 개념에 대해 매우 잘 알고 있으며 cp기본 도구의 매뉴얼 페이지는 물론 최근 POSIX 사양까지 여러 번 읽었습니다. 그러나 나는 다음과 같은 행동을 발견하고 여전히 놀랐습니다.

$ echo john > john
$ cp -l john paul
$ echo george > george

이 시점에서는 동일한 inode(및 내용)를 가지며 john두 가지 측면에서 다릅니다. 이제 우리는 다음을 수행합니다.paulgeorge

$ cp george paul

이 시점에서 나는 inode 번호는 다르지만 내용은 동일 george할 것으로 예상했습니다 paul. 이러한 기대는 충족되었습니다.반품paul이제 다른 inode 번호를 가지며 john여전히 john콘텐츠를 가질 것으로 예상됩니다 john. 나는 이것이 나를 놀라게 했다. 파일을 대상 경로에 복사하면 paul공유 inode paul의 다른 모든 대상 경로에도 동일한 파일(동일한 inode)이 설치되는 것으로 나타났습니다. 나는 이것이 cp새 파일을 생성하고 이전 파일이 이전에 차지했던 위치로 이동할 것이라고 생각했습니다 paul. 대신 기존 파일을 열고 paul잘라낸 다음 george기존 파일에 내용을 쓰는 것으로 보입니다 . 따라서 동일한 inode를 가진 "다른" 파일은 "그들의" 내용을 동시에 업데이트하게 됩니다.

글쎄, 이것은 시스템 동작이며 이제 이런 일이 발생한다는 것을 알았으므로 문제를 해결하는 방법을 알아내거나 적절한 경우 이를 활용할 수 있습니다. 혼란스러운 점은 이 동작이 문서화되어 있는 곳을 어디에서 볼 수 있느냐는 것입니다. 문서화되지 않았다면 놀랄 것입니다어딘가에제가 본 문서에는요. 그러나 분명히 나는 ​​그것을 놓쳤고 지금은 이 동작을 논의하는 소스를 찾을 수 없습니다.

답변1

cp설명서에는 대상 파일이 이미 존재하는 경우 해당 파일을 덮어쓴다는 내용이 나와 있습니다. 맞습니다. "재정의"가 무엇을 의미하는지 지정하지 않지만 "교체"가 아니라 "덮어쓰기"라고 확실히 나와 있습니다. 현명하게 말하면 "재정의"가 cp바로 그 일을 하고 원하는 동작을 "대체"라고 부르는 것이 적절하다고 말할 수 있습니다.

또한 cp기존 개체 파일을 "교체"하는 경우 이는 "덮어쓰기"보다 더 심각하거나 놀랍거나 잘못된 것으로 간주될 수 있습니다. 예를 들어:

  • 이전 파일을 먼저 삭제한 다음 새 파일을 생성 하면 파일이 존재하지 않는 기간이 있다는 cp것은 놀라운 일입니다 .
  • 임시 파일이 먼저 생성된 다음 제자리로 이동 되면 cp이상한 이름을 가진 임시 파일이 가끔 발견되기 때문에 이를 기록해야 하지만 그렇지 않습니다.
  • 권한 문제로 인해 이전 파일과 동일한 디렉터리에 새 파일을 생성할 수 없다면 cp불행한 일이 될 것입니다 (특히 이전 파일을 삭제한 경우).
  • 파일이 실행 중인 사용자에게 속하지 않고 실행 중인 사용자에게도 속하지 않는 경우 cpcp파일 root의 소유자 및 권한을 새 파일의 소유자 및 권한과 일치시키는 것이 불가능합니다.
  • 파일에 cp알려지지 않은 특수 속성이 있는 경우 이러한 속성은 복사본에서 손실됩니다. 요즘에는 의 구현이 cp확장 속성과 같은 항목을 안정적으로 이해할 수 있어야 하지만 항상 그런 것은 아닙니다. MacOS 리소스 포크나 원격 파일 시스템과 같은 다른 것들도 기본적으로 있습니다.

cp요약하자면, 이제 이것이 실제로 무엇을 하는지 알게 되었습니다 . 다시는 이것에 놀라지 않을 것입니다! 솔직히 말해서, 수년 전에도 같은 일이 나에게 일어났을 수도 있다고 생각합니다.

답변2

POSIX 2013 표준이 보입니다.관찰된 동작을 지정합니다.. 그것은 말한다:

  1. 만약에소스 파일일반 파일 형식인 경우 다음 단계를 수행해야 합니다.

    ㅏ. ... 만약에대상 파일존재하는 경우 다음과 같은 조치를 취해야 합니다.

    나. 이 -i옵션이 적용되는 경우 cp유틸리티는 표준 오류에 대한 프롬프트를 작성하고 표준 입력에서 한 줄을 읽어야 합니다. 대답이 부정적이면 cp더 이상의 조치가 취해지지 않습니다. 소스 파일나머지 파일을 계속 처리합니다.

    2. 파일 설명자대상 파일open()POSIX.1-2008의 시스템 인터페이스 볼륨에 정의된 기능과 동등한 작업을 수행하여 얻어야 합니다.대상 파일경로 인수로, 비트 단위 로 OR포함 O_WRONLYO_TRUNC초과분토론.

    3. 파일 설명자를 얻으려는 시도가 실패하고 이 옵션이 적용되는 경우 POSIX.1-2008 시스템 인터페이스 볼륨에 정의 -f된 기능과 동일한 작업을 수행하여 파일을 삭제하려고 시도해야 합니다.cpunlink()대상 파일경로 매개변수로. 이 시도가 성공하면 cp3b단계로 진행해야 합니다.

    ...

    d의 내용.소스 파일파일 설명자를 작성해야 합니다. 쓰기 오류로 인해 cp진단 메시지가 표준 오류에 기록되고 3e단계로 진행됩니다.

    e. 파일 설명자를 닫아야 합니다.

답변3

우선, 왜 이런 일을 하는가? 그 이유 중 하나는 역사적입니다. 그것이 일이 이루어진 방식입니다.유닉스의 첫 번째 버전에서.

파일은 쌍으로 제공됩니다. 첫 번째는 읽기용으로 열리고 두 번째는 모드 17을 생성합니다. 그런 다음 첫 번째 항목을 두 번째 항목에 복사합니다.

"만들다"는 뜻은creat시스템 호출(예:e가 빠진 것은 잘 알려져 있습니다.)가 있는 경우 해당 이름으로 기존 파일을 자릅니다.

그리고여기cpUnix 두 번째 버전의 소스 코드 입니다 (첫 번째 버전의 소스 코드는 찾을 수 없습니다). open소스 파일과 두 번째 파일에 대한 호출을 볼 수 있으며 creat, 첫 번째 버전에 비해 개선된 사항으로 두 번째 파일이 기존 디렉터리인 경우 cp해당 디렉터리에 파일이 생성됩니다.

그런데 그때 왜 이런 일을 했느냐고 물을 수도 있습니다. "애초에 유닉스가 왜 이런 짓을 했는가"에 대한 대답은 거의 항상 간단합니다. cp읽기 위해 소스 열기 및 대상 만들기 - 파일을 생성하는 시스템 호출은 쓰기 위해 파일을 열어 기존 파일을 덮어씁니다. 존재하거나 존재하지 않습니다.

이제 문서화된 위치에 대해 설명합니다.FreeBSD 매뉴얼 페이지.

기존의 각 대상 파일에 대해 권한이 허용되면 해당 내용을 덮어씁니다. -p 옵션을 지정하지 않으면 모드, 사용자 ID, 그룹 ID가 변경되지 않습니다.

이런 문구가 있군요최소 1990년으로 거슬러 올라감(당시 BSD는 4.3BSD였습니다.) 인터넷에도 비슷한 말이 있는데솔라리스 10:

target_file이 존재하는 경우 cp는 해당 내용을 덮어쓰지만 관련 스키마(및 해당되는 경우 ACL), 소유자 및 그룹은 변경되지 않습니다.

당신의 경우에도HP-UX 10수동:

new_file이 다른 링크가 있는 기존 파일에 대한 링크인 경우 기존 파일을 덮어쓰고 모든 링크가 유지됩니다.

POSIX는 이를 표준 언어로 표현합니다. 에서 인용단일 UNIX v2:

dest_file이 존재하면 다음 단계가 수행됩니다. (…) dest_file의 파일 디스크립터는 oflag 매개변수로 획득됩니다.

내가 인용한 매뉴얼 페이지와 사양에는 -f해당 옵션이 전달되고 대상 파일을 열거나 생성하려는 시도가 실패하는 경우(일반적으로 파일에 쓸 수 있는 권한이 없기 때문에) cp대상을 삭제하려고 시도하고 파일을 다시 생성해 보세요. 이렇게 하면 장면의 하드 링크가 끊어집니다.

문서 버그를 보고할 수도 있습니다.GNU coreutils 매뉴얼, 이 동작을 문서화하지 않기 때문입니다. --preserve=links(귀하의 시나리오에서는 paul링크가 제거되고 새 파일이 생성됨) 에 대한 설명조차도 그렇지 않으면 어떤 일이 발생할지 명시적으로 밝히지 않습니다 --preserve=links. -f("이 옵션이 없으면 --force를 사용할 때 복사가 실패합니다...")

답변4

"파일을 대상 경로에 복사하면 해당 inode를 공유하는 다른 모든 대상 경로 paul 에도 동일한 파일(동일한 inode)이 복사됩니다 paul."라고 말할 수 있다면 죄송합니다. 하드 링크가 좋습니다. 내가 매카트니 경에게 사과를 줬다면 폴에게는 사과를 줬고, 존 레논의 작곡 파트너에게는 사과를 줬다. 하지만 저는 아직 사과 세 개를 나눠주지 않았습니다. 이름/직함/설명이 여러 개인 사람에게 사과 한 개를 주었습니다.

마찬가지로 george에 복사 paul하면반품john대신에 해당 george디렉토리 항목이 해당 inode를 가리키는 파일에 데이터를 복사합니다 paul.

단계별:  당신이 할 때

echo john > john

john새 파일을 생성했습니다(지정된 파일이 디렉터리에 아직 없다고 가정). 또는 더 엄밀히 말하면 해당 이름을 가진 디렉터리 항목이 디렉터리에 이미 존재하지 않는다고 가정합니다 john(엄격히 말하면 디렉터리에 파일이 없고 inode를 가리키는 디렉터리 항목만 있으므로). 마친 후

cp -l john paul

또는

ln john paul

새 파일을 생성하지 않은 대신 기존 파일에 새 이름을 지정했습니다. 이제 두 개의 이름을 가진 파일이 생겼습니다: johnpaul. 네가 말할 때

cp george paul

당신이 덮고 있어요그 파일. 이름이 두 개 있다는 사실은 중요하지 않습니다. 이름이 42개일 수도 있고 액세스할 수 없는 위치에 있을 수도 있으며 이 명령은 george\n해당 이름(경로) 모두에 데이터를 복사하지 않습니다. 데이터가 도착하면 복사됩니다.파일이름이 여러 개 있습니다.

관련 정보