파일을 수정하지 않고 CRLF 또는 LF를 사용하는지 여부를 테스트하는 방법은 무엇입니까?


48

일부 텍스트 파일이 Linux 모드로 유지 되도록하는 명령을 정기적으로 실행해야 합니다. 불행히도 dos2unix항상 파일을 수정하면 파일과 폴더의 타임 스탬프가 엉망이되어 불필요한 쓰기가 발생합니다.

내가 작성한 스크립트는 Bash에 있으므로 Bash를 기반으로 한 답변을 선호합니다.

답변:


41

dos2unix필터로 사용 하고 출력을 원본 파일과 비교할 수 있습니다 .

dos2unix < myfile.txt | cmp -s - myfile.txt

2
첫 번째 또는 몇 줄뿐만 아니라 전체 파일을 테스트하기 때문에 매우 영리하고 유용합니다 .
halloleo

2
어쩌면 당신은 대체 할 수 test에 의한 myfile.txt혼동을 피하기 위해 귀하의 예를 두 번 /usr/bin/test.
Peterino

1
NB -s출력을 보려면 플래그를 삭제해야합니다 . 매뉴얼 페이지에서 : -s, --quiet, --silent suppress all normal output
tobalr

24

목표가 타임 스탬프에 영향을 미치지 않도록하려면 타임 스탬프를 동일하게 유지 dos2unix하는 -k또는 --keepdate옵션이 있습니다. 임시 파일을 작성하고 이름을 바꾸려면 여전히 쓰기 작업을 수행해야하지만 타임 스탬프에는 영향을 미치지 않습니다.

파일 수정이 허용되지 않는 경우이 답변 에서 다음 솔루션을 사용할 수 있습니다 .

find . -not -type d -exec file "{}" ";" | grep CRLF

1
문자 그대로 CRLF를 4 자 C, R, L 및 F로 작성한다는 의미입니까?
bodacydo

7
grep이 CR과 LF를 그대로 사용할 수 있다는 의미입니까?
bodacydo

@bodacydo 그가 링크 한 답변과 지금은 Scott의 BertS 답변 편집 unix.stackexchange.com/a/79708/59699에 설명되어 있습니다.
dave_thompson_085

@ dave_thompson_085 설명이 보이지 않습니다. CRLF에 대해서만 언급하지만 그것이 무엇인지 설명하지는 않습니다.
bodacydo

1
@bodacydo stackoverflow.com/questions/73833/...는 말한다 find ... -exec file ... | grep CRLFDOS 라인 엔딩의 파일 (예 : 바이트 0D 0A)은 "당신에게 뭔가를 얻을 것이다 : ./1/dos1.txt: ASCII text, with CRLF line terminators 당신이 실제 문자열 CRLF를 포함하므로 일치한다 볼 수 있듯이 grep을 찾고 간단한 문자열 CRLF
dave_thompson_085

22

grepCRLF 코드, 8 진수를 시도 할 수 있습니다 .

grep -U $'\015' myfile.txt

또는 16 진수 :

grep -U $'\x0D' myfile.txt

물론 이것은 텍스트 파일이라고 가정합니다.
mdpc

2
grep사용법은 디렉토리에있는 모든 파일을 쉽게 나열 grep -lU $'\x0D' *하고 출력을 전달할 수 있기 때문에 좋아 합니다 xargs.
Melebius

검색 패턴 전의 $의 의미는 무엇입니까? @don_crissti
fersarr

1
@fersarr-unix.stackexchange.com/ a
don_crissti


13

첫 번째 방법 ( grep) :

캐리지 리턴이 포함 된 행을 계산하십시오.

[[ $(grep -c $'\r' myfile.txt) -gt 0 ]] && echo dos

캐리지 리턴으로 끝나는 줄을 세십시오 .

[[ $(grep -c $'\r$' myfile.txt) -gt 0 ]] && echo dos

이들은 일반적으로 동일합니다. 라인 내부 (즉, 끝이 아님)의 캐리지 리턴은 드물다.

보다 효율적인 :

grep -q $'\r' myfile.txt && echo dos

이것은 더 효율적입니다

  1. 카운트를 ASCII 문자열로 변환 한 다음 해당 문자열을 다시 정수로 변환하고 0과 비교할 필요가 없으므로
  2. grep -c패턴의 모든 발생을 계산하기 위해 전체 파일을 읽어야 하기 때문에 패턴 grep -q의 첫 번째 발생을보고 종료 할 수 있습니다.

노트:

  • GNU 는 파일이 텍스트 파일인지 추측 하기 때문에 위와 같은 -U옵션 을 추가해야 할 수도 있습니다 (예 : use -cU또는 -qU) grep. 파일이 텍스트라고 생각하면 $정규 표현식이 "정확하게"작동 하도록하기 위해 행 끝에있는 캐리지 리턴을 무시 합니다. 정규 표현식이 \r$! -U(또는 --binary)을 지정 하면이 추측이 우선 적용 grep되어 파일을 이진 파일로 취급하고 CR 끝이 그대로있는 상태로 데이터를 일치하는 메커니즘으로 그대로 전달합니다.
  • 패턴 구분 기호로 취급 grep … $'\r\n' myfile.txt되므로 하지 마십시오 . 그냥 같이 포함 된 줄을 찾습니다 또는 널 (null) 문자열, 포함하는 라인을 찾습니다 또는 널 (null) 문자열, 모든 라인은 널 (null) 문자열과 일치합니다.grep\ngrep -E 'foo|'foogrep $'\r\n'\r

두 번째 방법 ( file) :

[[ $(file myfile.txt) =~ CRLF ]] && echo dos

file다음과 같은 보고서가 있기 때문입니다 .

myfile.txt: UTF-8 Unicode text, with CRLF line terminators

보다 안전한 변형 :

[[ $(file -b - < myfile.txt) =~ CRLF ]] && echo dos

어디

file 영어 이외의 로케일 에서는 출력을 확인 하지 못할 수 있습니다.


1
개인적으로 나는 오 탐지의 수를 줄이기 위해 사용하지만 "$(echo -e '\r')"훨씬 더 간단한 것으로 바꿀 수 있습니다 . $'\r'$'\r\n'
rici

@rici grep $'\r\n'는 내 시스템의 모든 파일과 일치하는 것 같습니다.
depquid

@rici : 잘 잡아. 귀하의 제안에 따라 답변을 편집했습니다. — depquid : Windows를 사용하고 계십니까? :-) rici의 팁이 여기서 작동합니다.
BertS

@depquid (및 BertS는) 사실, 올바른 호출이 생각 grep -U $'\r$'방지하기 위해, grep두 번째 추측 라인 엔딩하려고합니다.
rici

또한 추가 검사가 필요한 -q대신 일치하는 것이 있으면 리턴 코드를 설정하는 데 사용할 수 있습니다 -c. 개인적으로 나는 당신의 두 번째 솔루션을 좋아하지만, file영어가 아닌 로케일 에서는 변덕스럽고 작동하지 않을 수도 있습니다.
rici

11

사용하다 cat -A

$ cat file
hello
hello

이제이 파일이 * NIX 시스템에서 작성된 경우 표시됩니다.

$ cat -A file
hello$
hello$

그러나이 파일이 Windows에서 만들어진 경우 표시됩니다

$ cat -A file
hello^M$
hello

^M대표 CR$대표 LF. Windows는 마지막 줄을 저장하지 않았습니다.CRLF

파일 내용도 변경되지 않습니다.


가장 간단한 솔루션! 더 많은 투표가 필요합니다.
user648026

1
+1 가장 좋은 답변. 의존성, 복잡한 bash 스크립트가 없습니다. 그냥 -A고양이에. cat -A file | less파일이 너무 큰 경우 한 가지 팁을 사용 하는 것 입니다. 특히 긴 파일의 파일 끝을 확인하는 것이 드문 일이 아니라고 확신합니다. ( q
누르지

4

당신을위한 bash 함수 :

# return 0 (true) if first line ends in CR
isDosFile() {
    [[ $(head -1 "$1") == *$'\r' ]]  
}

그럼 당신은 같은 일을 할 수 있습니다

streamFile () {
    if isDosFile /tmp/foo.txt; then
        sed 's/\r$//' "$1"
    else
        cat "$1"
    fi
}

streamFile /tmp/foo.txt | process_lines_without_CR

3
귀하 isDosFile()의 예에서 사용할 필요는 없습니다 : streamFile() { sed 's/\r$//' "$1" ; }.

1
이것이 가장 우아한 해결책이라고 생각합니다. 전체 파일을 읽지 않고 첫 번째 줄만 읽습니다.
Adam Ryczkowski 2016 년

4

파일에 DOS / Windows 스타일의 CR-LF 줄 끝이 있으면 Unix 기반 도구를 사용하여 볼 경우 각 줄 끝에 CR ( '\ r') 문자가 표시됩니다.

이 명령은

grep -l '^M$' filename

인쇄 할 filename파일이 Windows 스타일의 라인 엔딩 하나 이상의 라인이 포함 된 경우와 그렇지 않은 경우 아무 것도 인쇄되지 않습니다. ^M문자 그대로 캐리지 리턴 문자 여야 한다는 점을 제외하고는 일반적으로 Ctrl+ V다음에 Enter (또는 Ctrl+ VCtrl+ M) 를 입력하여 터미널에 입력 합니다. bash 쉘을 사용하면 리터럴 캐리지 리턴을 $'\r'( here 문서화 됨 )로 작성할 수 있으므로 다음과 같이 작성할 수 있습니다.

grep -l $'\r$' filename

다른 쉘도 비슷한 기능을 제공 할 수 있습니다.

대신 다른 도구를 사용할 수 있습니다.

awk '/\r$/ { exit(1) }' filename

이 상태로 종료됩니다 1(설정 $?1파일이 모든 Windows 스타일의 라인 엔딩을 포함하고, 상태에있는 경우) 0는 쉘에서 유용하게하지 않는 경우 if문 (의 부족주의 [브래킷 ]) :

if awk '/\r$/ { exit(1) }' filename ; then
    echo filename has Unix-style line endings
else
    echo filename has at least one Windows-style line ending
fi

파일에는 Unix 스타일과 Windows 스타일 줄 끝이 혼합되어 포함될 수 있습니다. 여기서는 Windows 스타일 줄 끝 이 있는 파일을 감지하려고한다고 가정합니다 .


1
$'\r'이 질문에 대한 다른 답변에서 언급 했듯이을 입력하여 bash (및 다른 쉘)의 명령 줄에서 캐리지 리턴을 인코딩 할 수 있습니다 .
Scott

2

사용 file:

$ file README.md
README.md: ASCII text, with CRLF line terminators

$ dos2unix README.md
dos2unix: converting file README.md to Unix format...

$ file README.md
README.md: ASCII text

이 아이디어는 두 가지 이전 답변에서 훨씬 더 철저하게 논의되었습니다.
G-Man

1

나는 사용하고있다

cat -v filename.txt | diff - filename.txt

작동하는 것 같습니다. 출력보다 읽기가 조금 더 쉽습니다.

dos2unix < filename.txt | diff - filename.txt

dos2unix어떤 이유로 설치할 수없는 경우에도 유용합니다 .

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