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옵션 ) 을 인쇄하지 않도록 지시 하고 있으며 sedr명령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.