Як перетворити нову лінію DOS / Windows (CRLF) в нову лінію Unix (LF) у сценарії Bash?


336

Як я можу програмно (тобто не користуватися vi) конвертувати нові рядки DOS / Windows в Unix?

Команди dos2unixі та unix2dosкоманди недоступні в певних системах. Як я можу емулювати їх з командами типу sed/ awk/ tr?


9
Загалом, просто встановити dos2unixза допомогою менеджера пакунків, це дійсно набагато простіше і існує на більшості платформ.
Бред Кох

1
Домовились! @BradKoch Простий як 'варити встановити dos2unix' на Mac OSX
SmileIT

Відповіді:


322

Ви можете використовувати trдля перетворення з DOS в Unix; однак, ви можете це зробити безпечно, лише якщо CR відображається у вашому файлі лише як перший байт пари байтів CRLF. Зазвичай це так. Потім ви використовуєте:

tr -d '\015' <DOS-file >UNIX-file

Зауважте, що ім’я DOS-fileвідрізняється від імені UNIX-file; якщо ви спробуєте використати те саме ім’я двічі, у вас виявиться відсутність даних у файлі.

Ви не можете зробити це навпаки (зі стандартним 'tr').

Якщо ви знаєте, як ввести повернення каретки до сценарію ( control-V, control-Mщоб ввести контроль-M), тоді:

sed 's/^M$//'     # DOS to Unix
sed 's/$/^M/'     # Unix to DOS

де '^ M' - символ керування-M. Ви також можете використовувати механізм bash цитування ANSI-C для визначення повернення перевезення:

sed $'s/\r$//'     # DOS to Unix
sed $'s/$/\r/'     # Unix to DOS

Однак якщо вам доведеться це робити дуже часто (не раз, грубо кажучи), набагато розумніше встановити програми перетворення (наприклад, dos2unixі unix2dos, можливо, dtouі utod) та використовувати їх.

Якщо вам потрібно обробити цілі каталоги та підкаталоги, ви можете використовувати zip:

zip -r -ll zipfile.zip somedir/
unzip zipfile.zip

Це створить zip-архів із закінченнями рядків, зміненими з CRLF на CR. unzipпотім поверне перетворені файли на місце (і попросить вас файл по файлу - ви можете відповісти: Так, всім). Подяка @vmsnomad за вказівку на це.


9
використовуючи, tr -d '\015' <DOS-file >UNIX-fileде DOS-file== UNIX-fileпросто призводить до порожнього файлу. На жаль, у вихідному файлі має бути інший файл, на жаль.
Buttle Butkus

3
@ButtleButkus: Ну так; саме тому я використав два різні назви. Якщо ви запам’ятовуєте вхідний файл до того, як програма прочитає все це, як ви робите, коли два рази використовуєте те саме ім’я, ви отримуєте порожній файл. Це однакова поведінка в системах, схожих на Unix. Для безпечного перегляду вхідного файлу потрібен спеціальний код. Дотримуйтесь інструкцій, і ви будете в порядку.
Джонатан Леффлер

Я, здається, пам'ятаю функцію пошуку-заміни файлів у деяких функціях.
Buttle Butkus

4
Є місця; ви повинні знати, де їх знайти. У межах, GNU sedваріант -i(для на місці) роботи; межі - це пов'язані файли та посилання. sortКоманда має «завжди» (з 1979 року, якщо не раніше) підтримує -oваріант , який може перерахувати один з вхідних файлів. Однак це частково тому, що він sortповинен прочитати весь його вклад, перш ніж він зможе написати будь-який із своїх результатів. Інші програми спорадично підтримують перезапис одного з вхідних файлів. Ви можете знайти програму (сценарій) загального призначення, щоб уникнути проблем у "Програмі UNIX програмування" від Kernighan & Pike.
Джонатан Леффлер

3
Третій варіант працював для мене, дякую. Я використав варіант -i: sed -i $'s/\r$//' filename- для редагування на місці. Я працюю на машині, яка не має доступу до Інтернету, тому встановлення програмного забезпечення є проблемою.
Warren Dew

64
tr -d "\r" < file

подивіться тут приклади, використовуючи sed:

# IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format.
sed 's/.$//'               # assumes that all lines end with CR/LF
sed 's/^M$//'              # in bash/tcsh, press Ctrl-V then Ctrl-M
sed 's/\x0D$//'            # works on ssed, gsed 3.02.80 or higher

# IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format.
sed "s/$/`echo -e \\\r`/"            # command line under ksh
sed 's/$'"/`echo \\\r`/"             # command line under bash
sed "s/$/`echo \\\r`/"               # command line under zsh
sed 's/$/\r/'                        # gsed 3.02.80 or higher

Використовуйте sed -iдля перетворення на місці, наприклад sed -i 's/..../' file.


10
Я використовував варіант, оскільки мій файл мав лише \r:tr "\r" "\n" < infile > outfile
Метт Тодд

1
@MattTodd Ви могли б опублікувати це як відповідь? -dхарактеризується більш часто і не допоможе в «тільки \r» ситуації.
n611x007

5
Зауважте, що запропоноване \rдля \nвідображення має ефект подвійного інтервалу між файлами; кожен рядок CRLF, що закінчується в DOS, стає \n\nUnix.
Джонатан Леффлер

Чи можу це зробити рекурсивно?
Аарон Франке

36

Робити це за допомогою POSIX складно:

  • POSIX Sed не підтримує \rабо \15. Навіть якщо це було, варіант на місці -i- це не POSIX

  • POSIX Awk робить підтримку \rі \15, проте -i inplaceваріант не POSIX

  • d2u і dos2unix НЕ POSIX утиліт , але колишній є

  • POSIX колишній не підтримує \r, \15, \nабо\12

Щоб видалити повернення вагона:

ex -bsc '%!awk "{sub(/\r/,\"\")}1"' -cx file

Щоб додати декларації про перевезення:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file

2
Схоже, підтримує POSIX . tr\r Таким чином, ви також можете використовувати printf '%s\n' '%!tr -d "\r"' x | ex file(хоча це надано, це видалено, \rнавіть якщо це не було раніше \n). Крім того , -bваріант exне визначений POSIX.
Wildcard

1
Зробити це в POSIX легко. Вставте CR-літерал у скрипт, ввівши його (це control-M).
Джошуа

28

Ви можете використовувати vim програмно за допомогою параметра -c {command}:

Dos для Unix:

vim file.txt -c "set ff=unix" -c ":wq"

Unix для дос:

vim file.txt -c "set ff=dos" -c ":wq"

"встановити ff = unix / dos" означає змінити формат файлу (ff) файлу на формат Unix / DOS в кінці рядка

": wq" означає записати файл на диск і вийти з редактора (що дозволяє використовувати команду в циклі)


3
Це виглядало як найелегантніше рішення, але відсутність пояснень щодо того, що означає wq, прикро.
Джоррік Слейстер

5
Кожен, хто використовує, viбуде знати, що :wqозначає. Для тих, хто не має 3 символів, означає 1) відкрити командну область vi, 2) написати та 3) вийти.
Девід Ньюкомб

Я не здогадувався, що ви можете інтерактивно додавати команди для vim з CLI
Роберт Дандон

ви можете використовувати ": x" замість ": wq"
JosephConrad


23

Для перетворення файлу на місці використання

dos2unix <filename>

Для виведення конвертованого тексту в інше використання файлу

dos2unix -n <input-file> <output-file>

Ви можете встановити його на Ubuntu або Debian з

sudo apt install dos2unix

або на macOS, використовуючи домашню мову

brew install dos2unix

1
Я знаю, що питання задає альтернативи dos2unix, але це перший результат Google.
Борис

18

Цю проблему можна вирішити за допомогою стандартних інструментів, але є достатньо багато пасток для необережного, що я рекомендую вам встановити flipкоманду, написану понад 20 років тому Рахулом Десі, автором zoo. Це прекрасна робота по перетворенню форматів файлів, уникаючи, наприклад, уникнення випадкового знищення бінарних файлів, що занадто просто, якщо ви просто змагаєтесь навколо зміни кожного CRLF, який ви бачите ...


Будь-який спосіб зробити це потоковим способом, не змінюючи оригінальний файл?
augurar

@augurar ви можете перевірити «подібні пакети» packages.debian.org/wheezy/flip
n611x007

У мене був досвід зламати половину моєї ОС, просто запустивши texxto з неправильним прапором. Будьте уважні, особливо якщо ви хочете робити це на цілих папках.
A_P

14

Наразі розміщені рішення стосуються лише частини проблеми, перетворюючи CRLF DOS / Windows у LF Unix; частина, якої вони відсутні, полягає в тому, що DOS використовує CRLF як роздільник рядків , а Unix використовує LF як термінатор лінії . Різниця полягає в тому, що файл DOS (як правило) після останнього рядка у файлі нічого не матиме, тоді як Unix буде. Щоб виконати перетворення належним чином, вам потрібно додати цей остаточний LF (якщо файл не має нульової довжини, тобто у ньому взагалі немає рядків). Моя улюблена заклик до цього (з трохи доданої логіки для обробки файлів, розділених CR-стилем Mac, а не молетування файлів, які вже є у форматі Unix) - це трохи перл:

perl -pe 'if ( s/\r\n?/\n/g ) { $f=1 }; if ( $f || ! $m ) { s/([^\n])\z/$1\n/ }; $m=1' PCfile.txt

Зауважте, що це надсилає Уніфіковану версію файлу до stdout. Якщо ви хочете замінити файл на Уніфіковану версію, додайте -iпрапор Perl .


@LudovicZenohateLagouardette Це був звичайний текстовий файл (наприклад, текст csv або текст із дебетом) чи щось інше? Якщо він був у якомусь форматі бази даних, маніпулювання ним, як якщо б це був текст, дуже ймовірно, що може пошкодити його внутрішню структуру.
Гордон Девіссон

Простий текст csv, але я думаю, що вкладення було дивним. Я думаю, що це заплуталося через це. Однак не хвилюйтеся. Я завжди збираю резервні копії, це не був навіть реальний набір даних, а лише 1 Гбіт. Справжній - 26 Гбіт.
Людовик Зенохат Лагуардет

14

Якщо у вас немає доступу до dos2unix , але ви можете прочитати цю сторінку, ви можете скопіювати / вставити dos2unix.py звідси.

#!/usr/bin/env python
"""\
convert dos linefeeds (crlf) to unix (lf)
usage: dos2unix.py <input> <output>
"""
import sys

if len(sys.argv[1:]) != 2:
  sys.exit(__doc__)

content = ''
outsize = 0
with open(sys.argv[1], 'rb') as infile:
  content = infile.read()
with open(sys.argv[2], 'wb') as output:
  for line in content.splitlines():
    outsize += len(line) + 1
    output.write(line + '\n')

print("Done. Saved %s bytes." % (len(content)-outsize))

Перехресне повідомлення від суперрусера .


1
Використання вводить в оману. Реальний dos2unixперетворює всі вхідні файли за замовчуванням. Ваше використання має на увазі -nпараметр. І справжній dos2unix- це фільтр, який читає з stdin, пише в stdout, якщо файли не задані.
jfs

8

Супер пупер легко з PCRE;

Як сценарій, або замініть $@файли.

#!/usr/bin/env bash
perl -pi -e 's/\r\n/\n/g' -- $@

Це замінить ваші файли на місці!

Я рекомендую робити це лише за допомогою резервної копії (керування версіями чи іншим способом)


Дякую! Це працює, хоча я пишу ім'я файлу і ні --. Я вибрав це рішення, тому що це легко зрозуміти і адаптувати для мене. FYI, це те, що роблять перемикачі: -pприпустимо цикл "while input", -iвідредагуйте вхідний файл на місці, -eвиконайте наступну команду
Rolf

Строго кажучи, PCRE - це повторне втілення двигуна регулярних викидів Perl, а не двигуна регулярних викидів від Perl. Вони обидва мають цю можливість, хоча існують і відмінності, незважаючи на вказівку на ім'я.
трійка

6

Ще більш просте рішення для програми:

awk -v ORS='\r\n' '1' unix.txt > dos.txt

Технічно "1" - це ваша програма, але для отримання додаткової опції потрібна програма "b".

ОНОВЛЕННЯ : Після перегляду цієї сторінки вперше за довгий час я зрозумів, що ще ніхто не опублікував внутрішнє рішення, ось ось одне:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt

Це зручно, але просто для того, щоб бути зрозумілим: це перекладає Unix -> Windows / DOS, що є протилежним напрямку того, про що вимагала ОП.
mklement0

5
Це було зроблено навмисно, залишено як вправу для автора. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK

Чудово (і кудо вам за педагогічну вишуканість).
mklement0

1
"b / c awk вимагає його, коли надається опція." - awk завжди вимагає програми, вказані варіанти чи ні.
mklement0

1
Чистий розчин bash цікавий, але набагато повільніше, ніж еквівалент awkабо sedрішення. Крім того, ви повинні використовувати while IFS= read -r lineдля достовірного збереження вхідних рядків, інакше пробіл проміжних та кінцевих пробілів обрізаний (як альтернатива, не використовуйте в readкоманді імені змінної та не працюйте з нею $REPLY).
mklement0

5

Доводилося просто замислюватися над тим самим питанням (на стороні Windows, але однаково застосовно до Linux.) На жаль, ніхто не згадав про дуже автоматизований спосіб здійснення перетворення CRLF <-> LF для текстових файлів, використовуючи старий добрий zip -llваріант (Info-ZIP):

zip -ll textfiles-lf.zip files-with-crlf-eol.*
unzip textfiles-lf.zip 

ПРИМІТКА: це створило б zip-файл, зберігаючи оригінальні імена файлів, але перетворюючи закінчення рядків у LF. Потім unzipбуде вилучено файли як zip'ed, тобто з їх оригінальними іменами (але з LF-закінченнями), тим самим запропонувавши перезаписати локальні оригінальні файли, якщо такі є.

Відповідний уривок із zip --help:

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)

На мене найкраща відповідь, оскільки вона може обробляти цілі каталоги та підкаталоги. Я радий, що копав так далеко вниз.
карам

5

Цікаво, що в моєму git-bash на Windows вже sed ""зроблено трюк:

$ echo -e "abc\r" >tst.txt
$ file tst.txt
tst.txt: ASCII text, with CRLF line terminators
$ sed -i "" tst.txt
$ file tst.txt
tst.txt: ASCII text

Я здогадуюсь, що sed ігнорує їх, читаючи рядки з вхідних даних і завжди записує закінчення рядків Unix на виході.


4

Це працювало для мене

tr "\r" "\n" < sampledata.csv > sampledata2.csv 

9
Це буде конвертувати кожну одиночний DOS-рядок в два UNIX-символи нового рядка.
Мелебій

2

Для ОС Mac OSX, якщо у вас встановлено домашню програму [ http://brew.sh/ freedict1]

brew install dos2unix

for csv in *.csv; do dos2unix -c mac ${csv}; done;

Переконайтеся, що ви зробили копії файлів, оскільки ця команда змінить файли на місці. Опція -c mac робить перемикач сумісним з osx.


Ця відповідь насправді не відповідає оригінальному питанню плаката.
hlin117

2
Користувачі OS X не повинні використовувати -c mac, що призначено для перетворення попередньо OS X-тільки нових CRрядків. Ви хочете використовувати цей режим лише для файлів до та з Mac OS 9 або раніше.
askewchan

2

TIMTOWTDI!

perl -pe 's/\r\n/\n/; s/([^\n])\z/$1\n/ if eof' PCfile.txt

За матеріалами @GordonDavisson

Потрібно враховувати можливість [noeol]...


2

Ви можете використовувати awk. Встановіть роздільник записів ( RS) на регулярне вираження, яке відповідає всім можливим символам або новим рядкам. І встановіть роздільник записів виводу ( ORS) на символ нового рядка в стилі Unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt

Це той, хто працював на мене (MacOS, git diffшоу ^ M, редагується vim)
Доріан

2

У Linux легко перетворити ^ M (ctrl-M) в * nix newlines (^ J) за допомогою sed.

Це буде щось подібне на CLI, насправді в тексті буде розрив рядка. Однак \ передає ^ J разом із sed:

sed 's/^M/\
/g' < ffmpeg.log > new.log

Ви отримуєте це, використовуючи ^ V (ctrl-V), ^ M (ctrl-M) та \ (зворотний косий рядок) під час введення:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log

2
sed --expression='s/\r\n/\n/g'

Оскільки в питанні йдеться про sed, це найбільш прямий спосіб використання sed для досягнення цього. Що говорить вираз, замініть всі перевезення та повернення рядків лише на подачу рядків. Це те, що вам потрібно, коли ви переходите з Windows в Unix. Я перевірив, що це працює.


Привіт, Джон Пол. Ця відповідь позначена для видалення, тому вона опинилася в черзі на огляд для мене. Загалом, коли у вас є таке питання, якому вже 8 років, з 22 відповідями, ви хочете пояснити, наскільки ваша відповідь корисна таким чином, що інші відповіді не є.
zzxyz

0

Як розширення до рішення Unix в DOS Джонатана Леффлера для безпечного перетворення в DOS, коли ви не впевнені в поточних закінченнях файлу:

sed '/^M$/! s/$/^M/'

Це перевіряє, що рядок ще не закінчується в CRLF перед переходом у CRLF.


0

Я створив скрипт на основі прийнятої відповіді, щоб ви могли його перетворити безпосередньо, не потребуючи додаткового файлу в кінцевому підсумку, а потім видаляти та перейменувати.

convert-crlf-to-lf() {
    file="$1"
    tr -d '\015' <"$file" >"$file"2
    rm -rf "$file"
    mv "$file"2 "$file"
}

просто переконайтесь, що у вас є такий файл, як "file1.txt", що "file1.txt2" вже не існує, або він буде перезаписаний, я використовую це як тимчасове місце для зберігання файлу.


0

З bash 4.2 та новішими версіями ви можете використовувати щось подібне, щоб зняти контур CR, який використовує лише вбудовані bash:

if [[ "${str: -1}" == $'\r' ]]; then
    str="${str:: -1}"
fi

-3

Я спробував sed 's / ^ M $ //' file.txt на OSX, а також кілька інших методів ( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- dos-line-endings або http://hintsforums.macworld.com/archive/index.php/t-125.html ). Жоден не працював, файл залишався незмінним (для відтворення ^ M) потрібен btw Ctrl-v Enter. Врешті-решт я використав TextWrangler. Це не строго командний рядок, але він працює, і він не скаржиться.

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