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


336

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

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


9
В общем, просто установите dos2unixс помощью менеджера пакетов, это действительно намного проще и существует на большинстве платформ.
Брэд Кох

1
Согласовано! @BradKoch Простой как «brew install dos2unix» на Mac OSX
SmileIT

Ответы:


323

Вы можете использовать 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) и использовать их.

Если вам нужно обработать целые каталоги и подкаталоги, вы можете использовать zip:

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

Это создаст zip-архив с окончаниями строк, измененными с CRLF на CR. unzipзатем вернет преобразованные файлы обратно на место (и спросит вас файл за файлом - вы можете ответить: Да для всех). Кредиты @vmsnomad для указания на это.


9
использование tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-fileпросто приводит к пустому файлу. Выходной файл, к сожалению, должен быть другим файлом.
Баттл Буткус

3
@ButtleButkus: ну да; Вот почему я использовал два разных имени. Если вы запакуете входной файл до того, как программа все его прочитает, как вы это делаете, когда дважды используете одно и то же имя, вы получите пустой файл. Это единообразное поведение в Unix-подобных системах. Требуется специальный код для безопасной перезаписи входного файла. Следуйте инструкциям, и все будет в порядке.
Джонатан Леффлер

Кажется, я помню некоторые функции поиска-замены в файле.
Баттл Буткус

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

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

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\nв Unix.
Джонатан Леффлер

Могу ли я сделать это рекурсивно?
Аарон Франке

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 {команда}:

Дос в Unix:

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

Unix to dos:

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

«set ff = unix / dos» означает изменить формат файла (ff) файла на формат конца строки Unix / DOS

«: wq» означает запись файла на диск и выход из редактора (что позволяет использовать команду в цикле)


3
Это казалось самым элегантным решением, но отсутствие объяснения того, что означает wq, вызывает сожаление.
Джоррик Слейстер

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

Я понятия не имел, что вы могли бы интерактивно добавлять команды в vim из CLI
Роберт Дандон,

вы можете использовать «: x» вместо «: wq»
JosephConrad

25

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

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

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

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

2
Хорошее, портативное awk решение.
mklement0

24

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

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 вы можете проверить «похожие пакеты» packages.debian.org/wheezy/flip
n611x007

У меня был опыт взлома половины моей ОС, просто запустив texxto с неправильным флагом. Будьте осторожны, особенно если вы хотите сделать это на целых папках.
A_P

14

Опубликованные на данный момент решения касаются только части проблемы, превращая CRLF DOS / Windows в LF 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 версию, добавьте -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является фильтр, который читает из стандартного ввода, пишет в стандартный вывод, если файлы не передаются.
Jfs

8

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

В качестве сценария или заменить $@ваши файлы.

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

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

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


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

Строго говоря, PCRE - это переопределение движка регулярных выражений Perl, а не движка регулярных выражений от Perl. У них обоих есть такая возможность, хотя есть и различия, несмотря на смысл в названии.
tripleee

6

Еще более простое решение 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

Это удобно, но для ясности: это переводит Unix -> Windows / DOS, что противоположно тому, о чем просил OP.
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, если у вас установлен homebrew [ http://brew.sh/][1]

brew install dos2unix

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

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


Этот ответ на самом деле не вопрос оригинального постера.
hlin117

2
Пользователи OS X не должны использовать -c mac, то есть для преобразования pre-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 переводы строк (^ 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

В качестве расширения решения Jonathan Leffler для Unix to 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 $ //' для OSX, а также несколько других методов ( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- Окончание дос-линии или http://hintsforums.macworld.com/archive/index.php/t-125.html ). Ничего не сработало, файл остался без изменений (например, Ctrl-v Enter был необходим для воспроизведения ^ M). В конце концов я использовал TextWrangler. Это не строго командная строка, но она работает и не жалуется.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.