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


20

나는 하드 링크의 개념을 잘 이해하고 있으며 cp--- 심지어 최신 POSIX 사양과 같은 기본 도구에 대한 매뉴얼 페이지를 여러 번 읽었 습니다. 여전히 나는 다음 행동을 관찰하는 것에 놀랐습니다.

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

이 시점에서 johnpaul같은 아이 노드 (및 함량)를 가질 것이며, george두 점에서 다르다. 이제 우리는 :

$ cp george paul

이 시점에서 나는 예상 georgepaul다른 inode 수 있지만 동일한 내용 ---이 기대가 성취되었다을 가지고 ---하지만이 또한 예상되는 paul지금과는 다른 inode 번호를 가지고 john와에 대한, john여전히 콘텐츠를에 john. 내가 놀란 곳입니다. 대상 경로로 파일을 복사하면 paulinode를 공유하는 다른 모든 대상 경로에 동일한 파일 (동일한 inode)을 설치 한 결과 가 나옵니다 paul. cp새 파일 을 만들어 이전 파일이 있던 위치로 옮겼습니다 paul. 대신 기존 파일을 열고 paul자르고 쓰는 것입니다.george해당 파일을 기존 파일에 넣습니다. 따라서 동일한 inode를 가진 "기타"파일은 "그들"컨텐츠를 동시에 업데이트합니다.

좋아, 이것은 체계적인 행동이며 이제는 그것을 해결하는 방법을 알아낼 수 있거나 적절하게 활용할 수 있습니다. 이 행동이 문서화되어있는 곳은 무엇입니까? 이미 본 문서 어딘가에 문서화되어 있지 않으면 놀랍 습니다. 그러나 분명히 그것을 놓 쳤고 이제는이 동작을 설명하는 출처를 찾을 수 없습니다.

답변:


4

첫째, 왜 이런 식으로 이루어 집니까? 한 가지 이유는 역사적입니다. 이것이 Unix First Edition에서 수행 방식 입니다.

파일은 쌍으로 이루어집니다. 첫 번째는 읽기 위해 열리고 두 번째는 작성된 모드 17입니다. 그런 다음 첫 번째는 두 번째로 복사됩니다.

"Created"는 creat시스템 호출 ( e가 빠진 것으로 유명한 시스템 호출)을 말하며 , 기존 파일이 있으면 기존 이름을 잘라냅니다.

그리고 여기 '의 소스 코드이야 cp유닉스 두 번째 판에서 (나는 초판의 소스 코드를 찾을 수 없습니다). open소스 파일과 creat두 번째 파일에 대한 호출을 볼 수 있습니다 . First Edition의 개선으로, 두 번째 파일이 기존 디렉토리 인 경우 cp해당 디렉토리에 파일을 작성합니다.

그러나 왜 당시에는 그렇게 되었습니까? “유닉스가 왜 그렇게했는지”에 대한 대답은 거의 항상 단순합니다. cp읽기위한 소스를 열고 대상을 만듭니다. 파일을 작성하기위한 시스템 호출은 파일을 이미 존재하는지 여부에 관계없이 호출자가 주어진 이름으로 파일의 컨텐츠를 부과 할 수 있기 때문에 파일을 작성하여 기존 파일을 덮어 씁니다. 아니.

이제 FreeBSD 매뉴얼 페이지 에 문서화 된 위치가 있습니다 .

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

이 표현은 적어도 1990 년 (BSD가 4.3BSD 일 때)에 존재했습니다. Solaris 10 에는 비슷한 문구가 있습니다 .

target_file이 있으면 cp는 내용을 덮어 쓰지만 이와 관련된 모드 (및 해당되는 경우 ACL), 소유자 및 그룹은 변경되지 않습니다.

귀하의 사례는 HP-UX 10 설명서 에도 나와 있습니다.

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

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

dest_file이 존재하면 다음 단계를 수행합니다. (…) dest_file을 경로 인수로 사용하여 호출 된 XSH 스펙 open () 함수와 O_WRONLY 및 O_TRUNC의 비트 단위 OR을 사용하여 호출 된 XSH 스펙 open () 함수와 동일한 조치를 수행하여 dest_file에 대한 파일 디스크립터를 가져옵니다. oflag 인수로.

내가 인용 한 매뉴얼 페이지와 사양은 -f옵션이 전달되고 대상 파일을 열거 나 만들려는 시도가 실패하는 경우 (일반적으로 파일을 쓸 수있는 권한이 없기 때문에) cp대상을 제거하고 파일을 다시 만들려고합니다. . 시나리오에서 하드 링크가 끊어 질 수 있습니다.

GNU coreutils manual 에 대해이 버그를 문서화하지 않았기 때문에 문서 버그를보고하고자 할 수 있습니다 . --preserve=links귀하의 시나리오에서 paul링크가 제거되고 새 파일이 생성되는 ,에 대한 설명조차도 --preserve=links. -f종류에 대한 설명은 해당 내용 없이 발생하는 내용을 의미하지만 철자가 아닙니다 (“이 옵션을 사용하지 않고 복사하고 기존 대상 파일을 쓰기 위해 열 수 없으면 복사가 실패합니다. 그러나 --force,…”).


왜 "호출자가 파일의 존재 여부에 관계없이 파일 이름의 소유권을 가질 수 있기 때문에"라고 말합니까? Cp는 기존 파일의 소유권을 가지지 않습니다.
jrw32982는

@ jrw32982 나는 파일 메타 데이터의 의미가 아니라 파일에 들어갈 내용을 결정한다는 의미에서 소유권을 의미했다. 나는 그 문장을 다시 썼다.
Gilles 'SO- 악마 그만'

20

cp대상 파일이 이미 존재하는 경우 대상 파일을 덮어 쓰는 문서. "덮어 쓰기"의 의미를 자세하게 지정하지는 않지만 "바꾸기"가 아니라 "덮어 쓰기"라는 메시지가 표시됩니다. 만약 당신이 pedantic하고 싶다면, 당신은 "덮어 쓰기"가 정확히 무엇 cp이며, 당신이 기대했던 행동은 "바꾸기"라고 적절히 주장 할 수 있습니다.

또한 cp기존의 대상 파일을 "대체"하는 것이 합리적이거나 부정확 한 것으로 간주 될 수 있으며 아마도 "덮어 쓰기"가 아니라는 점에 유의하십시오 . 예를 들면 다음과 같습니다.

  • 경우 cp먼저 이전 파일을 삭제 한 후 새로 생성 한 후 놀라운 일이 될 것 파일이 존재하지 않을 것이다 동안 시간의 간격이있을 것입니다.
  • 경우 cp첫 번째 임시 파일을 만든 다음 장소에 이동 그때는 아마 이상한 이름을 가진 임시 파일이 종종 발견 될 수 있다는 사실에 때문에,이 문서해야한다 ...하지만 그렇지 않습니다.
  • cp권한으로 인해 이전 파일과 동일한 디렉토리에 새 파일을 작성할 수없는 경우 불행히도 (특히 이전 파일을 이미 삭제 한 경우) 안됩니다.
  • 실행중인 사용자가 파일을 소유하지 않고 실행중인 cp사용자 cp가 아닌 root경우 새 파일의 소유자 및 권한을 새 파일의 소유자 및 권한과 일치시킬 수 없습니다.
  • 파일에 알려지지 cp않은 멋진 특수 속성 이 있으면 사본에서 손실됩니다. 오늘날의 구현은 cp확장 된 속성과 같은 것을 확실하게 이해해야하지만 항상 그런 것은 아닙니다. 그리고 MacOS 리소스 포크 나 원격 파일 시스템과 같은 다른 것들도 기본적으로 있습니다.

결론 : 이제 cp실제로 무엇을하는지 알 수 있습니다. 다시는 놀라지 않을 것입니다! 솔직히, 나는 몇 년 전에 나에게도 같은 일이 일어났다 고 생각합니다.


POSIX의 참조를 확인해야하지만, 사실에 man대한 페이지 cp(적어도, OSX) BSD에와의는 GNU 버전은 cp"덮어 쓰기"에 대해 너무 명시 적으로하지 않습니다. 이 단어는 옵션 -i및 에 대한 주석에만 사용됩니다 -n. Gnu 맨 페이지는 특히 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.In the first synopsis form, the cp utility copies the contents of the source_file to the target_file.
유익하지 않습니다

Gnu coreutils 정보 페이지가 시작됩니다 :‘cp’ copies files (or, optionally, directories). The copy is completely independent of the original.
dubiousjim

2
POSIX 2008 표준은 관찰 된 동작을 지정합니다. 답변을 추가하겠습니다.
dubiousjim

16

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

  1. 경우 source_file이 유형의 일반 파일이며, 다음 단계를 취하여야한다 :

    에이. ... dest_file 이 존재하면 다음 단계를 수행해야합니다.

    나는. 경우 -i옵션이 적용되면, cp유틸리티는 표준 오류에 프롬프트를 작성하고 표준 입력으로부터 한 줄을 읽어하여야한다. 응답이 긍정이 아닌 경우 source_file로cp 더 이상 아무것도하지 않고 나머지 파일로 넘어갑니다.

    ii. 위한 파일 디스크립터 dest_file을 받는 작업이 상당하여 얻어진한다 open()하여 호출 POSIX.1-2008의 시스템 인터페이스 체적 정의 함수 dest_file를 경로를 인수로하고, 비트 포괄적 ORO_WRONLYO_TRUNC는 AS oflag의 인수.

    iii. 파일 디스크립터 확보 시도가 실패하고 -f옵션이 유효한 경우, dest_file 을 경로 인수로 사용하여 호출 된 POSIX.1-2008의 시스템 인터페이스 볼륨에 정의 된 기능 cp과 동등한 조치를 수행하여 파일 제거를 시도해야합니다 . 이 시도가 성공하면 3b 단계를 계속 진행합니다.unlink()cp

    ...

    디. source_file 의 내용은 파일 디스크립터에 기록되어야한다. 모든 쓰기 오류는 cp진단 메시지를 표준 오류에 기록하고 3e 단계를 계속합니다.

    이자형. 파일 기술자는 닫혀 져야한다.


1
흥미 롭군 당신처럼, 나는 cp비슷한 결과를 낼 것이라고 가정 하고 mv, dest가 속한 하드 링크를 끊습니다. 그러나 이제 그것에 대해 생각하기 때문에 구체적 unlink(2)으로 대상 ( cp -f)을 지정하거나 다른 이름의 임시를 만든 다음 의미 rename(2)해야합니다. 간단한 구현은 POSIX에 필요한 덮어 쓰기를 위해 파일을 여는 것입니다. 그것은 동등의cat src > dest
피터 코르

2

"파일을 대상 경로에 paul 복사하면 동일한 파일 (동일한 inode)이 다른 paulinode 를 공유하는 다른 모든 대상 경로 로 복사 됩니다."라고 말할 수 있다면, 하드 링크가 아주 좋습니다. 매카트니 경에게 사과를 주면 폴에게 사과를 주었고 존 레논의 작곡 파트너에게 사과를주었습니다. 그러나 나는 사과 3 개를주지 않았다. 이름 / 제목 / 설명자가 여러 개인 사람에게 사과를주었습니다.

복사 할 때 유사하게, georgepaul, 당신은되지 않습니다 또한 에 복사 john. 오히려 디렉토리 항목 george이 inode를 가리키는 파일로 데이터를 복사하고 있습니다 paul.

단계별 :   할 때

echo john > john

john해당 디렉토리에 이름이 지정된 파일이 없다고 가정하여 새 파일을 작성했습니다 . 또는 더 엄격하게 말하면, john해당 디렉토리에 이름 을 가진 디렉토리 항목이 이미 없다고 가정합니다 (엄밀히 말하면 디렉토리에는 파일이 없기 때문에 inode를 가리키는 디렉토리 항목 만). 당신이 한 후

cp -l john paul

또는

ln john paul

새 파일을 만들지 않았습니다. 오히려 기존 파일에 새로운 이름을 부여했습니다. 이제 두 개의 이름을 가진 파일이 있습니다 : johnpaul. 그리고 당신이 말할 때

cp george paul

그 파일을 덮어 쓰고 있습니다 . 이름이 두 개라는 사실은 관련이 없습니다. 액세스 할 수없는 위치에 42 개의 이름이있을 수 있으며이 명령은 george\n모든 해당 이름 (경로)에 데이터를 복사하지는 않습니다 . 여러 이름을 가진 하나의 파일로 데이터를 복사하는 것입니다 .


1
감사. 그래, 나는 그것을 쓴 내가 쓰고 있었는지의 공포 따옴표 필요한 문자를 알고 있었다 : johnpaul같은 파일에 대한 두 가지 경로 이름으로 시작합니다. 하지만 제가 생각하기에 가장 쉬운 방법이었습니다. 나는 정확히 이해 된 하드 링크의 개념이 (없이 )에 대한 두 가지 행동 중 하나를 지시 한다고 생각하지 않습니다 . cp-l
dubiousjim

그러나 prodding 주셔서 감사합니다; 나는 문구를 명확히하려고 노력했다.
dubiousjim
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.