Как преобразовать новую строку DOS / Windows (CRLF) в новую строку Unix (LF) в сценарии Bash?

271

Как я могу программным способом (т.е. не использовать vi) конвертировать строки DOS/Windows в Unix?

Команды dos2unix и unix2dos недоступны в некоторых системах. Как я могу имитировать их с помощью команд типа sed/awk/tr?

  • 0
    Если вы можете скомпилировать в целевой системе, вы можете попробовать github.com/mdolidon/endlines ; это сделано, чтобы быть довольно портативным.
  • 8
    В общем, просто установите dos2unix с помощью вашего менеджера пакетов, это действительно намного проще и существует на большинстве платформ.
Показать ещё 1 комментарий
Теги:
newline

22 ответа

280

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

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

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

Вы не можете сделать это наоборот (со стандартным "tr" ).

Если вы знаете, как ввести возврат каретки в script (control-V, control-M, чтобы ввести control-M), тогда:

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

где '^ M' является символом control-M. Вы также можете использовать механизм bash dos2unix и unix2dos, или, возможно, dtou и utod) и используйте их.

  • 6
    используя tr -d '\015' <DOS-file >UNIX-file где DOS-file == UNIX-file просто приводит к пустому файлу. Выходной файл, к сожалению, должен быть другим файлом.
  • 2
    @ButtleButkus: ну да; Вот почему я использовал два разных имени. Если вы запакуете входной файл до того, как программа все его прочитает, как вы это делаете, когда дважды используете одно и то же имя, вы получите пустой файл. Это единообразное поведение в Unix-подобных системах. Требуется специальный код для безопасной перезаписи входного файла. Следуйте инструкциям, и все будет в порядке.
Показать ещё 7 комментариев
53
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
  • 0
    @MattTodd не могли бы вы опубликовать это как ответ? -d используется чаще и не поможет в ситуации "only \r ".
Показать ещё 2 комментария
36

Выполнение этого с помощью POSIX сложно:

  • POSIX Sed не поддерживает \r или \15. Даже если бы это было так, то на месте опция -i не POSIX

  • POSIX Awk поддерживает \r и \15, однако опция -i inplace не POSIX

  • d2u и dos2unix не утилиты POSIX, но ex

  • POSIX ex не поддерживает \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.
  • 1
    Делать это в POSIX легко. Вставьте CR-литерал в скрипт, набрав его (это control-M).
19

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

  • 0
    Любой способ сделать это в потоковом режиме, без изменения исходного файла?
  • 0
    @augurar вы можете проверить «похожие пакеты» packages.debian.org/wheezy/flip
Показать ещё 1 комментарий
18

Используя AWK, вы можете:

awk '{ sub("\r$", ""); print }' dos.txt > unix.txt

Используя Perl, вы можете:

perl -pe 's/\r$//' < dos.txt > unix.txt
  • 2
    Хорошее, портативное решение для awk .
  • 0
    Можно ли сделать это рекурсивно?
14

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

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

Обратите внимание, что это отправляет Unixified версию файла в stdout. Если вы хотите заменить файл Unixified, добавьте флаг perl -i.

  • 0
    RIP мой файл данных. что-то пошло не так xD
  • 0
    @LudovicZenohateLagouardette Был ли это простой текстовый файл (т. Е. CSV или разделенный табуляцией текст) или что-то еще? Если он был в каком-то формате базы данных, манипулирование им, как будто это был текст, очень вероятно, повредит его внутреннюю структуру.
Показать ещё 1 комментарий
11

Если у вас нет доступа к 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))

Отправлено через superuser.

  • 0
    Использование вводит в заблуждение. Настоящий dos2unix по умолчанию конвертирует все входные файлы. Ваше использование подразумевает параметр -n . А настоящий dos2unix - это фильтр, который читает из stdin и записывает в stdout, если файлы не передаются.
  • 1
    @JFSebastian, какие инструменты dos2unix реальны? Это в стандарте POSIX?
Показать ещё 2 комментария
7

Супер пупер с PCRE;

Как script или замените $@ вашими файлами.

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

Это перезапишет ваши файлы на месте!

Я рекомендую делать это только с помощью резервного копирования (контроль версий или иначе)

  • 0
    Спасибо! Это работает, хотя я пишу имя файла и нет -- . Я выбрал это решение, потому что это легко понять и адаптировать для меня. К вашему сведению, это то, что делают переключатели: -p предполагают цикл «во время ввода», -i редактируют входной файл на месте, -e выполняют следующую команду
  • 0
    Строго говоря, PCRE - это переопределение движка регулярных выражений Perl, а не движка регулярных выражений от Perl. У них обоих есть такая возможность, хотя есть и различия, несмотря на значение в названии.
6

Еще более простое awk-решение без программы:

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

Технически "1" - это ваша программа, b/c awk требует один, если задана опция.

UPDATE: После повторного просмотра этой страницы в первый раз за долгое время я понял, что никто еще не опубликовал внутреннее решение, так что вот один из них:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
  • 0
    Это удобно, но для ясности: это переводит Unix -> Windows / DOS, что противоположно тому, о чем просил OP.
  • 5
    Это было сделано специально, оставлено в качестве упражнения для автора. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
Показать ещё 4 комментария
4

Чтобы преобразовать файл на место, выполните

dos2unix <filename>

Для вывода преобразованного текста в другой файл выполните

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

Он уже установлен на Ubuntu и доступен на homebrew с brew install dos2unix


Я знаю, что вопрос явно запрашивает альтернативы этой утилите, но это первый результат поиска google для "convert dos to unix line endings".

4

Интересно, что в моем git - bash на окнах 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 на выходе.

3

Вы можете использовать vim программно с опцией -c {команда}:

Дос для Unix:

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

Unix для dos:

vim file.txt -c "set ff=dos" -c ":wq"
  • 0
    Это казалось самым элегантным решением, но отсутствие объяснения того, что означает wq, вызывает сожаление.
  • 0
    Любой, кто использует vi будет знать, что означает :wq . Для тех, кто не использует 3 символа, это означает 1) открытая область команд vi, 2) запись и 3) выход.
3

Это сработало для меня

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
  • 9
    Это будет конвертировать каждую DOS-строку в два UNIX-символы новой строки.
2

TIMTOWTDI!

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

Основываясь на @GordonDavisson

Необходимо рассмотреть возможность [noeol]...

1

Было бы просто подумать об этом же вопросе (на стороне 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)
1

Вы можете использовать awk. Установите разделитель записей (RS) в регулярное выражение, которое соответствует всем возможным символам новой строки или символам. И установите разделитель выходной записи (ORS) в символ новой строки в стиле unix.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
  • 0
    Это тот, который работал для меня (MacOS, git diff показывает ^ M, отредактировано в vim)
1

Для Mac OSX, если у вас установлен доморощенный [http:// brew.sh/] [1]

brew install dos2unix

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

Убедитесь, что вы сделали копии файлов, так как эта команда будет изменять файлы на месте. Параметр -c mac делает коммутатор совместимым с osx.

  • 0
    dos2unix оказался довольно удобным!
  • 0
    Этот ответ на самом деле не вопрос оригинального постера.
Показать ещё 1 комментарий
0
sed --expression='s/\r\n/\n/g'

Поскольку в вопросе упоминается sed, это самый простой способ использовать sed для достижения этой цели. В выражении говорится, что все возвраты каретки и перевод строки заменяются только переводом строки. Это то, что вам нужно, когда вы переходите с Windows на Unix. Я проверил, что это работает.

  • 0
    Привет, Джон Пол - этот ответ был помечен для удаления, поэтому я попал в очередь на проверку. В общем, когда у вас есть такой вопрос, которому 8 лет, с 22 ответами, вы захотите объяснить, насколько ваш ответ полезен в отличие от других существующих ответов.
0

В 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
0

В качестве расширения для решения Jonathan Leffler Unix для DOS, чтобы безопасно конвертировать в DOS, когда вы не уверены в текущем окончании строки файла:

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

Это проверяет, что линия еще не заканчивается в CRLF перед преобразованием в CRLF.

-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). Ничего не работало, файл остался без изменений (для воспроизведения ^ М) потребовалось ввести бит-в-В. В конце я использовал TextWrangler. Это не строго командная строка, но она работает, и она не жалуется.

-6

Есть много ответов awk/sed/etc, так как дополнение (поскольку это один из лучших результатов поиска для этой проблемы):

У вас может не быть dos2unix, но у вас есть iconv?

iconv -f UTF-16LE -t UTF-8 [filename.txt]
-f from format type
-t to format type

Или все файлы в каталоге:

find . -name "*.sql" -exec iconv -f UTF-16LE -t UTF-8 {} -o ./{} \;

Выполняет ту же самую команду для всех файлов .sql в текущей папке. -o - это выходной каталог, поэтому вы можете заменить его текущими файлами или, по соображениям безопасности/резервного копирования, выводить в отдельный каталог.

  • 1
    Это пытается добиться преобразования кодирования из UTF-16LE в UTF-8, но это не касается концов строк. Это не имеет ничего общего с задаваемым вопросом.
  • 0
    Виноват. Я проверю это, но я только что использовал ЭТОТ ДЕНЬ, чтобы исправить мою проблему с grep, не запущенным на моих файлах, потому что они были отформатированы в Windows.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню