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

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

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

25 ответов

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

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

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

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

Если вы знаете, как ввести возврат каретки в сценарий (control-V, control-M для ввода control-M), то:

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

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

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

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

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

Дос в Unix:

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

Unix to dos:

vim file.txt -c "set ff=dos" -c ":wq"
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,

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

dos2unix <filename>

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

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

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


Я знаю, что вопрос явно требует альтернатив этой утилите, но это первый результат поиска в Google по запросу "конвертировать dos в окончание строки Unix".

Делать это с 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

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

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

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

perl -pe 's/\r$//' < dos.txt > unix.txt

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

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

Кросс-пост от суперпользователя.

Опубликованные на данный момент решения касаются только части проблемы, превращая DRL /Windows CRLF в LIX Unix; часть, которую они пропускают, состоит в том, что DOS использует CRLF в качестве разделителя строк, в то время как Unix использует LF в качестве ограничителя строки. Разница в том, что файл DOS (обычно) не будет иметь ничего после последней строки в файле, в то время как Unix будет. Чтобы правильно выполнить преобразование, вам нужно добавить этот окончательный LF (если только файл не имеет нулевой длины, то есть не содержит строк). Мое любимое заклинание для этого (с небольшой добавленной логикой для обработки файлов в стиле Mac в стиле CR, а не для файлов, которые уже находятся в формате 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 версию файла на стандартный вывод. Если вы хотите заменить файл на Unixified версию, добавьте perl's -i флаг.

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

Как скрипт или заменить $@ с вашими файлами.

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

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

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

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

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

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

ОБНОВЛЕНИЕ: После повторного посещения этой страницы впервые за долгое время я понял, что никто еще не опубликовал внутреннее решение, поэтому вот одно:

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

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

Просто задумался над тем же вопросом (на стороне 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)

Для Mac OSX, если у вас установлен homebrew [ http://brew.sh/%5D%5B1%5D

brew install dos2unix

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

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

Просто дополняя отличный ответ @Jonathan Leffler, если у вас есть файл со смешанными окончаниями строк (LF и CRLF) и вам нужно нормализовать до CRLF (DOS), последовательно используйте следующие команды...

      # DOS to Unix
sed -i $'s/\r$//' "<YOUR_FILE>"

# Unix to DOS (normalized)
sed -i $'s/$/\r/' "<YOUR_FILE>"

ПРИМЕЧАНИЕ. Если у вас есть файл со смешанными окончаниями строк (LF и CRLF), одна только вторая команда выше вызовет беспорядок.

Если вам нужно преобразовать в LF (Unix), будет достаточно только первой команды...

      # DOS to Unix
sed -i $'s/\r$//' "<YOUR_FILE>"

Спасибо!

[Ссылка (ы).: https://stackoverflow.com/a/3777853/3223785 ]

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

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

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

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

TIMTOWTDI!

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

Основано на @GordonDavisson

Надо учитывать возможность [noeol]...

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

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

В Linux легко конвертировать ^M (ctrl-M) в *nix переводы строк (^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

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

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

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

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

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

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

В bash 4.2 и новее вы можете использовать что-то вроде этого для удаления завершающего CR, который использует только встроенные модули bash:

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

Я попробовал файл sed 's/^M$//' для 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). Ничего не сработало, файл остался без изменений (кстати, Ctrl-v Enter был необходим для воспроизведения ^M). В конце концов я использовал TextWrangler. Это не строго командная строка, но она работает и не жалуется.

Существует множество ответов на 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 является выходным каталогом, поэтому вы можете заменить его текущими файлами или, в целях безопасности / резервного копирования, вывести в отдельный каталог.

Другие вопросы по тегам