จะทดสอบได้อย่างไรว่าไฟล์ใช้ 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
หมายเหตุคุณจะต้องลบการ-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 มันอธิบายในคำตอบที่เขาเชื่อมโยงไปและตอนนี้ยังอยู่ในการแก้ไขสกอตต์ของคำตอบ 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 CRLFสำหรับไฟล์ที่ลงท้ายด้วย DOS line (เช่น bytes 0D 0A 0 ")" จะทำให้คุณได้รับสิ่งที่ชอบ: ./1/dos1.txt: ASCII text, with CRLF line terminators ตามที่คุณเห็นสิ่งนี้มีสตริง CRLF จริงดังนั้นจึงจับคู่โดยgrepมองหา สตริงธรรมดา CRLF
dave_thompson_085

22

คุณสามารถลองgrepใช้รหัส CRLF เลขฐานแปดได้:

grep -U $'\015' myfile.txt

หรือฐานสิบหก:

grep -U $'\x0D' myfile.txt

แน่นอนว่าสมมติฐานนี้คือไฟล์ข้อความ
mdpc

2
ฉันเช่นนี้grepการใช้งานเพราะมันช่วยให้ผมได้อย่างง่ายดายรายการไฟล์ดังกล่าวทั้งหมดในไดเรกทอรีที่มีและผ่านออกไปgrep -lU $'\x0D' * xargs
Melebius

ความหมายของ $ ก่อนรูปแบบการค้นหาคืออะไร @don_crissti
fersarr


21

ตั้งแต่รุ่น7.1dos2unix มี-i, --infoตัวเลือกที่จะได้รับข้อมูลเกี่ยวกับการแบ่งบรรทัด คุณสามารถใช้ dos2unix เพื่อทดสอบว่าไฟล์ใดต้องการการแปลง

ตัวอย่าง:

dos2unix -ic *.txt | xargs dos2unix

นี่คือลิงก์ไปยังตัวเปลี่ยนแปลงเองwaterlan.home.xs4all.nl/dos2unix/NEWS.txt
Adam Ryczkowski

13

วิธีแรก ( grep):

นับจำนวนบรรทัดที่มีการขึ้นบรรทัดใหม่:

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

นับจำนวนบรรทัดที่ลงท้ายด้วย carriage return:

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

โดยทั่วไปจะเทียบเท่า การขึ้นบรรทัดใหม่กลับด้านในของเส้น (เช่นไม่ใช่ที่จุดสิ้นสุด) นั้นหายาก

มีประสิทธิภาพมากกว่า:

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

นี้มีประสิทธิภาพมากขึ้น

  1. เพราะมันไม่จำเป็นต้องแปลงจำนวนเป็นสตริง ASCII แล้วแปลงสตริงนั้นกลับไปเป็นจำนวนเต็มและเปรียบเทียบกับศูนย์และ
  2. เพราะgrep -cจำเป็นต้องอ่านไฟล์ทั้งหมดเพื่อนับการเกิดขึ้นทั้งหมดของรูปแบบในขณะที่grep -qสามารถออกจากเมื่อเห็นการเกิดขึ้นครั้งแรกของรูปแบบ

หมายเหตุ:

  • ตลอดข้างต้นคุณอาจต้องเพิ่ม-Uตัวเลือก (เช่นใช้-cUหรือ-qU) เนื่องจาก GNU grepเดาว่าไฟล์นั้นเป็นไฟล์ข้อความหรือไม่ หากคิดว่าไฟล์เป็นข้อความมันจะละเว้นการขึ้นบรรทัดใหม่ที่ท้ายบรรทัดในความพยายามที่จะทำให้$นิพจน์ทั่วไปทำงาน "ถูกต้อง" - แม้ว่านิพจน์ทั่วไปจะเป็น\r$! การระบุ-U(หรือ--binary) ลบล้างการคาดเดานี้ทำให้grepเป็นไฟล์ไบนารีและส่งผ่านข้อมูลไปยังกลไกการจับคู่คำต่อคำด้วย CR-endings เหมือนเดิม
  • อย่าทำgrep … $'\r\n' myfile.txtเพราะgrepถือว่า\nเป็นตัวคั่นรูปแบบ เช่นเดียวกับการgrep -E 'foo|'ค้นหาบรรทัดที่มีfooหรือสตริง null grep $'\r\n'ค้นหาบรรทัดที่มี\rหรือสตริง null และทุกบรรทัดตรงกับสตริง null

วิธีที่สอง ( 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 -bส่งออกเฉพาะประเภทไฟล์และไม่ใช่ชื่อไฟล์ หากไม่มีสิ่งนี้ไฟล์ที่มีชื่อรวมตัวละครCRLF จะทำให้เกิดผลบวกปลอม
  • file - < filenameทำงานได้แม้กระทั่งถ้าเริ่มต้นด้วย  filename ดูBash script: ตรวจสอบว่าไฟล์เป็นไฟล์ข้อความหรือไม่-

ระวังว่าการตรวจสอบผลลัพธ์จาก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 โดยคำตอบที่ดีที่สุด ไม่มีการพึ่งพาไม่มีสคริปต์ทุบตีที่ซับซ้อน แค่-Aให้แมว หนึ่งเคล็ดลับแม้ว่าจะใช้cat -A file | lessถ้าไฟล์มีขนาดใหญ่เกินไป ฉันแน่ใจว่าไม่ใช่เรื่องผิดปกติที่จะต้องตรวจสอบการสิ้นสุดไฟล์สำหรับไฟล์ที่ยาวเป็นพิเศษ (กดqเพื่อออกน้อยกว่า)
Nicholas Pipitone

4

ฟังก์ชั่นทุบตีสำหรับคุณ:

# 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

4

หากไฟล์มีจุดสิ้นสุดบรรทัดบรรทัด CR-LF สไตล์ DOS / Windows ดังนั้นหากคุณดูโดยใช้เครื่องมือที่ใช้ Unix คุณจะเห็นอักขระ CR ('\ r') ที่ท้ายแต่ละบรรทัด

คำสั่งนี้:

grep -l '^M$' filename

จะพิมพ์filenameหากไฟล์มีหนึ่งบรรทัดขึ้นไปที่มีการสิ้นสุดบรรทัดสไตล์ Windows และจะพิมพ์อะไรก็ได้หากไม่มี ยกเว้นว่า^Mจะต้องเป็นตัวอักษรกลับรถตามปกติมักจะป้อนใน terminal โดยพิมพ์Ctrl+ Vตามด้วยEnter (หรือCtrl+ VและCtrl+ แล้ว+ M) เปลือกทุบตีช่วยให้คุณเขียนการขึ้นบรรทัดใหม่ตามตัวอักษร$'\r'( เอกสารที่นี่ ) เพื่อให้คุณสามารถเขียน:

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
คุณสามารถเข้ารหัสการขึ้นบรรทัดใหม่บนบรรทัดรับคำสั่งใน bash (และเชลล์อื่น ๆ ) โดยพิมพ์$'\r'ดังที่กล่าวไว้ในคำตอบอื่น ๆ สำหรับคำถามนี้
สกอตต์

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 กล่าวว่า 'Reinstate Monica'

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.