Як перевірити, чи використовує файл CRLF або LF, не змінюючи його?


48

Мені потрібно періодично запускати команду, яка забезпечує збереження деяких текстових файлів у режимі Linux. На жаль, dos2unixзавжди модифікує файл, що може зіпсувати часові позначки файлів і папок і спричинити зайві записи.

Сценарій, який я пишу, є в Bash, тому я вважаю за краще відповіді, засновані на Bash.

Відповіді:


41

Ви можете використовувати dos2unixяк фільтр і порівнювати його вихід з вихідним файлом:

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

2
Дуже розумний і корисний, тому що він тестує повний файл і не тільки перший або кілька рядків.
Халлолео

2
Може бути , ви могли б замінити testна myfile.txtдва рази в вашому прикладі , щоб уникнути плутанини з /usr/bin/test.
Петерино

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
Ви також маєте на увазі, що греп може приймати CR та LF саме так?
bodacydo

@bodacydo Це пояснюється у відповіді, на яку він посилається, а тепер також у редакції Скотта відповіді Бертса тут unix.stackexchange.com/a/79708/59699 .
dave_thompson_085

@ dave_thompson_085 Я не бачу пояснень. Він лише згадує CRLF, але не пояснює, що це таке.
бодацидо

1
@bodacydo stackoverflow.com/questions/73833 / ... каже , що find ... -exec file ... | grep CRLFдля файлу з символами кінця рядка DOS (тобто байт 0D 0A) «отримає Вас що - щось на кшталт: ./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.
Мелебій

яке значення $ перед схемою пошуку? @don_crissti
fersarr


21

Починаючи з версією 7.1dos2unix має -i, --infoможливість отримати інформацію про розриви рядків. Ви можете використовувати сам dos2unix, щоб перевірити, які файли потребують перетворення.

Приклад:

dos2unix -ic *.txt | xargs dos2unix

Ось посилання на сам журнал
Адам Ріцковський

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, а потім перетворити цей рядок у ціле число і порівняти його до нуля, і
  2. тому що grep -cпотрібно прочитати весь файл, порахувати всі виникнення шаблону, при цьому grep -qможна вийти, побачивши перше виникнення шаблону.

Примітки:

  • У всьому вищесказаному вам може знадобитися додати -Uпараметр (тобто використовувати -cUабо -qU), оскільки GNU grepздогадується, чи файл є текстовим файлом. Якщо він вважає, що файл є текстовим, він ігнорує повернення каретки в кінці рядків, намагаючись зробити $в регулярних виразах роботу «правильно» - навіть якщо регулярний вираз є \r$! Зазначення -U(або --binary) перекриває це здогадка, викликаючи grepтрактування файлів (файлів) як бінарних та передавання даних до механізму узгодження дослівно, з CR-закінченнями неушкодженими.
  • Не робіть цього grep … $'\r\n' myfile.txt, бо grepтрактує \nяк роздільник візерунка. Так само, як grep -E 'foo|'шукає рядки, що містять fooабо нульовий рядок, grep $'\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

grep $'\r\n'Здається, @rici відповідає всім файлам у моїй системі ...
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

Це також не змінює вміст файлу.


Найкраще і найпростіше рішення! потрібно більше голосів.
користувач648026

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
Я думаю, що це найелегантніше рішення; він не читає весь файл, лише перший рядок.
Адам Ріцковський

4

Якщо файл має закінчення рядка CR-LF у стилі DOS / Windows, то якщо ви подивитеся на нього за допомогою інструменту на основі Unix, ви побачите символи CR ('\ r') в кінці кожного рядка.

Ця команда:

grep -l '^M$' filename

буде надруковано, filenameякщо файл містить одну або декілька рядків із закінченнями рядків у стилі Windows, і нічого не буде надруковано, якщо цього немає. За винятком того, що ^Mмає бути буквальним символом повернення каретки, як правило, вводиться в термінал шляхом введення Ctrl+, Vа потім Enter (або Ctrl+, Vа потім Ctrl+ M). Оболонка bash дозволяє записати буквальне повернення каретки як $'\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 каже: "Відновіть Моніку"

1

Я використовую

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

який, здається, працює. Я вважаю, що вихід трохи легше читати, ніж

dos2unix < filename.txt | diff - filename.txt

Це також корисно, якщо ви не можете встановити dos2unixякусь причину.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.