bash / sed 스크립트를 사용하여 텍스트 파일의 첫 줄을 어떻게 제거합니까?


554

bash 스크립트를 사용하여 거대한 텍스트 파일에서 첫 번째 줄을 반복해서 제거해야합니다.

지금은 사용하고 sed -i -e "1d" $FILE있지만 삭제하는 데 약 1 분이 걸립니다.

이것을 달성하는 더 효율적인 방법이 있습니까?


나는 무엇을 의미합니까?
cikatomo

4
@cikatomo : 인라인 편집을 의미합니다. 생성하는대로 파일을 편집합니다.
drewrockshard 18시 03 분

4
꼬리는 sed보다 훨씬 느립니다. 꼬리는 13.5 초, sed는 0.85 초가 필요합니다. 내 파일에는 ~ 1M 줄, ~ 100MB가 있습니다. SSD가 장착 된 MacBook Air 2013
jcsahnwaldt는 GoFundMonica가

답변:


1029

꼬리를 보십시오 :

tail -n +2 "$FILE"

-n x: 마지막 x줄만 인쇄하십시오 . tail -n 5입력의 마지막 5 줄을 줄 것입니다. +반전의 부호 종류의 인수 및 메이크업 tail인쇄 아무것도하지만 첫 번째 x-1라인. tail -n +1전체 파일을 인쇄하고tail -n +2 첫 번째 줄을 제외한 모든 것을 .

GNU tail는보다 훨씬 빠릅니다 sed. tailBSD에서도 사용할 수 있으며 -n +2플래그는 두 도구에서 일관됩니다. 자세한 내용은 FreeBSD 또는 OS X 매뉴얼 페이지를 확인하십시오 .

그러나 BSD 버전은보다 느릴 수 있습니다 sed. 그들이 어떻게 관리했는지 궁금합니다. 스크립트를 해석하고 정규 표현식을 적용하는 등의 복잡한 작업을 수행하는 tail동안 파일을 한 줄씩 읽어야합니다 sed.

참고 : 사용하고 싶을 수도 있습니다

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

그러나 이것은 당신에게 빈 파일을 줄 것 입니다. 쉘이 재 호출 >하기 전에 재 지정 ( )이 발생하기 때문 tail입니다.

  1. 셸은 파일을 자릅니다 $FILE
  2. 쉘은 새로운 프로세스를 만듭니다 tail
  3. 쉘은 tail프로세스의 표준 출력 을$FILE
  4. tail 지금 비어있는에서 읽습니다 $FILE

파일 내부의 첫 번째 줄을 제거하려면 다음을 사용해야합니다.

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

&&문제가있을 때 파일이 덮어하지 않도록 할 것입니다.


3
ss64.com/bash/tail.html 에 따르면 -r옵션 과 함께 BSD 'tail'을 사용할 때 일반적인 버퍼의 기본값은 32k 입니다. 시스템 어딘가에 버퍼 설정이 있습니까? 아니면 -n32 비트 부호있는 숫자입니까?
이즈미르 라미레즈

41
@ 에디 : user869097은 줄이 15Mb 이상이면 작동하지 않는다고 말했습니다 . 줄이 더 짧으면 tail모든 파일 크기에서 작동합니다.
Aaron Digulla

6
이 논쟁에 대해 설명해 주시겠습니까?
Dreampuf

17
@Dreampuf-맨 페이지에서 :-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Will Sheppard

11
@JonaChristopherSahnwaldt와 동의 할 것입니다-꼬리는 sed 변형보다 훨씬 느립니다. 500,000K 줄 (한 줄에 50 자 이하)의 파일로 테스트하고 있습니다. 그러나 FreeBSD 버전의 tail (기본적으로 OS X와 ​​함께 제공됨)을 사용하고 있음을 깨달았습니다. GNU tail로 전환했을 때 tail 호출은 sed 호출 (및 GNU sed 호출)보다 10 배 빠릅니다. GNU를 사용하는 경우 AaronDigulla가 여기에 맞습니다.
Dan Nguyen

179

'>'연산자를 사용하지 않고 -i를 사용하여 파일을 업데이트 할 수 있습니다. 다음 명령은 파일에서 첫 번째 줄을 삭제하고 파일에 저장합니다.

sed -i '1d' filename

1
오류가 발생했습니다 :unterminated transform source string
Daniel Kobe

10
이것은 매번 작동하며 실제로 최고의 대답이어야합니다!
xtheking

4
기억하십시오. Mac에서는 sed를 내부 편집 할 때 접미사가 제공되어야합니다. 그래서 -i.bak와 위의를 실행
MJP

3
참고 사항 – 여러 줄 사용 제거sed -i '1,2d' filename
대부

4
이 버전은 실제로보다 읽기 쉽고 보편적 tail -n +2입니다. 왜 최고의 답변이 아닌지 잘 모르겠습니다.
Luke Davis


17

아니, 그것은 당신이 얻을만큼 효율적입니다. 작업을 조금 더 빠르게 할 수있는 C 프로그램을 작성할 수는 있지만 (시작 시간이 적고 인수를 처리하는 경우) 파일이 커질 때 sed와 동일한 속도로 향할 수 있습니다 (분이 걸리면 크기가 크다고 가정합니다) ).

그러나 귀하의 질문에는 솔루션을 미리 제안한다는 점에서 다른 많은 사람들과 동일한 문제가 있습니다. 당신이 세부에서 우리에게 얘기를한다면 무엇을 당신이보다는 일을하려고하고 어떻게 , 우리는 더 나은 옵션을 제안 할 수 있습니다.

예를 들어, 이것이 다른 프로그램 B가 처리하는 파일 A 인 경우 한 솔루션은 첫 번째 행을 제거하지 않고 프로그램 B를 수정하여 다르게 처리하는 것입니다.

모든 프로그램이이 파일 A에 추가되고 프로그램 B는 현재 첫 번째 행을 읽고 처리하기 전에이를 읽습니다.

프로그램 B를 다시 엔지니어링하여 첫 번째 줄을 삭제하려고 시도하지 않았지만 파일 A에 대한 영구적 인 (아마도 파일 기반) 오프셋을 유지하여 다음에 실행될 때 해당 오프셋을 찾고 프로세스를 찾을 수 있습니다. 거기에 선을 긋고 오프셋을 업데이트하십시오.

그런 다음 조용한 시간 (자정?)에서 파일 A의 특수 처리를 수행하여 현재 처리 된 모든 행을 삭제하고 오프셋을 다시 0으로 설정할 수 있습니다.

프로그램이 파일을 열고 다시 쓰는 것이 아니라 파일을 열고 찾는 것이 더 빠를 것입니다. 이 토론은 물론 프로그램 B를 제어한다고 가정합니다. 그 경우인지 모르겠지만 추가 정보를 제공하면 다른 가능한 해결책이있을 수 있습니다.


OP 가이 질문을 찾은 것을 달성하려고 노력하고 있다고 생각합니다. 각각 500k 줄의 10 개의 CSV 파일이 있습니다. 모든 파일은 첫 번째 행과 동일한 헤더 행을 갖습니다. 나는 고양이 :이 파일을 하나의 파일로 만든 다음 DB로 가져 와서 DB가 첫 번째 줄에서 열 이름을 만들 수있게합니다. 분명히 나는 ​​그 줄이 파일 2-10에서 반복되는 것을 원하지 않습니다.
db

1
@db이 경우 awk FNR-1 *.csv아마도 더 빠를 것입니다.
jinawee

10

파일을 제자리에서 편집 있습니다. 다음 -i과 같이 펄의 플래그를 사용 하십시오 :

perl -ni -e 'print unless $. == 1' filename.txt

요청한대로 첫 번째 줄이 사라집니다. Perl은 전체 파일을 읽고 복사해야하지만 출력이 원본 파일 이름으로 저장되도록 정렬합니다.


10

다음과 같이 쉽게 수행 할 수 있습니다.

cat filename | sed 1d > filename_without_first_line

명령 행에서; 또는 파일의 첫 번째 행을 영구적으로 제거하려면 -i플래그 와 함께 sed의 내부 모드를 사용하십시오 .

sed -i 1d <filename>

9

Pax가 말했듯이 아마도 이것보다 더 빠를 수는 없습니다. 그 이유는 파일 시작 부분에서 잘림을 지원하는 파일 시스템이 거의 없기 때문에 파일 크기가 O ( n) 작업 이 될 것 n입니다. 훨씬 더 빨리 할 수있는 일은 동일한 바이트 수 (공백 또는 주석이있을 수 있음)로 첫 번째 줄을 덮어 쓰는 것입니다. 실제로 수행하려는 작업에 따라 작동 할 수 있습니다 (무엇입니까?).


다시 "... 거의 지원하는이 ... 절단 된 파일 시스템 없습니다" : 흥미로운 것을; 그러한 파일 시스템의 이름을 괄호로 묶는 것을 고려하십시오.
agc

1
@agc : 지금은 무관하지만, 70 년대에 처음으로 일한 것은 소규모 신생 기업인 Quadex (지금은 사라졌으며 현재는 그 이름을 사용하는 두 회사와 무관)였습니다. 파일의 시작 또는 끝 부분에 파일을 추가 하거나 제거 할 수있는 파일 시스템이 있었는데, 창에 창 아래에 파일을 두어 3KB 미만으로 편집을 구현하는 데 주로 사용되었습니다. 자체 이름이 없었으며, Quadex 다중 사용자 운영 체제 인 QMOS의 일부였습니다. ( '멀티'보통 LSI-11 / 64킬로바이트 RAM에서 02 몇 RX01 형 8 "플로피 디스크 각 2백50킬로바이트에 보통 2 ~ 3이었다.) :-)
dave_thompson_085

9

sponge유틸리티 는 임시 파일을 저글링 할 필요가 없습니다.

tail -n +2 "$FILE" | sponge "$FILE"

sponge실제로 허용 된 솔루션 ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE") 보다 훨씬 더 깨끗하고 강력합니다.
Jealie

1
'sponge'에는 'moreutils'패키지가 설치되어 있어야합니다.
FedFranzoni

이것은 데비안 도커 이미지에서 시스템 파일을 변경하는 데 도움이 된 유일한 솔루션입니다. 파일을 쓰려고 할 때 "장치 또는 리소스 사용 중"오류로 인해 다른 솔루션이 실패했습니다.
FedFranzoni

그러나 sponge전체 파일을 메모리에 버퍼링합니까? 수백 GB이면 작동하지 않습니다.
OrangeDog

@OrangeDog, 파일 시스템이 파일을 저장할 수 sponge있는 한 / tmp 파일을 중간 단계로 사용하여 나중에 원본을 대체하는 데 사용 되므로 파일 시스템에 흡수됩니다 .
agc

8

이 곳에서 파일을 수정하려면, 당신은 항상 원래 사용할 수있는 ed대신의 의의 treaming 후계자를 sed:

ed "$FILE" <<<$'1d\nwq\n'

ed도 훨씬 덜 그래픽 워크 스테이션 전체 화면 단자가되기 전에 명령은 원래 UNIX 텍스트 편집기이었다. ex편집기, 최고의 입력은 콜론 프롬프트에서 때 사용중인로 알려진 vi,입니다 전직 의 경향 버전의 ed동일한 명령 작업의 많은이. ed대화식으로 사용하기위한 것이지만 ,이 솔루션이 수행하는 명령 문자열을 전송하여 배치 모드에서도 사용할 수 있습니다.

순서는 <<<$'1d\nwq\n'여기 - 스트링 (위한 배시의 지원을 활용 <<<() 및 POSIX 지수 $'... '받는 이송 입력) ed: 두 개의 라인으로 이루어진 명령 1d, 개발 eletes가 라인 1을 다음과 wq어느 w 밖으로 의식 파일 뒷면 디스크는 q는 편집 세션을 UITS.


이것은 우아하다. +1
Armin

그러나 전체 파일을 메모리로 읽어야합니다. 수백 GB이면 작동하지 않습니다.
OrangeDog

5

첫 번째 행을 제외한 행을 표시해야합니다.

cat textfile.txt | tail -n +2

4
- "tail -n +2 textfile.txt"
niglesias

5
@niglesiais 필자는이 솔루션이 파일뿐만 아니라 파이프 된 컨텐츠에서도 괜찮다는 점을 분명히 알기 때문에 "고용이없는 고양이 사용"에 동의하지 않습니다.
Titou

5

vim을 사용하여이를 수행 할 수 있습니다.

vim -u NONE +'1d' +'wq!' /tmp/test.txt

vim은 프로세스 할 때 전체 파일을 읽지 않기 때문에 더 빠릅니다.


+wq!쉘이 bash라면 인용해야 할 수도 있습니다. 아마도 !단어의 시작 부분이 아니기 때문에 아마도 인용하지 않는 것이 좋을 것입니다. (그리고 불필요하게 인용하지 않음으로써 초 고효율을 원한다면 인용 부호가 필요하지 않습니다 1d.)
Mark Reed

vim 전체 파일을 읽을 필요가 있습니다. 실제로이 Q에서 요청한대로 파일이 메모리보다 큰 경우 vim은 전체 파일을 읽고 임시 파일에 파일 (또는 대부분)을 쓰고 편집 한 후에는 파일을 모두 영구 파일에 다시 씁니다. 나는 당신이 그것을 가능하게 일할 수있는 생각하는 방법을 모른다 없이 이.
dave_thompson_085

4

csplit을 사용하는 것은 어떻습니까?

man csplit
csplit -k file 1 '{1}'

이 구문도 작동하지만 3 개 대신 2 개의 출력 파일 만 생성합니다 csplit file /^.*$/1. 또는 더 간단하게 : csplit file //1. 또는 더 간단하게 : csplit file 2.
Marco Roy

1

삭제 속도를 높일 수없는 것처럼 들리므로 다음과 같이 파일을 일괄 처리하는 것이 좋습니다.

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

이것의 단점은 프로그램이 중간에 죽거나 (혹은 거기에 나쁜 sql이있어 "프로세스"부분이 죽거나 잠기는 경우) 줄을 건너 뛰거나 두 번 처리한다는 것입니다. .

(file1은 SQL 코드 줄을 포함합니다)


첫 줄에는 무엇이 포함되어 있습니까? 내 게시물에서 제안한대로 SQL 주석으로 덮어 쓸 수 있습니까?
Robert Gamble

0

당신이 찾고있는 것이 실패 후 복구하는 것이라면, 지금까지 한 일을 가진 파일을 만들 수 있습니다.

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

0

이 라이너는 다음을 수행합니다.

echo "$(tail -n +2 "$FILE")" > "$FILE"

이후이 작품 tail이전에 실행 echo한 후 파일을 임시 파일에 따라서 필요, 잠금이 해제되지 않습니다.


-1

N-1 행에 tail을 사용하여 파일로 지정하고 이전 파일을 제거한 다음 새 파일의 이름을 이전 이름으로 바꾸겠습니까?

프로그래밍 방식 으로이 작업을 수행하는 경우 파일을 읽고 각 줄을 읽은 후 파일 오프셋을 기억하므로 해당 위치로 돌아가서 줄이 하나 적은 파일을 읽을 수 있습니다.


첫 번째 솔루션은 브렌트가 지금하는 것과 본질적으로 동일합니다. 나는 프로그래밍 방식을 이해하지 못합니다. 첫 번째 줄만 삭제하면됩니다. 첫 번째 줄을 읽고 버리고 나머지는 sed 및 tail 접근법과 동일한 다른 파일로 복사하십시오.
Robert Gamble

두 번째 해결책은 파일이 매번 첫 번째 줄씩 줄어들지 않는다는 의미입니다. 이 프로그램은 축소 된 것처럼 간단하게 처리하지만 매번 다음 줄에서 시작합니다.
EvilTeach

나는 여전히 당신이 두 번째 해결책이 무엇인지 이해하지 못합니다.
Robert Gamble
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.