[unix] IO 리디렉션 및 헤드 명령

.hgignore오늘 Cygwin bash 쉘에서 파일 을 신속하게 편집하려고 시도했지만 실수 한 줄을 추가했습니다. 이것이 최선의 방법인지 확실하지 않지만 head -1 .hgignore문제를 일으키는 줄을 제거하는 데 빨리 생각했습니다 (이전에 파일에 한 줄만있었습니다). 물론 실행될 때 첫 번째 줄을 유일한 출력으로 제공합니다.

그러나 출력을 리디렉션하고을 사용하여 파일을 다시 쓰려고 할 head -1 .hgignore > .hgignore때 파일이 비어있었습니다. 왜 이런 일이 발생합니까? 대신을 추가하려고하면 head -1 .hgignore >> .hgignore올바르게 추가되지만 원하는 결과는 아닙니다. 이 경우 잘림 리디렉션이 작동하지 않는 이유는 무엇입니까?



답변

쉘이 다음과 같은 명령 행을 받으면 : command > file.out쉘 자체가라는 파일을 열고 (아마도 작성합니다) file.out. 쉘은 파일 디스크립터 0을 파일 파일 디스크립터로 설정합니다. 이것이 I / O 리디렉션의 작동 방식입니다. 모든 프로세스는 파일 디스크립터 0, 1 및 2에 대해 알고 있습니다.

이것에 대한 어려운 부분은 여는 방법file.out 입니다. 대부분의 경우 file.out오프셋 0 (즉, 잘림)으로 쓰기 위해 열려고합니다. 이것이 쉘이 수행 한 작업입니다. .hgignore를 자르고, 쓰기 위해 열었고, 파일 기술자를 0으로 dup 한 다음 exec’ed했습니다 head. 즉각적인 파일 클로버 링.

bash 셸에서는 set noclobber이 동작을 변경합니다.


답변

Bruce 는 쉘 파이프 라인 으로 여기서 무슨 일이 일어나고 있는지 대답 합니다 .

내가 가장 좋아하는 작은 유틸리티 중 하나는 moreutilssponge명령입니다 . 대상 출력 파일을 열고 데이터를 쓰기 전에 사용 가능한 모든 입력을 “잠글”하여이 문제를 정확하게 해결합니다. 예상대로 정확하게 파이프 라인을 작성할 수 있습니다.

$ head -1 .hgignore | sponge .hgignore

가난한 사람의 해결책은 출력을 임시 파일로 파이프하는 것입니다. 그런 다음 pipline이 완료된 후 (예를 들어 다음 명령 실행) 임시 파일을 원래 파일 위치로 다시 이동하는 것입니다.

$ head -1 .hgingore > .hgignore.tmp
$ mv .hgignore{.tmp,}


답변

head -n 1 file > file

filehead시작 하기 전에 잘립니다 . 그러나 작성하는 경우 :

head -n 1 file 1<> file

file읽기-쓰기 모드에서 열리지 않습니다 . 그러나 head쓰기가 끝나면 파일을 자르지 않으므로 위의 줄은 작동하지 않습니다 ( head첫 번째 줄을 다시 작성하고 다른 줄은 그대로 두십시오).

그러나을 head반환 한 후 fd가 여전히 열려있는 동안 을 수행하는 다른 명령을 호출 할 수 있습니다 truncate.

예를 들어 :

{ head -n 1 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file

여기서 중요한 것은 truncatehead의 첫 번째 줄 바로 다음에서 파일 내에서 fd 1에 대한 커서를 옮기는 것입니다. 우리가 필요로하지 않은 첫 번째 줄을 다시 작성하지만 해롭지는 않습니다.

POSIX 헤드를 사용하면 첫 번째 줄을 다시 쓰지 않고도 실제로 벗어날 수 있습니다.

{ head -n 1 > /dev/null
  perl -e 'truncate STDIN, tell STDIN'
} <> file

여기서는 head커서 위치를 stdin으로 이동시키는 사실을 사용하고 있습니다. 하지만 head일반적으로 성능을 향상시키기 위해 큰 덩어리로 입력을 읽을 것, POSIX는 그것을 (수) 필요 seek그것을 넘어 들어갔 더라면 바로 첫 번째 줄 이후에 백업 할 수 있습니다. 그러나 모든 구현이 그렇지는 않습니다.

또는 read이 경우 대신 쉘 명령을 사용할 수 있습니다 .

{ read -r dummy; perl -e 'truncate STDIN, tell STDIN'; } <> file


답변

진짜 남자의 해결책은

ed .hgignore
$d
wq

또는 하나의 라이너

printf '%s\n' '$d' 'wq' | ed .hgignore

또는 GNU sed와 함께 :

sed -i '$d' .hgignore

(아니요, 농담입니다. 대화 형 편집기를 사용하겠습니다. vi .hgignore GddZZ)


답변

Ex 모드에서 Vim을 사용할 수 있습니다 :

ex -sc '2,d|x' .hgignore
  1. 2, 끝까지 라인 2를 선택

  2. d 지우다

  3. x 저장하고 닫습니다


답변

전체 파일 편집을 위해 Jürgen Hötzel이 sed ‘s / c / d /’myFile에서 myFile으로 경로 재 지정 출력 에 표시된대로 열린 파일 핸들 트릭을 사용할 수도 있습니다 .

exec 3<.hgignore
rm .hgignore  # prevent open file from being truncated
head -1 <&3 > .hgignore

ls -l .hgignore  # note that permissions may have changed


답변