[server] Linux : 파일을 동시에 입력 및 출력으로 사용하는 방법은 무엇입니까?

방금 bash에서 다음을 실행했습니다.

uniq .bash_history > .bash_history

내 역사 파일이 완전히 비워졌습니다.

쓰기 전에 전체 파일을 읽는 방법이 필요하다고 생각합니다. 어떻게됩니까?

추신 : 분명히 임시 파일을 사용하려고 생각했지만 더 우아한 해결책을 찾고 있습니다.



답변

moreutilssponge 에서 사용 하는 것이 좋습니다 . 맨 페이지에서 :

DESCRIPTION
  sponge  reads  standard  input  and writes it out to the specified file. Unlike
  a shell redirect, sponge soaks up all its input before opening the output file.
  This allows for constructing pipelines that read from and write to the same 
  file.

이것을 문제에 적용하려면 다음을 시도하십시오.

uniq .bash_history | sponge .bash_history


답변

나는 단지 간단하고 스폰지를 사용하지 않는 또 다른 답변을 제공하고 싶었습니다 (가벼운 환경에 종종 포함되지 않기 때문에).

echo "$(uniq .bash_history)" > .bash_history

원하는 결과가 있어야합니다. 서브 쉘은 쓰기 위해 .bash_history를 열기 전에 실행됩니다. Phil P의 답변에서 설명했듯이 원래 명령에서 .bash_history를 읽을 때까지 이미 ‘>’연산자로 잘 렸습니다.


답변

문제는 명령을 실행하기 전에 쉘이 명령 파이프 라인을 설정하고 있다는 것입니다. 그것은 “입력 및 출력”의 문제가 아니며, 파일의 내용이 이미 uniq가 실행되기 전에 이미 사라졌기 때문입니다. 그것은 다음과 같습니다 :

  1. 쉘은 >출력을 위해 출력 파일을 열어서 잘 립니다.
  2. 쉘은 그 출력에 파일 디스크립터 1 (stdout)이 사용되도록 설정합니다.
  3. 쉘은 아마도 execlp ( “uniq”, “uniq”, “.bash_history”, NULL)와 같은 uniq을 실행합니다.
  4. uniq가 실행되고 .bash_history가 열리고 아무것도 발견되지 않습니다.

내부 편집 및 다른 사람들이 언급 한 임시 파일 사용을 포함하여 다양한 솔루션이 있지만 문제의 핵심은 실제로 문제가 발생하는 이유와 이유를 이해하는 것입니다.


답변

을 사용하지 않고 이것을 수행하는 또 다른 트릭 sponge은 다음 명령입니다.

{ rm .bash_history && uniq > .bash_history; } < .bash_history

이것은 backreference.org 에서 파일의 “In-place”편집 에 관한 훌륭한 기사에서 설명한 속임수 중 하나입니다 .

기본적으로 읽을 파일을 연 다음 “제거”합니다. 그러나 실제로 제거되지는 않습니다. 파일을 가리키는 열린 파일 설명자가 있으며 열려있는 한 파일은 여전히 ​​존재합니다. 그런 다음 이름이 같은 새 파일을 작성하고 고유 한 행을 작성합니다.

이 솔루션의 단점 : uniq어떤 이유로 든 실패하면 기록이 사라집니다.


답변

moreutils의 스폰지 사용

uniq .bash_history | sponge .bash_history


답변

sed스크립트는 인접한 중복을 제거합니다. 이 -i옵션을 사용하면 수정이 제자리에서 수행됩니다. sed info파일 에서 온 것입니다 .

sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history


답변

흥미로운 tidbit로서, sed는 임시 파일도 사용합니다 (이것은 당신을 대신합니다).

$ strace sed -i 's/foo/bar/g' foo    
open("foo", O_RDONLY|O_LARGEFILE)       = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096)               = 4
write(4, "bar\n"..., 4)                 = 4
read(3, ""..., 4096)                    = 0
close(3)                                = 0
close(4)                                = 0
rename("./sedPmPv9z", "foo")            = 0
close(1)                                = 0
close(2)                                = 0

설명 :
임시 파일 ./sedPmPv9z은 fd 4가되고 foo파일은 fd 3이됩니다. 읽기 작업은 fd 3에 있고 fd 4 (임시 파일)에 기록됩니다. 그런 다음 이름 바꾸기 호출에서 임시 파일로 foo 파일을 덮어 씁니다.