EOF에서 여러 줄 바꿈을 제거하는 방법?


25

하나 이상의 줄 바꿈으로 끝나고 하나의 줄 바꿈으로 끝나야하는 파일이 있습니다. Bash / Unix / GNU 도구로 어떻게 할 수 있습니까?

잘못된 파일 예 :

1\n
\n
2\n
\n
\n
3\n
\n
\n
\n

수정 된 파일 예 :

1\n
\n
2\n
\n
\n
3\n

다시 말해 : EOF와 파일의 마지막 개행 문자 사이에는 정확히 하나의 개행 문자가 있어야합니다.

참조 구현

파일 내용을 읽고 끝에 줄 바꿈이 더 이상 없어 질 때까지 한 줄 바꿈을 잘라내어 다시 쓰십시오.

#! /bin/python

import sys

with open(sys.argv[1]) as infile:
    lines = infile.read()

while lines.endswith("\n\n"):
    lines = lines[:-1]

with open(sys.argv[2], 'w') as outfile:
    for line in lines:
        outfile.write(line)

설명 : 물론, 더 우아하다면 배관이 허용됩니다.

답변:


16
awk '/^$/ {nlstack=nlstack "\n";next;} {printf "%s",nlstack; nlstack=""; print;}' file

2
+1 : awk의 솔루션은 (거의) 항상 우아하고 읽을 수 있습니다!
Olivier Dulac

@OlivierDulac 실제로. sed제안을 보았을 때 방금 OMG를 생각했습니다.
Hauke ​​Laging

1
Homebrew에서 제공하는 최신 awk를 사용하는 OSX Mavericks에서는 작동하지 않습니다. 오류가 awk: illegal statement있습니다. 그래도 brew install mawk명령을 변경하면 mawk작동합니다.
tjmcewan

@noname 나는 심지어 그 질문을 이해하지 못한다 ...
Hauke ​​Laging

스크립트가 작동하지 않는 모든 awk는 심하게 깨진 awk입니다. 사용을 중지하고 새로운 awk를 얻으십시오.이를 수행 할 수 없다면 다른 파손이 무엇인지 알기 때문입니다.
에드 모튼

21

에서 유용 한 줄의 스크립트가 나오지 .

# Delete all trailing blank lines at end of file (only).
sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' file

4
감사합니다. 여러 파일에 대해 다음 작업을 수행했습니다. find . -type f -name '*.js' -exec sed --in-place -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
jakub.g

@ jakub.g와 재귀는 내가 필요한 것입니다. 고맙습니다.
Buttle Butkus

: 당신이 OS X에서이 같은 명령을 호출 할 수 있습니다 @ jakub.g에서 우수한 주석을 추가하려면find . -type f -name '*.js' -exec sed -i '' -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
davejagoda

18

더 적합한 도구 인 sed와 awk에 대한 답변이 이미 있으므로; $(< file)빈 줄 을 제거 한다는 사실을 활용할 수 있습니다 .

a=$(<file); printf '%s\n' "$a" > file

그 싼 해킹은 공백이나 다른 비 인쇄 문자를 포함 할 수있는 후행 빈 줄을 제거하지 않고 후행 빈 줄을 제거하기 위해 작동하지 않습니다. 파일에 null 바이트가 포함되어 있으면 작동하지 않습니다.

bash 및 zsh 이외의 쉘에서는 $(cat file)대신을 사용하십시오 $(<file).


나에게 버그처럼 보이는 것을 지적하기 위해 +1 : $ (<file)은 실제로 파일을 읽지 않습니까? 왜 후행 줄 바꿈을 버립니까? (
그렇습니다

2
@OlivierDulac $()은 후행 줄 바꿈을 버립니다. 그것은 디자인 결정입니다. 나는 이것이 다른 문자열로의 통합을 더 쉽게 echo "On $(date ...) we will meet."할 것이라고 가정한다. 거의 모든 쉘 명령이 마지막에 출력하는 개행에는 악의적이다.
Hauke ​​Laging

@HaukeLaging : 좋은 지적, 아마도 그 행동의 근원 일 것입니다
Olivier Dulac

빈 파일에 "\ n"을 추가하지 않도록 특별한 경우를 추가했습니다 [[ $a == '' ]] || printf '%s\n' "$a" >"$file".
davidchambers

파일의 시작 부분에서 여러 줄 바꿈을 제거하려면 tac을 프로세스에 삽입하십시오 (Mac에서는 gnu coreutils를 사용하므로 gtac은 나에게 적합합니다) :a=$(gtac file.txt); printf '%s\n' "$a" | gtac > file.txt
r_alex_hall

5

이 트릭을 cat& 와 함께 사용할 수 있습니다 printf.

$ printf '%s\n' "`cat file`"

예를 들어

$ printf '%s\n' "`cat ifile`" > ofile
$ cat -e ofile
1$
$
2$
$
$
3$

$라인의 종료를 나타낸다.

참고 문헌


4

이 질문은 태그로 지정 되었지만 아무도 ed해결책 을 제안하지 않았습니다 .

여기 하나가 있습니다 :

ed -s file <<'ED_END'
a

.
?^..*?+1,.d
w
ED_END

또는 동등하게

printf '%s\n' a '' . '?^..*?+1,.d' w | ed -s file

ed 시작할 때 기본적으로 편집 버퍼의 마지막 줄에 배치됩니다.

첫 번째 명령 ( a)은 버퍼 끝에 빈 줄을 추가합니다 (편집 스크립트의 빈 줄은이 줄이며 점 ( .)은 명령 모드로 돌아 오기위한 것임).

두 번째 명령 ( ?)은 무언가를 포함하는 가장 가까운 이전 행을 찾은 다음 (공백 문자 포함) 다음 행에서 버퍼 끝까지 모든 것을 삭제합니다.

세 번째 명령 ( w)은 파일을 디스크에 다시 씁니다.

추가 된 빈 줄은 원본 파일 끝에 빈 줄이없는 경우 나머지 파일이 삭제되지 않도록 보호합니다.


3

한 번에 두 줄 이상을 메모리로 읽을 필요 가없는 Perl 솔루션은 다음과 같습니다 .

my $n = 0;
while (<>) {
    if (/./) {
        print "\n" x $n, $_;
        $n = 0;
    } else {
        $n++;
    }
}

또는 단일 라이너로 :

perl -ne 'if (/./) { print "\n" x $n, $_; $n = 0 } else { $n++ }'

파일을 한 번에 한 줄씩 읽고 각 줄을 검사하여 줄 바꿈이 아닌 문자가 포함되어 있는지 확인합니다. 그렇지 않으면 카운터를 증가시킵니다. 이 경우 카운터가 나타내는 줄 바꿈 수를 인쇄 한 다음 줄 자체를 인쇄 한 다음 카운터를 재설정합니다.

기술적으로 메모리에서 한 줄을 버퍼링하는 것조차 필요하지 않습니다. 고정 길이 청크로 파일을 읽고 상태 머신을 사용하여 문자별로 처리하여 일정한 양의 메모리를 사용하여이 문제점을 해결할 수 있습니다. 그러나 일반적인 사용 사례에는 불필요하게 복잡 할 것으로 생각됩니다.


1

파일이 메모리에 들어가기에 충분히 작은 경우, 이것을 사용할 수 있습니다

perl -e 'local($/);$f=<>; $f=~s/\n*$/\n/;print $f;' file

0

파이썬에서 (나는 그것이 당신이 원하는 것이 아니라는 것을 알고 있지만 파일을 다시 쓰지 않고 모든 파일을 읽지 않고 (파일이 좋은 경우 좋은 것입니다) 매우 큰):

#!/bin/python
import sys
infile = open(sys.argv[1], 'r+')
infile.seek(-1, 2)
while infile.read(1) == '\n':
  infile.seek(-2, 1)
infile.seek(1, 1)
infile.truncate()
infile.close()

EOL 문자가 '\ n'이 아닌 파일에서는 작동하지 않습니다.


0

파이썬 알고리즘을 구현하는 bash 버전이지만 많은 프로세스가 필요하므로 효율성이 떨어집니다.

#!/bin/bash
n=1
while test "$(tail -n $n "$1")" == ""; do
  ((n++))
done
((n--))
truncate -s $(($(stat -c "%s" "$1") - $n)) "$1"

0

이것은 빠르게 입력 할 수 있으며 sed를 알고 있다면 기억하기 쉽습니다.

tac < file | sed '/[^[:blank:]]/,$!d' | tac

sed 스크립트를 사용하여 위의 Alexey가 참조하는 sed 에 대한 유용한 한 줄 스크립트 에서 선행 빈 줄 을 삭제 하고 tac (역 고양이)를 삭제합니다.

빠른 테스트에서 18MB, 64,000 라인 파일에서 Alexey의 접근 방식이 더 빨랐습니다 (0.036 대 0.046 초).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.