IO 리디렉션 및 헤드 명령


9

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

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


답변:


10

쉘이 다음과 같은 명령 행을 받으면 : 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이 동작을 변경합니다.


아하 명령을 실행하기 전에 셸이 파일을 자르고 있다고 생각했지만 그 이유를 알지 못했습니다. 설명 주셔서 감사합니다!
voithos 2016 년

10

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

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

$ head -1 .hgignore | sponge .hgignore

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

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

몇 년 후이 보면, 생각이 나에게 발생 : 우리가 할 수 없었 head -1 .hgignore | tee .hgignore? tee은 coreutils에 있으며, 특권 / 부작용으로, 이것은 또한 STDOUT에 기록합니다
voithos

@voithos 내 지식으로 tee는 다른 모든 것처럼 인스턴스화 될 때 쓰고있는 파일을 열고 자르므로 쓰기로 파일을 자르기 전에 파일 내용을 읽는 경쟁 조건의 주요 문제를 해결하지 못합니다.
Caleb

내가 알지 못했던 지점, 즉 파이프 명령이 순차적이 아닌 즉시 시작 된다는 점을 알 수 있습니다. 정확합니까? 그러나 나는 그것을 테스트 하고 원하는 것을하는 tee 것처럼 보입니다 . 8.13내 컴퓨터에 버전이 있습니다.
voithos

1
pipline의 @voithos Yes 명령과 관련된 모든 입력 / 출력 채널은 역순으로 시작되므로 파이프 라인은 데이터를 처음 시작할 때 데이터를 수신 할 준비가됩니다. 아마도 너무 작은 데이터 덩어리를 사용했기 때문에 테스트에 결함이 있다고 생각합니다. 필요하기 전에 모든 것이 읽기 버퍼에 캐시되어 있습니다. tee프로그램은 그 버퍼 두 배로 설정되지 않습니다, 파일을 자릅니다.
Caleb

3

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

1
Stephane, STDIN당신은 perl위에서 사용했던 것과 비슷하게 잘라낼 수있는 표준 또는 coreutils 명령을 알고
있습니까

2
@ 1_CR, 아니요. 파일의 dd임의의 절대 오프셋에서 자를 수 있습니다 . 따라서 두 번째 라인의 바이트 오프셋을 결정하고 다음과 같이 잘라낼 수 있습니다.dd bs=1 seek="$offset" of=file
Stéphane Chazelas

1

진짜 남자의 해결책은

ed .hgignore
$d
wq

또는 하나의 라이너

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

또는 GNU sed와 함께 :

sed -i '$d' .hgignore

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


나는 :wq이상 을 사용하면 어떤 이점이 ZZ있습니까?
voithos 2016 년

또한, :x이는 내 손가락이 자동으로 할 것입니다
글렌 잭맨

ZQ동일하다:q!
글렌 잭맨

ZZ 및 : x는 쓸 내용이있는 경우에만 쓰기 ... : w는 파일이 필요한지 여부에 관계없이 항상 파일을 디스크에 동기화합니다. 탭을 사용하기 때문에 : xa를 사용합니다.
xenoterracide

1

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

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

  2. d 지우다

  3. x 저장하고 닫습니다


0

전체 파일 편집을 위해 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

2
그리고 rm .hgignore정전 후 몇 시간의 노력을 앗아갑니다. 좋아, 그것은 중요하지 .hgignore않지만 왜 어쨌든 복잡한 일을 하시겠습니까? 따라서 나의 downvote : 기술적으로 정확하지만 매우 나쁜 생각입니다.
Gilles 'SO- 악의를 멈춰라'

@Gilles, 그다지 좋지 않은 아이디어이지만, 예를 들어 perl -i(인플레 이스 편집의 경우) 수행하는 작업이며, 일부 sed -i최신 버전의 구현이 그렇게 해도 놀랍지 않습니다 (최신 버전의 GNU sed는 그렇지 않음).
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.