거대한 파일의 시작과 끝에 줄 추가


23

거대한 파일의 시작과 끝에 줄을 추가하는 시나리오가 있습니다.

아래에 표시된 것처럼 시도했습니다.

  • 첫 줄 :

    sed -i '1i\'"$FirstLine" $Filename
  • 마지막 줄 :

    sed -i '$ a\'"$Lastline" $Filename  

그러나이 명령의 문제점은 파일의 첫 번째 행을 추가하고 전체 파일을 순회한다는 것입니다. 마지막 줄의 경우 전체 파일을 다시 탐색하고 마지막 줄을 추가합니다. 매우 큰 파일 (14GB) 이후 시간이 오래 걸립니다.

파일을 한 번만 읽는 동안 파일의 시작 부분과 끝 부분에 줄을 추가하려면 어떻게해야합니까?

답변:


20

sed -itempfile을 구현 세부 사항으로 사용합니다. 그러나 기존 내용을 덮어 쓰지 않고 데이터 스트림의 시작 부분에 데이터를 추가하려면 파일을 다시 써야합니다 sed -i.

파일 재 작성이 옵션이 아닌 경우 파일을 읽을 때 조작하는 것을 고려할 수 있습니다 (예 :

{ echo some prepended text ; cat file ; } | command

또한 sed는 스트림을 편집하기위한 것입니다. 파일은 스트림이 아닙니다. ed 또는 ex와 같이이 목적을위한 프로그램을 사용하십시오. -ised에 대한 옵션은 이식 가능할뿐만 아니라 파일에 대한 심볼릭 링크도 끊습니다. 파일을 삭제하고 다시 생성하기 때문에 무의미합니다.

다음과 ed같이 단일 명령으로이 작업을 수행 할 수 있습니다 .

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

ed의 구현에 따라 페이징 파일을 사용할 수 있으므로 최소한 사용 가능한 공간이 있어야합니다.


안녕하세요, u 제공 한 ed 명령은 큰 파일에서 매우 잘 작동합니다. 그러나 나는 Test, Test1, Test 2와 같은 3 개의 거대한 파일을 가지고 있습니다. $ a이 줄을 끝에 추가하십시오. w EOF 그러나 테스트 파일 만 가져오고 첫 번째 / 마지막 줄을 추가합니다. 모든 파일에서 첫 번째와 마지막 행을 추가해야하는 동일한 명령으로 변경하는 방법
UNIXbest

@UNIXbest- for루프 사용 :for file in Tes*; do [command]; done
Chris Down

안녕하세요, Tes *의 파일에 대해 아래 명령을 사용했습니다. ed -s Tes * << 'EOF'0a HEllO HDR을 수행하십시오. $ a Hello TLR. w EOF 완료 그러나 여전히 첫 번째 파일에 쓰고 있습니다.
UNIXbest

맞습니다 .에 대한 인수가 "$file"아닌 Tes*을 ( 를) 사용해야 합니다 ed.
Chris Down

2
@UNIXbest이 답변으로 문제가 해결 되었다면, 수락하는 것을 고려해야합니다.
Joseph R.

9

디스크에 파일의 전체 사본을 할당하지 않으려면 다음을 수행하십시오.

sed '
1i\
begin
$a\
end' < file 1<> file

그것은 stdin / stdout이 파일 일 때 sed 블록별로 읽고 쓰는 사실을 사용합니다 . 따라서 여기에서 추가하는 첫 번째 줄이 sed블록 크기 보다 작은 한 (4k 또는 8k와 같아야 함 ) 읽고있는 파일을 무시해도됩니다 .

어떤 이유로 든 sed실패하면 (killed, machine crash ...) 파일이 절반으로 처리되어 중간에 누락 된 첫 번째 줄의 크기를 의미합니다.

또한 sedGNU sed가 아닌 경우 이진 데이터에는 작동하지 않습니다 (그러나 사용하기 때문에 -iGNU sed를 사용하고 있음).


우분투 16.04에 나를 위해이 오류
사바 토스

4

다음은 몇 가지 선택 사항입니다 (모두 파일의 새 사본을 작성하므로 충분한 공간이 있는지 확인하십시오).

  • 간단한 에코 / 고양이

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawk 등

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awk그리고 ilk는 파일을 한 줄씩 읽습니다. BEGIN{}블록은 첫 번째 행과 전에 실행되는 END{}마지막 라인 이후 블록. 따라서 위의 명령은을 의미 print "first" at the beginning, then print every line in the file and print "last" at the end합니다.

  • perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    이것은 본질적으로 Perl로 작성된 위의 gawk와 동일합니다.


1
이 모든 경우에 새 파일을위한 최소 14GB의 공간이 필요합니다.
Chris Down

@ChrisDown 좋은 지적, 나는 그것을 명확하게하기 위해 대답을 편집했습니다. OP가 sed -i임시 파일을 만드는 데 사용했기 때문에 문제가되지 않았다고 가정했습니다 .
terdon

3

나는 훨씬 간단한 것을 선호합니다.

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

파일을 변환합니다.

asdf
qwer

파일에 :

foo
asdf
qwer
bar

2

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

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 첫 줄을 선택하십시오

  2. i 텍스트와 개행 삽입

  3. $ 마지막 줄을 선택하십시오

  4. a 텍스트와 개행 추가

  5. x 저장하고 닫습니다


여러 파일에이 작업을 수행하려면 어떻게해야합니까?
geoyws

1
@geoyws이 질문에 실제로 해당되지 않습니다
Steven Penny

이것이 $ a이고 % a가 아닌가?
Carlos Robles

2

파일 시작 부분에 데이터를 삽입 할 수있는 방법 ¹이 없습니다. 새 파일을 만들고 추가 데이터를 작성하고 이전 데이터를 추가하기 만하면됩니다. 따라서 첫 번째 줄을 삽입하려면 전체 파일을 한 번 이상 다시 작성해야합니다. 그러나 파일을 다시 쓰지 않고 마지막 줄을 추가 할 수 있습니다.

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

또는 두 개의 명령을 한 번의 sed 실행에 결합 할 수 있습니다.

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -i새 출력 파일을 만든 다음 이전 파일 위로 옮깁니다. 이것은 sed가 작동하는 동안 공간을 차지하는 두 번째 파일 사본이 있음을 의미합니다. 파일을 제자리덮어 써서 이것을 피할 수 있지만 주요 제한 사항이 있습니다. 추가하는 행이 sed의 버퍼보다 ​​작아야합니다. 시스템이 충돌하면 파일이 손상되고 일부 내용이 손실됩니다. 중간에 있으므로 강력히 권장합니다.

¹ Linux에는 파일에 데이터를 삽입 할 수있는 방법이 있지만 전체 파일 시스템 블록 만 삽입 할 수 있으며 임의 길이의 문자열을 삽입 할 수 없습니다. 데이터베이스 및 가상 시스템과 같은 일부 응용 프로그램에는 유용하지만 텍스트 파일에는 유용하지 않습니다.


사실이 아니다. 에서 봐 fallocate()FALLOC_FL_INSERT_RANGE현대 커널에서 XFS로 볼 수 있습니다 및 ext4에 (4.XX) man7.org/linux/man-pages/man2/fallocate.2.html
에릭

@Eric 적어도 ext4를 사용하는 Linux 4.15.0에서는 임의의 바이트 길이가 아닌 전체 블록 만 삽입 할 수 있습니다. 임의의 바이트 길이를 삽입 할 수있는 파일 시스템이 있습니까?
Gilles 'SO- 악마 그만'

그렇습니다. 그러나 여전히 당신의 진술은 정확하지 않습니다. "파일 시작 부분에 데이터를 삽입 할 방법이 없습니다"라고 썼습니다. 파일의 시작 부분에 범위를 삽입하는 메커니즘이 있습니다. 물론 경고가 있지만 일부 사용자는 공백이나 캐리지 리턴으로 채워서 블록 크기 제한에 신경 쓰지 않을 수 있으므로 언급 할 가치가 있습니다.
에릭

0
$ (echo "Some Text" ; cat file1) > file2

4
코드 답변 만 허용되지 않습니다. 답변을 개선하십시오
Networker

제안에 대한 설명이나 솔루션을 지원하는 설명서 링크를 포함하도록 답변을 확장하십시오.
HalosGhost

-1

최신 Linux 커널 (4.1 또는 4.2 이상)은 fallocate()시스템 시작을 통해 파일 시작 부분에서 데이터 삽입을 지원합니다.FALLOC_FL_INSERT_RANGE 은 ext4 및 xfs 파일 합니다. 본질적으로 이것은 논리적 이동 작업입니다. 데이터는 논리적으로 더 높은 오프셋으로 재배치됩니다.

파일 시작 부분에 삽입하려는 범위의 세분성에 대한 제약 조건이 있습니다. 그러나 텍스트 파일의 경우 필요 이상으로 (세분화 경계까지) 할당하거나 공백 또는 캐리지 리턴을 채울 수 있지만 응용 프로그램에 따라 다릅니다.

파일 범위를 조작하는 쉽게 사용할 수있는 Linux 유틸리티는 모르지만 쓰기는 어렵지 않습니다. 파일 설명자를 가져 와서 fallocate()적절한 인수로 호출 하십시오. 자세한 내용은 fallocate시스템 호출 매뉴얼 페이지를 참조하십시오 : http://man7.org/linux/man-pages/man2/fallocate.2.html


유틸리티는 문제가되지 않습니다 (포함되지 않은 Linux를 가정) : util-linux에는 fallocate유틸리티 가 포함되어 있습니다 . 문제는 전체 블록을 세분화하여 대부분의 텍스트 파일에 사용할 수 없다는 것입니다. 또 다른 문제는 범위 할당 및 후속 수정이 원자 적이 지 않다는 것입니다. 따라서 이것이 실제로 문제를 해결하지는 않습니다.
Gilles 'SO- 악마 중지'

세분성은 내가 이미 언급 한주의 사항이며 아니요, 쓸모 없게 만들지 않으며 응용 프로그램에 따라 다릅니다. 원 자성이 중요하다는 질문에서 어디에서 보았습니까? 공연 문제 만 볼 수 있습니다. 심지어이 시스템 콜은 원자 것 같다 그래서 : elixir.bootlin.com/linux/latest/source/fs/open.c#L228 및 자성은 (는 아니지만,이 인수를 위해서라고) 다음 중요한 becames 경우 파일 잠금을 사용하십시오. ( fallocate원자가 깨지는 커널 코드의 위치 를 알려주세요. 궁금합니다)
Eric
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.