Jak przekonwertować DOS / Windows newline (CRLF) na Unix newline (LF) w skrypcie Bash?

336

Jak mogę programowo (tj. Nie używać vi) konwertować nowe wiersze DOS / Windows na Unix?

dos2unixI unix2dospolecenia nie są dostępne w niektórych systemach. Jak mogę je emulować za pomocą poleceń takich jak sed/ awk/ tr?

Koran Molovik
źródło
9
Ogólnie rzecz biorąc, po prostu zainstaluj dos2unixza pomocą menedżera pakietów, to naprawdę jest znacznie prostsze i istnieje na większości platform.
Brad Koch
1
Zgoda! @BradKoch Proste jak „brew install dos2unix” na Mac OSX
SmileIT

Odpowiedzi:

322

Możesz użyć trdo konwersji z DOS-a na Uniksa; można to jednak zrobić bezpiecznie tylko wtedy, gdy CR pojawia się w pliku tylko jako pierwszy bajt pary bajtów CRLF. Zazwyczaj tak jest. Następnie używasz:

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

Zauważ, że nazwa DOS-filejest inna niż nazwa UNIX-file; jeśli spróbujesz użyć tej samej nazwy dwa razy, skończysz bez danych w pliku.

Nie możesz tego zrobić na odwrót (ze standardowym „tr”).

Jeśli wiesz, jak wprowadzić znak powrotu karetki do skryptu ( control-V, control-Maby wpisać control-M), to:

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

gdzie „^ M” jest znakiem kontrolnym M. Możesz także użyć mechanizmu bash cytowania ANSI-C , aby określić zwrot karetki:

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

Jeśli jednak będziesz musiał to robić bardzo często (mniej więcej raz, z grubsza mówiąc), rozsądniej jest zainstalować programy do konwersji (np. dos2unixI unix2dos, a może dtoui utod) i używać ich.

Jeśli chcesz przetwarzać całe katalogi i podkatalogi, możesz użyć zip:

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

Spowoduje to utworzenie archiwum zip ze zmienionymi zakończeniami linii z CRLF na CR. unzipnastępnie umieści przekonwertowane pliki z powrotem na swoim miejscu (i poprosi o plik po pliku - możesz odpowiedzieć: Tak-dla-wszystkich). Podziękowania dla @vmsnomad za zwrócenie na to uwagi.

Jonathan Leffler
źródło
9
użycie tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-filepowoduje po prostu pusty plik. Plik wyjściowy musi niestety być innym plikiem.
Buttle Butkus
3
@ButtleButkus: Cóż, tak; dlatego użyłem dwóch różnych nazw. Jeśli zapełnisz plik wejściowy, zanim program przeczyta wszystko, podobnie jak w przypadku dwukrotnego użycia tej samej nazwy, otrzymujesz pusty plik. To jest jednolite zachowanie w systemach uniksopodobnych. Wymaga specjalnego kodu do bezpiecznego nadpisywania pliku wejściowego. Postępuj zgodnie z instrukcjami, a wszystko będzie dobrze.
Jonathan Leffler,
Wydaje mi się, że pamiętam jakąś funkcję wyszukiwania w zamianie w pliku.
Buttle Butkus
4
Są miejsca; musisz wiedzieć, gdzie je znaleźć. W granicach limitów działa sedopcja GNU -i(na miejscu); limitami są połączone pliki i dowiązania symboliczne. sortPolecenie ma „zawsze” (od 1979 roku, jeśli nie wcześniej) poparła -oopcję, która potrafi wymienić jeden z plików wejściowych. Jest to jednak częściowo spowodowane tym, że sortmusi odczytać wszystkie dane wejściowe, zanim będzie mógł zapisać dowolne dane wyjściowe. Inne programy sporadycznie obsługują zastępowanie jednego ze swoich plików wejściowych. Możesz znaleźć program ogólnego zastosowania (skrypt), aby uniknąć problemów w 'The Programming Environment UNIX' autorstwa Kernighan & Pike.
Jonathan Leffler,
3
Trzecia opcja zadziałała dla mnie, dzięki. Użyłem opcji -i: sed -i $'s/\r$//' filename- do edycji w miejscu. Pracuję na maszynie, która nie ma dostępu do Internetu, więc problem stanowi instalacja oprogramowania.
Warren Dew
64
tr -d "\r" < file

spójrz tutaj na przykłady, używając 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

Użyj sed -ido konwersji w miejscu, np sed -i 's/..../' file.

ghostdog74
źródło
10
Użyłem wariantu, ponieważ mój plik zawierał tylko \r:tr "\r" "\n" < infile > outfile
Matt Todd,
1
@MattTodd czy możesz to opublikować jako odpowiedź? -djest opisywany częściej i nie pomoże w „tylko \r” sytuacji.
n611x007
5
Należy zauważyć, że proponowane \rdo \nmapowania daje efekt podwójnych odstępów plików; każda pojedyncza linia CRLF kończąca się na DOS staje się \n\nUnix.
Jonathan Leffler
Czy mogę to zrobić rekurencyjnie?
Aaron Franke
36

Robienie tego z POSIX jest trudne:

  • POSIX Sed nie obsługuje \rlub \15. Nawet jeśli tak, opcja na miejscu -inie jest POSIX

  • POSIX awk robi wsparcie \ri \15, jednak -i inplaceopcja ta nie jest POSIX

  • d2u i dos2unix nie są narzędziami POSIX , ale ex jest

  • POSIX ex nie obsługuje \r, \15, \nlub\12

Aby usunąć zwroty karetki:

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

Aby dodać zwrot karetki:

ex -bsc '%!awk "{sub(/$/,\"\r\")}1"' -cx file
Steven Penny
źródło
2
Wygląda na to, że obsługuje POSIX . tr\r Więc możesz również użyć printf '%s\n' '%!tr -d "\r"' x | ex file(choć przyznane, to usunięte, \rnawet jeśli nie bezpośrednio poprzedzające \n). Ponadto -bopcja exnie jest określona przez POSIX.
Wildcard
1
Robienie tego w POSIX jest łatwe. Osadź literał CR w skrypcie, wpisując go (to control-M).
Joshua
28

Możesz używać vima programowo z opcją -c {polecenie}:

Dos dla Uniksa:

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

Uniks do dos:

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

„set ff = unix / dos” oznacza zmianę formatu pliku (ff) pliku na format końca wiersza Unix / DOS

„: wq” oznacza zapisanie pliku na dysk i zamknięcie edytora (pozwalając na użycie polecenia w pętli)

Johan Zicola
źródło
3
To wydawało się być najbardziej eleganckim rozwiązaniem, ale brak wyjaśnienia, co oznacza wq, jest niefortunne.
Jorrick Sleijster
5
Każdy, kto korzysta, vibędzie wiedział, co :wqznaczy. Dla tych, którzy nie mają 3 znaków, 1) otwórz obszar poleceń vi, 2) napisz i 3) wyjdź.
David Newcomb
Nie miałem pojęcia, że ​​możesz interaktywnie dodawać polecenia do vima z CLI
Robert Dundon
możesz użyć „: x” zamiast „: wq”
JosephConrad
25

Korzystając z AWK możesz:

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

Używając Perla możesz:

perl -pe 's/\r$//' < dos.txt > unix.txt
kodaddict
źródło
2
Ładne, przenośne awk rozwiązanie.
mklement0
23

Aby przekonwertować plik na miejscu, użyj

dos2unix <filename>

Aby wyprowadzić przekonwertowany tekst na inny plik, użyj

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

Możesz zainstalować go na Ubuntu lub Debianie za pomocą

sudo apt install dos2unix

lub w systemie macOS za pomocą homebrew

brew install dos2unix
Boris
źródło
1
Wiem, że pytanie dotyczy alternatyw dla dos2unix, ale jest to pierwszy wynik Google.
Boris
18

Ten problem można rozwiązać za pomocą standardowych narzędzi, ale istnieje wystarczająco wiele pułapek dla nieostrożnych, że zalecam zainstalowanie flippolecenia, które zostało napisane ponad 20 lat temu przez autora Rahula Dhesi zoo. Doskonale radzi sobie z konwersją formatów plików, na przykład unikając przypadkowego zniszczenia plików binarnych, co jest nieco zbyt łatwe, jeśli po prostu ścigasz się zmieniając każdy CRLF, który widzisz ...

Norman Ramsey
źródło
Jakiś sposób to zrobić w sposób strumieniowy, bez modyfikowania oryginalnego pliku?
sierpnia
@ augurar możesz sprawdzić "podobne pakiety" packages.debian.org/wheezy/flip
n611x007
Miałem doświadczenie z łamaniem połowy mojego systemu operacyjnego poprzez uruchomienie texxto z niewłaściwą flagą. Zachowaj ostrożność, zwłaszcza jeśli chcesz to zrobić dla całych folderów.
A_P
14

Dotychczasowe rozwiązania dotyczą tylko części problemu, przekształcając CRLF DOS / Windows w LF Unixa; brakuje im tylko tego, że DOS używa CRLF jako separatora linii , podczas gdy Unix używa LF jako terminatora linii . Różnica polega na tym, że plik DOS (zwykle) nie będzie miał nic po ostatniej linii pliku, podczas gdy Unix będzie. Aby poprawnie wykonać konwersję, musisz dodać ten końcowy LF (chyba że plik ma zerową długość, tj. Nie ma w nim żadnych linii). Moje ulubione zaklęcie do tego (z nieco dodaną logiką do obsługi plików rozdzielonych CR w stylu Mac, a nie molestujących plików, które są już w formacie unixowym) to trochę perl:

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

Zauważ, że wysyła to Unixified wersję pliku na standardowe wyjście. Jeśli chcesz zastąpić plik wersją Unixified, dodaj -iflagę perla .

Gordon Davisson
źródło
@LudovicZenohateLagouardette Czy był to zwykły plik tekstowy (tj. Plik CSV lub tekst z rozróżnieniem tabulatorów), czy coś innego? Jeśli był w jakimś formacie bazodanowym, manipulowanie nim tak, jakby to był tekst, może uszkodzić jego wewnętrzną strukturę.
Gordon Davisson
Zwykły tekst csv, ale myślę, że szyfrowanie było dziwne. Myślę, że to popsuło z tego powodu. Jednak nie martw się. Zawsze zbieram kopie zapasowe, a to nie był nawet prawdziwy zestaw danych, tylko 1 GB. Rzeczywista to 26 GB.
Ludovic Zenohate Lagouardette
14

Jeśli nie masz dostępu do dos2unix , ale możesz przeczytać tę stronę, możesz skopiować / wkleić dos2unix.py z tego miejsca.

#!/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))

Przeniesiony z superużytkownika .

anatoly techtonik
źródło
1
Zastosowanie jest mylące. Rzeczywiste domyślnie dos2unixkonwertuje wszystkie pliki wejściowe. Twoje użycie oznacza -nparametr. A prawdziwy dos2unixto filtr, który odczytuje ze standardowego wejścia, zapisuje na standardowe wyjście, jeśli pliki nie są podane.
jfs,
8

Łatwe kopiowanie z PCRE;

Jako skrypt lub zamień na $@swoje pliki.

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

Spowoduje to zastąpienie plików na miejscu!

Zalecam robienie tego tylko z kopią zapasową (kontrola wersji lub w inny sposób)

ThorSummoner
źródło
Dziękuję Ci! To działa, chociaż piszę nazwę pliku i nie --. Wybrałem to rozwiązanie, ponieważ jest dla mnie łatwe do zrozumienia i dostosowania. Do Twojej dyspozycji są przełączniki: -pzałóż pętlę „while input”, -iedytuj plik wejściowy na miejscu, -ewykonaj następujące polecenie
Rolf
Ściśle mówiąc, PCRE to reimplementacja silnika wyrażeń regularnych Perla, a nie silnika wyrażeń regularnych Perla. Obaj mają taką możliwość, chociaż istnieją także różnice, pomimo implikacji w nazwie.
tripleee
6

Jeszcze prostsze rozwiązanie awk bez programu:

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

Technicznie „1” to twój program, b / c awk wymaga jednego, gdy podano opcję.

AKTUALIZACJA : Po ponownym odwiedzeniu tej strony od dłuższego czasu zdałem sobie sprawę, że nikt jeszcze nie opublikował wewnętrznego rozwiązania, więc oto jedno:

while IFS= read -r line;
do printf '%s\n' "${line%$'\r'}";
done < dos.txt > unix.txt
nawK
źródło
Jest to przydatne, ale dla jasności: tłumaczy to Unix -> Windows / DOS, co jest odwrotnym kierunkiem do tego, o co poprosił OP.
mklement0
5
Zostało to zrobione celowo, pozostawione jako ćwiczenie dla autora. eyerolls awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK
Świetne (i wyrazy uznania dla was za pedagogiczną finezję).
mklement0
1
„b / c awk wymaga jednego, gdy podano opcję.” - awk zawsze wymaga programu, niezależnie od tego, czy podano opcje, czy nie.
mklement0
1
Rozwiązanie czysto bashowe jest interesujące, ale znacznie wolniejsze niż jego odpowiednik awklub sedrozwiązanie. Musisz także użyć, while IFS= read -r lineaby wiernie zachować linie wejściowe, w przeciwnym razie początkowe i końcowe białe znaki zostaną przycięte (alternatywnie, nie używaj nazwy zmiennej w readpoleceniu i pracuj z $REPLY).
mklement0
5

Musiałem tylko zastanowić się nad tym samym pytaniem (po stronie Windows, ale równie dobrze dotyczy Linuksa). Zaskakująco nikt nie wspomniał o bardzo zautomatyzowanym sposobie wykonywania konwersji CRLF <-> LF dla plików tekstowych przy użyciu starej dobrej zip -llopcji (Info-ZIP):

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

UWAGA: spowoduje to utworzenie pliku zip z zachowaniem oryginalnych nazw plików, ale konwersją zakończeń linii do LF. Następnieunzip rozpakowałbym pliki jako skompresowane, czyli z ich oryginalnymi nazwami (ale z końcówkami LF), prosząc w ten sposób o zastąpienie lokalnych oryginalnych plików, jeśli takie istnieją.

Odpowiedni fragment z zip --help :

zip --help
...
-l   convert LF to CR LF (-ll CR LF to LF)
Vmsnomad
źródło
Według mnie najlepsza odpowiedź, ponieważ może przetwarzać całe katalogi i podkatalogi. Cieszę się, że skopałem tak daleko.
caram
5

co ciekawe w mojej git-bash na Windowsie sed ""zrobiłem już lewę:

$ 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

Domyślam się, że sed ignoruje je podczas odczytu linii z wejścia i zawsze zapisuje na wyjściu zakończenia linii unixowych.

użytkownik829755
źródło
4

To zadziałało dla mnie

tr "\r" "\n" < sampledata.csv > sampledata2.csv 
Santosh
źródło
9
Będzie to przekształcić każdą pojedynczą DOS-przełamane na dwie UNIX nowej linii.
Melebius
2

W przypadku systemu Mac OSX, jeśli masz zainstalowany program Homebrew [ http://brew.sh/][1]

brew install dos2unix

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

Upewnij się, że wykonałeś kopie plików, ponieważ to polecenie zmodyfikuje pliki na miejscu. Opcja -c mac sprawia, że ​​przełącznik jest zgodny z systemem osx.

Ashley Raiteri
źródło
Ta odpowiedź tak naprawdę nie jest pytaniem oryginalnego plakatu.
hlin117
2
Użytkownicy systemu OS X nie powinni używać -c mac, czyli do konwertowania CRtylko nowych linii przed systemem OS X. Chcesz używać tego trybu tylko do plików do i z Mac OS 9 lub wcześniejszych.
askewchan
2

TIMTOWTDI!

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

Na podstawie @GordonDavisson

Należy wziąć pod uwagę możliwość [noeol]...

lzc
źródło
2

Możesz użyć awk. Ustaw separator rekordów ( RS) na wyrażenie regularne, które pasuje do wszystkich możliwych znaków nowej linii lub znaków. I ustaw separator rekordów wyjściowych ( ORS) na znak nowej linii w stylu uniksowym.

awk 'BEGIN{RS="\r|\n|\r\n|\n\r";ORS="\n"}{print}' windows_or_macos.txt > unix.txt
kazmer
źródło
To dla mnie zadziałało (MacOS, git diffpokazuje ^ M, edytowany w vimie)
Dorian
2

W Linuksie łatwo jest przekonwertować ^ M (ctrl-M) na * nix nowe linie (^ J) za pomocą sed.

Będzie to coś takiego w interfejsie CLI, tak naprawdę nastąpi przerwanie linii w tekście. Jednak \ przekazuje to ^ J do sed:

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

Możesz to zrobić, używając ^ V (ctrl-V), ^ M (ctrl-M) i \ (ukośnik odwrotny) podczas pisania:

sed 's/^V^M/\^V^J/g' < ffmpeg.log > new.log
strumień
źródło
2
sed --expression='s/\r\n/\n/g'

Ponieważ w pytaniu mowa jest o sed, jest to najprostszy sposób użycia sed, aby to osiągnąć. To, co mówi to wyrażenie, zastępuje wszystkie znaki powrotu karetki i znak wiersza tylko wierszami. Właśnie tego potrzebujesz, kiedy przechodzisz z Windowsa na Unixa. Sprawdziłem, czy to działa.

Jan Paweł
źródło
Hej, John Paul - ta odpowiedź została oznaczona do usunięcia, więc pojawiła się w kolejce do mnie. Zasadniczo, gdy masz takie pytanie, które ma 8 lat i 22 odpowiedzi, powinieneś wyjaśnić, w jaki sposób twoja odpowiedź jest przydatna w sposób, w jaki inne istniejące odpowiedzi nie są.
zzxyz
0

Jako rozszerzenie rozwiązania Jonathan Leffler Unix na DOS, aby bezpiecznie przekonwertować na DOS, gdy nie masz pewności co do bieżących zakończeń linii pliku:

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

To sprawdza, czy linia nie kończy się na CRLF przed konwersją do CRLF.

Głuptak
źródło
0

Stworzyłem skrypt w oparciu o zaakceptowaną odpowiedź, więc możesz go przekonwertować bezpośrednio, bez potrzeby dodatkowego pliku na końcu, a następnie usunąć i zmienić jego nazwę.

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

tylko upewnij się, że jeśli masz plik taki jak „plik1.txt”, że „plik1.txt2” jeszcze nie istnieje lub zostanie zastąpiony, używam go jako tymczasowego miejsca do przechowywania pliku.

OZZIE
źródło
0

W wersji bash 4.2 i nowszych możesz użyć czegoś takiego do usunięcia końcowego CR, który wykorzystuje tylko wbudowane bash:

if [[ "${str: -1}" == $'\r' ]]; then
    str="${str:: -1}"
fi
Glevand
źródło