버전 제어 시스템을 사용하면 diff가 말할 때 소음에 짜증이납니다 No newline at end of file
.
그래서 궁금해했습니다. 파일 끝에 줄 바꿈을 추가하여 해당 메시지를 제거하는 방법은 무엇입니까?
버전 제어 시스템을 사용하면 diff가 말할 때 소음에 짜증이납니다 No newline at end of file
.
그래서 궁금해했습니다. 파일 끝에 줄 바꿈을 추가하여 해당 메시지를 제거하는 방법은 무엇입니까?
답변:
프로젝트를 재귀 적으로 소독하려면이 oneliner를 사용하십시오.
git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done
설명:
git ls-files -z
저장소에 파일을 나열합니다. 작업을 특정 파일 / 디렉토리로 제한하려는 경우에 유용한 추가 매개 변수로 선택적 패턴을 사용합니다. 다른 방법으로, find -print0 ...
또는 유사한 프로그램을 사용 하여 영향을받는 파일을 나열 할 수 있습니다. 파일이 NUL
구분 된 항목 인지 확인하십시오 .
while IFS= read -rd '' f; do ... done
공백 및 / 또는 줄 바꿈이 포함 된 파일 이름을 안전하게 처리하여 항목을 반복합니다.
tail -c1 < "$f"
파일에서 마지막 문자를 읽습니다.
read -r _
후행 줄 바꿈이 없으면 0이 아닌 종료 상태로 종료합니다.
|| echo >> "$f"
이전 명령의 종료 상태가 0이 아닌 경우 파일에 개행을 추가합니다.
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
버전 관리에서 추적되지 않는 파일을 편집하지 않아도 패턴을 전달할 수도 있습니다 .
IFS=
분리자를 설정 해제하면 주변 공백을 유지하는 것이 좋습니다. null로 끝나는 항목은 이름에 개행 문자가있는 파일이나 디렉토리가있는 경우에만 관련이 있습니다. 작은 경고와 마찬가지로 POSIX sh 에서는 -d
옵션을 read
사용할 수 없습니다.
tail -n1 < "$f"
시작하는 파일 이름의 문제를 피하기 위해 사용을 참조하십시오 -
( tail -n1 -- "$f"
라는 파일에는 작동하지 않음 -
) 대답이 이제 zsh / bash와 관련되어 있음을 명확히 할 수 있습니다.
여기 있습니다 :
sed -i -e '$a\' file
대안으로 OS X의 경우 sed
:
sed -i '' -e '$a\' file
이렇게 하면 줄 바꿈으로 끝나지 않은 경우 에만\n
파일 끝에 추가 됩니다 . 따라서 두 번 실행하면 다른 줄 바꿈이 추가되지 않습니다.
$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
man sed
: $ Match the last line.
그러나 우연히 만 작동합니다. 귀하의 솔루션도 작동합니다.
$
있습니다. 형식과 같은 정규 표현식 내부에는 /<regex>/
일반적인 "줄 끝 일치"의미가 있습니다. 그렇지 않으면, 주소로 사용되는 sed는 특별한 "파일의 마지막 줄"을 의미합니다. sed는 기본적으로 개행을 출력에 추가하지 않기 때문에 기본적으로 코드가 작동합니다. "$ a \"코드는 "파일의 마지막 줄과 일치하고 아무 것도 추가하지 않습니다"라고 말합니다. 그러나 sed는 암시 적으로 줄 바꿈 $
이없는 경우 처리하는 모든 줄 (예 :이 줄)에 줄 바꿈을 추가합니다 .
/regex/
다른 의미를 부여합니다. FreeBSD 맨 페이지는 좀 더 유익합니다. freebsd.org/cgi/man.cgi?query=sed
보세요 :
$ echo -n foo > foo
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo
그래서 echo "" >> noeol-file
트릭을 할해야합니다. (또는 이러한 파일을 식별 하고 수정하도록 요청 했습니까?)
edit 에서 ""
from을 제거했습니다 echo "" >> foo
(@yuyichao의 의견 참조) edit2 가 ""
다시 추가 했습니다 ( 그러나 @Keith
Thompson의 의견 참조)
""
(적어도 bash에 대한) 필요하지 않으며 tail -1 | wc -l
끝에 새 줄이없는 파일을 찾을 수 있습니다
""
bash에는 필요 echo
하지 않지만 인수없이 호출 할 때 아무것도 인쇄하지 않는 구현을 보았습니다 (지금은 찾을 수있는 것은 없습니다). echo "" >> noeol-file
아마 약간 더 강력 할 것입니다. printf "\n" >> noeol-file
훨씬 더입니다.
csh
's echo
는 인수를 전달하지 않으면 아무것도 출력하지 않는 것으로 알려져 있습니다. 그러나 우리는 비 본쉘을 지원하는거야 다음, 우리는해야한다 echo ''
대신 echo ""
으로 echo ""
OUPUT 것 ""<newline>
으로 rc
또는 es
예를 들어.
tcsh
,와 달리 csh
, 설정과 상관없이 인수없이 호출 할 때 줄 바꿈을 인쇄합니다 $echo_style
.
를 사용하는 다른 솔루션 ed
. 이 솔루션은 마지막 줄에만 영향을 미칩니다 \n
.
ed -s file <<< w
그것은 본질적으로 스크립트를 통해 편집하기 위해 파일을 여는 작업이며, 스크립트는 w
파일을 디스크에 다시 쓰는 단일 명령입니다. ed(1)
맨 페이지 에있는이 문장에 근거 합니다 :
한계 (...) 텍스트 (이진이 아닌) 파일이 줄 바꿈 문자로 끝나지 않으면 그런 다음 ed는 읽거나 쓸 때 하나를 추가합니다. 이진의 경우 ed는 읽기 / 쓰기에 개행을 추가하지 않습니다.
결석 한 최종 개행을 텍스트 파일에 추가하는 간단하고 이식 가능한 POSIX 호환 방법은 다음과 같습니다.
[ -n "$(tail -c1 file)" ] && echo >> file
이 방법은 전체 파일을 읽을 필요는 없습니다. 그것은 단순히 EOF를 추구하고 거기서부터 일할 수 있습니다.
이 방법은 또한 임시 파일 (예 : sed -i)을 만들 필요가 없으므로 하드 링크에는 영향을 미치지 않습니다.
echo는 명령 대체 결과가 비어 있지 않은 문자열 인 경우에만 파일에 개행을 추가합니다. 파일이 비어 있지 않고 마지막 바이트가 줄 바꿈이 아닌 경우에만 발생할 수 있습니다.
파일의 마지막 바이트가 줄 바꿈이면 tail은 해당 파일을 반환 한 다음 명령 대체에서 제거합니다. 결과는 빈 문자열입니다. -n 테스트가 실패하고 에코가 실행되지 않습니다.
파일이 비어 있으면 명령 대체 결과도 빈 문자열이며 echo가 다시 실행되지 않습니다. 빈 파일은 유효하지 않은 텍스트 파일이 아니거나 빈 줄이있는 비어 있지 않은 텍스트 파일과 동일하지 않기 때문에 바람직합니다.
yash
파일의 마지막 문자가 멀티 바이트 문자 (예 : UTF-8 로케일)이거나 로케일이 C이고 파일의 마지막 바이트에 8 번째 비트 세트가있는 경우에는 작동하지 않습니다 . 다른 쉘 (zsh 제외)의 경우 파일이 NUL 바이트로 끝난 경우 줄 바꿈을 추가하지 않습니다 (다시 말해 줄 바꿈을 추가 한 후에도 입력이 텍스트가 아님).
다음에 관계없이 줄 바꿈을 추가하십시오.
echo >> filename
다음은 파이썬을 사용하여 줄 바꿈을 추가하기 전에 줄 바꿈이 있는지 확인하는 방법입니다.
f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
echo ""
보다 강력 해 보입니다 echo -n '\n'
. 또는 당신은 사용할 수 있습니다printf '\n'
가장 빠른 해결책은 다음과 같습니다.
[ -n "$(tail -c1 file)" ] && printf '\n' >>file
정말 빠릅니다.
중간 크기의 파일 seq 99999999 >file
에서는 몇 초가 걸립니다.
다른 솔루션은 시간이 오래 걸립니다 :
[ -n "$(tail -c1 file)" ] && printf '\n' >>file 0.013 sec
vi -ecwq file 2.544 sec
paste file 1<> file 31.943 sec
ed -s file <<< w 1m 4.422 sec
sed -i -e '$a\' file 3m 20.931 sec
ash, bash, lksh, mksh, ksh93, attsh 및 zsh에서 작동하지만 yash에서는 작동하지 않습니다.
yash (및 위에 나열된 다른 모든 셸)에 이식 가능한 솔루션이 필요한 경우 조금 더 복잡해질 수 있습니다.
f=file
if [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then printf '\n' >>"$f"
fi
파일의 마지막 바이트가 개행인지 테스트하는 가장 빠른 방법은 마지막 바이트 만 읽는 것입니다. 그것으로 할 수 있습니다 tail -c1 file
. 그러나 셸에 따라 일반적으로 명령 확장 내에서 마지막 줄 바꿈을 제거하지 못하는 경우 (예 : 파일의 마지막 문자가 UTF- 인 경우 yash에서 바이트 값이 줄 바꿈인지 테스트하는 간단한 방법) 8 값.
파일의 마지막 바이트가 새로운 행인지 확인하는 올바른 POSIX 호환 모든 (합리적인) 쉘 방법은 xxd 또는 hexdump를 사용하는 것입니다.
tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'
그런 다음 위의 출력을 비교하여 0A
강력한 테스트를 제공합니다.
비어있는 파일에 새 줄을 추가하지 않는 것이 좋습니다. 물론
의 마지막 문자를 제공하지 못하는 파일 0A
:
f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"
짧고 달다. 마지막 바이트 (EOF로 검색)를 읽으므로 시간이 거의 걸리지 않습니다. 파일이 큰지는 중요하지 않습니다. 그런 다음 필요한 경우 1 바이트 만 추가하십시오.
임시 파일이 필요하거나 사용되지 않습니다. 하드 링크는 영향을받지 않습니다.
이 테스트를 두 번 실행 하면 다른 줄 바꿈이 추가 되지 않습니다 .
xxd
아닙니다 hexdump
. POSIX toolchest에서는 od -An -tx1
바이트의 16 진수 값을 가져옵니다.
파일을 마지막으로 편집 한 사용자의 편집기를 수정하는 것이 좋습니다. 당신이 파일을 편집 한 마지막 사람이라면-어떤 편집기를 사용하고 있습니까? 나는 textmate ..?
emacs
파일의 끝에 줄 바꿈을 추가하지 마십시오.
(setq require-final-newline 'ask)
내.emacs
파이프 라인을 처리 할 때 줄 바꿈을 빠르게 추가하려면 다음을 사용하십시오.
outputting_program | { cat ; echo ; }
POSIX 와도 호환됩니다.
물론 파일로 리디렉션 할 수 있습니다.
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
입력에 널이없는 경우 :
paste - <>infile >&0
... 파일 이름이없는 경우 항상 줄 바꿈을 파일의 꼬리 끝에 만 추가하면 충분합니다. 그리고 입력 파일을 올바르게 읽으려면 한 번만 입력하면됩니다.
paste infile 1<> infile
대신에 필요 합니다.
질문에 직접 대답하지는 않지만 다음은 줄 바꿈으로 끝나지 않는 파일을 감지하기 위해 작성한 관련 스크립트입니다. 매우 빠릅니다.
find . -type f | # sort | # sort file names if you like
/usr/bin/perl -lne '
open FH, "<", $_ or do { print " error: $_"; next };
$pos = sysseek FH, 0, 2; # seek to EOF
if (!defined $pos) { print " error: $_"; next }
if ($pos == 0) { print " empty: $_"; next }
$pos = sysseek FH, -1, 1; # seek to last char
if (!defined $pos) { print " error: $_"; next }
$cnt = sysread FH, $c, 1;
if (!$cnt) { print " error: $_"; next }
if ($c eq "\n") { print " EOL: $_"; next }
else { print "no EOL: $_"; next }
'
perl 스크립트는 stdin에서 (선택적으로 정렬 된) 파일 이름 목록을 읽고 모든 파일에 대해 파일이 줄 바꿈으로 끝나는 지 여부를 판별하기 위해 마지막 바이트를 읽습니다. 각 파일의 전체 내용을 읽지 않기 때문에 매우 빠릅니다. 읽은 각 파일에 대해 한 줄씩 출력합니다. 어떤 종류의 오류가 발생하면 접두어 "error :", 파일이 비어있는 경우 "empty :"(개행으로 끝나지 않습니다!), "EOL :"( "끝 파일이 줄 바꿈으로 끝나는 경우 "line") 파일이 줄 바꿈으로 끝나지 않으면 "EOL 없음 :"입니다.
참고 :이 스크립트는 줄 바꿈이 포함 된 파일 이름을 처리하지 않습니다. GNU 또는 BSD 시스템을 사용하는 경우 다음과 같이 -print0을 찾아서 -z를 정렬하고 -0을 펄로 추가하여 가능한 모든 파일 이름을 처리 할 수 있습니다.
find . -type f -print0 | sort -z |
/usr/bin/perl -ln0e '
open FH, "<", $_ or do { print " error: $_"; next };
$pos = sysseek FH, 0, 2; # seek to EOF
if (!defined $pos) { print " error: $_"; next }
if ($pos == 0) { print " empty: $_"; next }
$pos = sysseek FH, -1, 1; # seek to last char
if (!defined $pos) { print " error: $_"; next }
$cnt = sysread FH, $c, 1;
if (!$cnt) { print " error: $_"; next }
if ($c eq "\n") { print " EOL: $_"; next }
else { print "no EOL: $_"; next }
'
물론 출력에 줄 바꿈을 사용하여 파일 이름을 인코딩하는 방법을 생각해 내야합니다 (리더의 연습으로 남음).
원하는 경우 출력을 필터링하여 파일이없는 파일에 줄 바꾸기를 추가 할 수 있습니다.
echo >> "$filename"
일부 버전의 셸 및 기타 유틸리티는 이러한 파일을 읽을 때 누락 된 최종 개행을 제대로 처리하지 않으므로 최종 개행이 없으면 스크립트에 버그가 발생할 수 있습니다.
내 경험상 최종 줄 바꿈이 부족한 것은 다양한 Windows 유틸리티를 사용하여 파일을 편집하기 때문입니다. 나는 파일을 편집 할 때 vim이 최종 줄 바꿈을 누락시키는 것을 보지 못했습니다.
마지막으로 파일 이름 입력을 반복하여 줄 바꿈으로 끝나지 않는 파일을 인쇄 할 수있는 훨씬 더 짧은 (그러나 더 느린) 스크립트가 있습니다.
/usr/bin/perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...
vi
/ vim
/ ex
편집자가 자동으로 추가 <EOL>
파일이 이미 그것을 가지고 있지 않는 EOF에.
따라서 다음 중 하나를 시도하십시오.
vi -ecwq foo.txt
이는 다음과 같습니다.
ex -cwq foo.txt
테스트 :
$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt
여러 파일 을 수정 하려면 다음을 확인하십시오. 많은 파일에 대해 '파일 끝에 줄 바꿈 없음'을 수정하는 방법은 무엇입니까? 그래서
이것이 왜 중요한가? POSIX 호환 파일을 유지합니다 .
허용 된 답변을 현재 디렉토리 (및 하위 디렉토리)의 모든 파일에 적용하려면 다음을 수행하십시오.
$ find . -type f -exec sed -i -e '$a\' {} \;
이것은 Linux (Ubuntu)에서 작동합니다. OS X에서는 아마 -i ''
(unested) 를 사용해야 합니다.
find .
파일을 포함하여 모든 파일 이 나열됩니다 .git
. 제외 :find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
적어도 GNU 버전에서는 입력을 간단하게 grep ''
또는awk 1
정식화하여 아직없는 경우 최종 개행을 추가합니다. 그들은 프로세스에서 파일을 복사합니다.이 경우 큰 시간이 걸리지 만 (소스는 너무 커서 읽을 수 없어야합니까?)
mv file old; grep '' <old >file; touch -r old file
(파일을 수정했기 때문에 체크인하는 파일에서 괜찮을 수도 있지만) 더 조심하지 않으면 하드 링크, 기본이 아닌 권한 및 ACL 등이 손실됩니다.
grep '' file 1<> file
그래도 파일을 완전히 읽고 쓸 수 있습니다.
이것은 AIX ksh에서 작동합니다.
lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
echo "/n" >> *filename*
fi
필자의 경우 파일에 줄 바꿈이 없으면 wc
명령은 값을 반환 2
하고 줄 바꿈을 씁니다.
Patrick Oscity의 답변에 추가하여 특정 디렉토리에 적용하려면 다음을 사용할 수도 있습니다.
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
줄 바꿈을 추가하려는 디렉토리 내에서 이것을 실행하십시오.
파일이 Windows 줄 끝으로 끝나고 \r\n
Linux에있는 경우이 sed
명령을 사용할 수 있습니다 . \r\n
마지막 줄이 없으면 추가 합니다.
sed -i -e '$s/\([^\r]\)$/\1\r\n/'
설명:
-i replace in place
-e script to run
$ matches last line of a file
s substitute
\([^\r]\)$ search the last character in the line which is not a \r
\1\r\n replace it with itself and add \r\n
마지막 줄에 이미가 포함되어 있으면 \r\n
검색 정규 표현식이 일치하지 않으므로 아무 일도 일어나지 않습니다.
다음 fix-non-delimited-line
과 같은 스크립트를 작성할 수 있습니다 .
#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
if sysopen -rwu0 -- "$file"; then
if sysseek -w end -1; then
read -r x || print -u0
else
syserror -p "Can't seek in $file before the last byte: "
ret=1
fi
else
ret=1
fi
done
exit $ret
여기에 제공된 솔루션 중 일부와 달리
예를 들어 다음과 같이 사용할 수 있습니다.
that-script *.txt
또는:
git ls-files -z | xargs -0 that-script
POSIX로, 당신은 기능적으로 동등한 것을 할 수 있습니다.
export LC_ALL=C
ret=0
for file do
[ -s "$file" ] || continue
{
c=$(tail -c 1 | od -An -vtc)
case $c in
(*'\n'*) ;;
(*[![:space:]]*) printf '\n' >&0 || ret=$?;;
(*) ret=1;; # tail likely failed
esac
} 0<> "$file" || ret=$? # record failure to open
done