bash를 사용하여 파일 (인수)을 "제자리"에서 편집하는 명령을 어떻게 실행합니까?


110

sortbash 의 명령 으로 정렬하려는 temp.txt 파일이 있습니다 .

정렬 된 결과가 원본 파일을 대체하고 싶습니다.

예를 들어 이것은 작동하지 않습니다 (빈 파일이 생겼습니다).

sortx temp.txt > temp.txt

임시 파일에 복사하지 않고 한 줄로이 작업을 수행 할 수 있습니까?


편집 :이 -o옵션은 sort. 내가 사용하는 sort예를 들어 내 질문에. 다른 명령과 동일한 문제가 발생합니다.

uniq temp.txt > temp.txt.

더 나은 일반 솔루션이 있습니까?


답변:


171
sort temp.txt -o temp.txt

3
이것은 대답입니다. 이 문제에 대한 일반적인 해결책이 있는지 실제로 궁금합니다. 예를 들어 "in place"파일에서 모든 UNIQ 줄을 찾으려면 -o
— jm을

일반적인 것은 아니지만 -u를 GNU 정렬과 함께 사용하여 고유 한 줄을 찾을 수 있습니다.
— James

누구든지 예를 들어 문제를 해결 sort --inplace *.txt했습니까? 그건 미친 멋진 것
— sehe

@sehe이 시도 :find . -name \*.txt -exec sort {} -o {} \;
— 키스 고간

29

A sort는 출력을 시작하기 전에 모든 입력을 확인해야합니다. 이러한 이유로 sort프로그램은 파일을 내부에서 수정하는 옵션을 쉽게 제공 할 수 있습니다.

sort temp.txt -o temp.txt

구체적으로, GNU 문서sort 는 다음 과 같이 말합니다.

일반적으로 sort는 출력 파일을 열기 전에 모든 입력을 읽으므로 sort -o F F및 같은 명령을 사용하여 파일을 제자리에 안전하게 정렬 할 수 있습니다 cat F | sort -o F. 그러나, sort와 --merge( -m) 명령 같은, 모든 입력을 읽기 전에 출력 파일을 열 수 있습니다 cat F | sort -m -o F - G종류의 쓰기 시작 하듯이 안전하지 F전에 cat를 읽어 이루어집니다.

BSD의 문서 sort는 다음 과 같이 말합니다.

[the] output-file이 입력 파일 중 하나 인 경우 sort는 출력을 정렬하고 [the] output-file에 쓰기 전에 임시 파일에 복사합니다.

과 같은 명령 uniq은 입력 읽기를 완료하기 전에 출력 쓰기를 시작할 수 있습니다. 이러한 명령은 일반적으로 내부 편집을 지원하지 않으며이 기능을 지원하기가 더 어렵습니다.

일반적으로 임시 파일을 사용하여이 문제를 해결하거나 중간 파일을 사용하지 않으려는 경우 버퍼를 사용하여 전체 결과를 저장하기 전에 작성할 수 있습니다. 예를 들면 다음과 perl같습니다.

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

여기서 perl 부분은 uniqin 변수 에서 전체 출력을 읽은 $_다음이 데이터로 원본 파일을 덮어 씁니다. 선택한 스크립팅 언어, 아마도 Bash에서도 똑같이 할 수 있습니다. 그러나 전체 파일을 저장하려면 충분한 메모리가 필요하므로 대용량 파일로 작업 할 때는 권장되지 않습니다.


19

여기에 좀 더 일반적인 접근 방식이 있으며 uniq, sort 및 기타와 함께 작동합니다.

{ rm file && uniq > file; } < file

14
또 다른 일반적인 접근 방식, spongemoreutils에서 : cat file |frobnicate |sponge file.
— Tobu

3
@Tobu : 별도의 답변으로 제출하지 않으시겠습니까?
— Flimm

1
이것이 반드시 파일 권한을 보존하는 것은 아니라는 점에 유의하는 것이 좋습니다. umask는 새로운 권한이 무엇인지 지시합니다.
— wor

1
까다로운 것. 정확히 어떻게 작동하는지 설명해 주시겠습니까?
— patryk.beza

2
@ patryk.beza : 순서 : 입력 FD는 원본 파일에서 열립니다. 원래 디렉토리 항목이 삭제됩니다. 리디렉션이 처리되어 이전 파일과 동일한 이름을 가진 새로운 빈 파일이 생성됩니다. 그런 다음 명령이 실행됩니다.
— Charles Duffy 2015

10

스펀지에 대한 토부의 논평 은 그 자체로 답이 될 것을 보증한다.

moreutils 홈페이지 에서 인용하려면 :

아마도 지금까지 moreutils에서 가장 일반적인 도구는 sponge (1)으로, 다음과 같은 작업을 할 수 있습니다 :

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

그러나 Steve Jessop이 여기에 언급sponge 한 것과 동일한 문제 가 있습니다. 파이프 라인의 명령 중 하나라도 sponge실패하면 원본 파일을 덮어 씁니다.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

어-오, my-important-file사라졌습니다.


1
Sponge는 입력 파일을 대체하는 데 사용될 것이라는 것을 알고 있으며 처음에는 경쟁 조건을 피하기 위해 임시 파일을 만듭니다. 이것이 작동하려면 스펀지가 파이프 라인의 마지막 요소 여야하며 출력 파일 자체를 생성 할 수 있어야합니다 (예를 들어 셸 수준 출력 리디렉션과 반대). BTW : 'fail'케이스에 대한 쉬운 소스 코드 수정은 pipefail의 경우 임시 파일의 이름을 바꾸지 않는 것 같습니다 (스펀지에 해당 옵션이없는 이유를 모릅니다).
— Brent Bradburn 2014 년

set -o pipefail스크립트 시작 부분에 추가 하면 오류가 mistyped_command my-important-file실행되기 전에 스크립트가 즉시 종료 sponge되어 중요한 파일이 보존됩니다.
— Elouan Keryell-Even

6

여기에 한 줄이 있습니다.

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

기술적으로는 임시 파일에 대한 복사가 없으며 'mv'명령은 즉각적이어야합니다.


6
흠. 나는 여전히 임시 파일을 temp.txt.sort라고 부를 것이다.
— JesperE

5
작업을 완료하지 않고 어떤 이유로 든 정렬이 실패하면 원본을 덮어 쓰므로이 코드는 위험합니다.
— Steve Jessop

1
디스크 공간 부족은 그럴듯한 원인이거나 신호입니다 (사용자가 CTRL-C를 누름).
— Steve Jessop

5
이와 같은 것을 사용하려면 대신 && (논리적 and)를 사용하십시오. 이를 사용하면 명령이 실패하면 다음 명령이 실행되지 않습니다. 예 : cp backup.tar /root/backup.tar && rm backup.tar 복사 할 권한이 없으면 파일이 삭제되지 않으므로 안전합니다
— daniels

1
귀하의 제안을 고려하여 내 대답을 변경했습니다. 감사합니다
— davr

4

나는 sort file -o file대답을 좋아 하지만 같은 파일 이름을 두 번 입력하고 싶지 않습니다.

BASH 히스토리 확장 사용 :

$ sort file -o !#^

를 누르면 현재 줄의 첫 번째 인수를 가져옵니다 enter.

내부 고유 정렬 :

$ sort -u -o file !#$

현재 줄의 마지막 인수를 가져옵니다.


3

많은 사람들이 -o 옵션 을 언급했습니다 . 다음은 맨 페이지 부분입니다.

man 페이지에서 :

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

3

이것은 메모리가 매우 제한적이지만 awk를 사용하여 중간 데이터를 메모리에 저장 한 다음 다시 쓸 수 있습니다.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

명령 ( 이 경우)이 파일을 읽기 전에 파일 이 잘릴 수 있다고 생각 합니다. >uniq
— Martin

3

sponge더 일반적인 것에 대한 대안 sed:

sed -ni r<(command file) file

그것은 어떤 명령 (작동 sort, uniq, tac, ...)와 사용 매우 잘 알려진 sed'의 -i옵션 (현재 위치에서 편집 파일).

경고 :command file 파일을 내부에서 편집하는 것은 본질적으로 안전하지 않으므로 먼저 시도하십시오 .


설명

첫째, sed(원본) 줄 ( -n옵션 ) 을 인쇄하지 않도록 지시 하고 있으며 sed의 r명령 및 bash의 프로세스 대체 를 통해 생성 된 내용이 제자리에<(command file) 저장된 출력 이됩니다 .


일을 더 쉽게 만들기

이 솔루션을 함수로 래핑 할 수 있습니다.

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

예

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file

1

인수를 사용 --output=하거나-o

FreeBSD에서 방금 시도했습니다.

sort temp.txt -otemp.txt

올바른 있지만, 그것의 중복 단지의 이 대답은
— whoan

1

uniq기능 을 추가하려면 다음 과 같은 단점이 있습니다.

sort inputfile | uniq | sort -o inputfile


0

sort프로그램 사용을 고집하는 경우 중간 파일을 사용해야합니다 sort. 메모리 정렬 옵션 이 없다고 생각 합니다. stdin / stdout을 사용한 다른 트릭은 정렬의 stdin에 대한 버퍼 크기가 전체 파일에 맞을만큼 충분히 크다고 보장 할 수 없으면 실패합니다.

편집 : 부끄러워. sort temp.txt -o temp.txt훌륭하게 작동합니다.


나는 또한 Q를 "in-place"라고 읽었지만 두 번째 읽기는 그가 정말로 그것을 요구하지 않는다고 믿게 만들었다
— epatel

0

또 다른 해결책 :

uniq file 1<> file

이 <>트릭은이 경우에만 작동 한다는 점에 유의해야합니다. uniq입력 라인 만 출력 라인에 복사하여 일부를 삭제한다는 점에서 특별 하기 때문 입니다. 다른 명령 (예를 들어,이 경우 sed) 입력을 바꿀 것이다 사용 (예 : 매일 변경됩니다 a로를 aa다음 그것을 대체 할 수 있습니다) file(입력이 충분히 크다는 이상을 제공하고, 모든 감각과 무한도 루프를하지 않는 방법으로 단일 읽기 버퍼).
— David
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.