Làm cách nào để chuyển đổi dòng mới của DOS / Windows (CRLF) sang dòng mới Unix (LF) trong tập lệnh Bash?


336

Làm cách nào tôi có thể lập trình (nghĩa là không sử dụng vi) chuyển đổi các dòng mới của DOS / Windows sang Unix?

Các lệnh dos2unixunix2doskhông có sẵn trên các hệ thống nhất định. Làm thế nào tôi có thể mô phỏng chúng bằng các lệnh như sed/ awk/ tr?


9
Nói chung, chỉ cần cài đặt dos2unixbằng trình quản lý gói của bạn, nó thực sự đơn giản hơn nhiều và tồn tại trên hầu hết các nền tảng.
Brad Koch

1
Đã đồng ý! @BradKoch Đơn giản như 'brew install dos2unix' trên Mac OSX
SmileIT

Câu trả lời:


322

Bạn có thể sử dụng trđể chuyển đổi từ DOS sang Unix; tuy nhiên, bạn chỉ có thể thực hiện việc này một cách an toàn nếu CR chỉ xuất hiện trong tệp của bạn dưới dạng byte đầu tiên của cặp byte CRLF. Đây thường là trường hợp. Sau đó, bạn sử dụng:

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

Lưu ý rằng tên DOS-filekhác với tên UNIX-file; nếu bạn cố gắng sử dụng cùng một tên hai lần, bạn sẽ không có dữ liệu trong tệp.

Bạn không thể làm theo cách khác (với tiêu chuẩn 'tr').

Nếu bạn biết cách nhập trở lại vận chuyển vào tập lệnh ( control-V, control-Mđể nhập control-M), thì:

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

trong đó '^ M' là ký tự control-M. Bạn cũng có thể sử dụng cơ chế bash trích dẫn ANSI-C để chỉ định trả về vận chuyển:

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

Tuy nhiên, nếu bạn đang đi để có để làm điều này rất thường xuyên (nhiều hơn một lần, khoảng nói), nó còn lâu mới hợp lý hơn để cài đặt các chương trình chuyển đổi (ví dụ dos2unixunix2dos, hoặc có lẽ dtouutod) và sử dụng chúng.

Nếu bạn cần xử lý toàn bộ thư mục và thư mục con, bạn có thể sử dụng zip:

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

Điều này sẽ tạo ra một kho lưu trữ zip với các kết thúc dòng được thay đổi từ CRLF thành CR. unzipsau đó sẽ đặt các tệp đã chuyển đổi trở lại vị trí (và yêu cầu bạn gửi tệp theo tệp - bạn có thể trả lời: Có-tất cả). Tín dụng cho @vmsnomad để chỉ ra điều này.


9
bằng cách sử dụng tr -d '\015' <DOS-file >UNIX-filewhere DOS-file== UNIX-filechỉ dẫn đến một tệp trống. Thật không may, tập tin đầu ra phải là một tập tin khác.
Butussy Butkus

3
@BriptButkus: Vâng, vâng; đó là lý do tại sao tôi sử dụng hai tên khác nhau Nếu bạn hạ gục tệp đầu vào trước khi chương trình đọc hết, giống như khi bạn sử dụng cùng một tên hai lần, bạn sẽ kết thúc với một tệp trống. Đó là hành vi thống nhất trên các hệ thống giống Unix. Nó yêu cầu mã đặc biệt để xử lý ghi đè một tệp đầu vào một cách an toàn. Thực hiện theo các hướng dẫn và bạn sẽ ổn thôi.
Jonathan Leffler

Tôi dường như nhớ chức năng thay thế tìm kiếm trong tập tin nào đó.
Butussy Butkus

4
Có nơi; bạn phải biết nơi để tìm thấy chúng. Trong giới hạn, sedtùy chọn GNU -i(cho tại chỗ) hoạt động; các giới hạn là các tập tin liên kết và liên kết tượng trưng. Các sortlệnh có 'always' (kể từ năm 1979, nếu không muốn nói trước đó) hỗ trợ các -otùy chọn mà có thể liệt kê một trong những tập tin đầu vào. Tuy nhiên, đó là một phần vì sortphải đọc tất cả đầu vào của nó trước khi nó có thể ghi bất kỳ đầu ra nào của nó. Các chương trình khác thỉnh thoảng hỗ trợ ghi đè một trong các tệp đầu vào của chúng. Bạn có thể tìm thấy một chương trình mục đích chung (tập lệnh) để tránh các vấn đề trong 'Môi trường lập trình UNIX' của Kernighan & Pike.
Jonathan Leffler

3
Lựa chọn thứ ba làm việc cho tôi, cảm ơn. Tôi đã sử dụng tùy chọn -i: sed -i $'s/\r$//' filename- để chỉnh sửa tại chỗ. Tôi đang làm việc trên một máy không có quyền truy cập internet, vì vậy cài đặt phần mềm là một vấn đề.
Warren Dew

64
tr -d "\r" < file

hãy xem ở đây để biết ví dụ bằng cách sử dụng 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

Sử dụng sed -iđể chuyển đổi tại chỗ, ví dụ sed -i 's/..../' file.


10
Tôi đã sử dụng một biến thể vì tệp của tôi chỉ có \r:tr "\r" "\n" < infile > outfile
Matt Todd

1
@MattTodd bạn có thể đăng bài này như một câu trả lời không? những -dlà đặc trưng thường xuyên hơn và sẽ không giúp đỡ trong "chỉ \r" tình hình.
n611x007

5
Lưu ý rằng các đề xuất \rđể \nlập bản đồ có tác dụng kép khoảng cách các tập tin; mỗi dòng CRLF duy nhất kết thúc bằng DOS sẽ trở thành \n\nUnix.
Jonathan Leffler

Tôi có thể làm điều này đệ quy không?
Aaron Franke

36

Làm điều này với POSIX rất khó:

  • POSIX Sed không hỗ trợ \rhoặc \15. Ngay cả khi nó đã làm, tùy chọn tại chỗ -ikhông phải là POSIX

  • POSIX Awk không hỗ trợ \r\15tuy nhiên -i inplacetùy chọn không phải là POSIX

  • d2udos2unix không phải là tiện ích POSIX , nhưng ex

  • POSIX cũ không hỗ trợ \r, \15, \nhoặc\12

Để loại bỏ trả lại vận chuyển:

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

Để thêm lợi nhuận vận chuyển:

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

2
Có vẻ như POSIX trhỗ trợ \r. Vì vậy, bạn cũng có thể sử dụng printf '%s\n' '%!tr -d "\r"' x | ex file(mặc dù được cấp, điều này được loại bỏ \rngay cả khi không ngay trước đó \n). Ngoài ra, -btùy chọn exkhông được chỉ định bởi POSIX.
tự đại diện

1
Làm điều này trong POSIX rất dễ dàng. Nhúng chữ CR trong tập lệnh bằng cách nhập nó (đó là control-M).
Joshua

28

Bạn có thể sử dụng vim theo chương trình với tùy chọn -c {lệnh}:

Dos sang Unix:

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

Unix để dos:

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

"set ff = unix / dos" có nghĩa là thay đổi định dạng tệp (ff) của tệp thành định dạng cuối dòng Unix / DOS

": wq" có nghĩa là ghi tệp vào đĩa và thoát khỏi trình chỉnh sửa (cho phép sử dụng lệnh trong một vòng lặp)


3
Đây có vẻ như là giải pháp tao nhã nhất nhưng việc thiếu giải thích về ý nghĩa của wq là không may.
Jorrick Sleijster

4
Bất cứ ai sử dụng visẽ biết những gì :wqcó nghĩa. Đối với những người không có 3 ký tự có nghĩa là 1) mở vùng lệnh vi, 2) viết và 3) thoát.
David Newcomb

Tôi không biết bạn có thể tương tác thêm các lệnh vào vim từ CLI
Robert Dundon

bạn có thể sử dụng ": x" thay vì ": wq"
JosephConrad

25

Sử dụng AWK bạn có thể làm:

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

Sử dụng Perl bạn có thể làm:

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

2
Một giải pháp di động tốt đẹp awk.
mkuity0

23

Để chuyển đổi một tập tin tại chỗ sử dụng

dos2unix <filename>

Để xuất văn bản đã chuyển đổi sang một tệp khác, sử dụng

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

Bạn có thể cài đặt nó trên Ubuntu hoặc Debian với

sudo apt install dos2unix

hoặc trên macOS sử dụng homebrew

brew install dos2unix

1
Tôi biết câu hỏi yêu cầu thay thế cho dos2unix nhưng đó là kết quả đầu tiên của google.
Boris

18

Vấn đề này có thể được giải quyết bằng các công cụ tiêu chuẩn, nhưng có rất nhiều bẫy cho sự không sẵn sàng mà tôi khuyên bạn nên cài đặt fliplệnh, được viết bởi hơn 20 năm trước bởi Rahul Dhesi, tác giả của zoo. Nó thực hiện một công việc tuyệt vời để chuyển đổi các định dạng tệp trong khi, ví dụ, để tránh việc phá hủy các tệp nhị phân một cách vô tình, điều này quá dễ dàng nếu bạn chỉ chạy đua thay đổi mọi CRLF mà bạn thấy ...


Bất kỳ cách nào để làm điều này theo cách phát trực tuyến, mà không sửa đổi tệp gốc?
augurar

@augurar bạn có thể kiểm tra "các gói tương tự" packages.debian.org/wheezy/flip
n611x007

Tôi đã có kinh nghiệm phá vỡ một nửa hệ điều hành của mình chỉ bằng cách chạy texxto với một cờ sai. Hãy cẩn thận đặc biệt nếu bạn muốn làm điều đó trên toàn bộ thư mục.
A_P

14

Các giải pháp được đăng cho đến nay chỉ giải quyết được một phần của vấn đề, chuyển đổi CRLF của DOS / Windows thành LF của Unix; phần họ thiếu là DOS sử dụng CRLF làm công cụ phân tách dòng , trong khi Unix sử dụng LF làm công cụ kết thúc dòng . Sự khác biệt là một tệp DOS (thường) sẽ không có gì sau dòng cuối cùng trong tệp, trong khi Unix sẽ. Để thực hiện chuyển đổi đúng cách, bạn cần thêm phần cuối cùng đó (trừ khi tệp có độ dài bằng không, tức là không có dòng nào trong đó). Câu thần chú yêu thích của tôi cho điều này (với một chút logic được thêm vào để xử lý các tệp được phân tách CR theo kiểu Mac và không phải là các tệp molly đã ở định dạng unix) là một chút sai lầm:

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

Lưu ý rằng điều này sẽ gửi phiên bản Unixified của tệp tới thiết bị xuất chuẩn. Nếu bạn muốn thay thế tệp bằng phiên bản Unixified, hãy thêm -icờ của perl .


@LudovicZenohateLagouardette Đó có phải là một tệp văn bản đơn giản (tức là văn bản csv hoặc tab bị hủy), hay cái gì khác không? Nếu nó ở định dạng cơ sở dữ liệu nào đó, thao tác với nó như thể nó là văn bản rất có khả năng làm hỏng cấu trúc bên trong của nó.
Gordon Davisson

Một văn bản csv đơn giản, nhưng tôi nghĩ rằng điều đó là lạ. Tôi nghĩ rằng nó đã gây rối vì điều đó. Tuy nhiên đừng lo lắng. Tôi luôn luôn thu thập các bản sao lưu, đây thậm chí không phải là tập dữ liệu thực, chỉ là 1gb. Thực tế là 26gb.
Ludovic Zenohate Lagouardette

14

Nếu bạn không có quyền truy cập vào dos2unix , nhưng có thể đọc trang này, thì bạn có thể sao chép / dán dos2unix.py từ đây.

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

Đăng chéo từ siêu người dùng .


1
Việc sử dụng là sai lệch. Thực tế dos2unixchuyển đổi tất cả các tập tin đầu vào theo mặc định. Việc sử dụng của bạn ngụ ý -ntham số. Và thực tế dos2unixlà một bộ lọc đọc từ stdin, ghi vào thiết bị xuất chuẩn nếu các tệp không được cung cấp.
jfs

8

Siêu lừa đảo dễ dàng với PCRE;

Là một tập lệnh, hoặc thay thế $@bằng các tập tin của bạn.

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

Điều này sẽ ghi đè lên các tập tin của bạn tại chỗ!

Tôi khuyên bạn chỉ nên làm điều này với một bản sao lưu (kiểm soát phiên bản hoặc cách khác)


Cảm ơn bạn! Điều này hoạt động, mặc dù tôi đang viết tên tệp và không --. Tôi đã chọn giải pháp này vì nó dễ hiểu và thích nghi với tôi. FYI, đây là những gì các thiết bị chuyển mạch thực hiện: -pgiả sử vòng lặp "while input", -ichỉnh sửa tệp đầu vào tại chỗ, -ethực hiện lệnh sau
Rolf

Nói một cách chính xác, PCRE là sự tái hiện của công cụ regex của Perl, chứ không phải công cụ regex từ Perl. Cả hai đều có khả năng này, mặc dù cũng có những khác biệt, bất chấp sự áp đặt trong tên.
tripleee

6

Một giải pháp awk thậm chí còn đơn giản hơn với chương trình w / oa:

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

Về mặt kỹ thuật '1' là chương trình của bạn, b / c awk yêu cầu một tùy chọn khi được cung cấp.

CẬP NHẬT : Sau khi xem lại trang này lần đầu tiên sau một thời gian dài tôi nhận ra rằng chưa có ai đăng một giải pháp nội bộ, vì vậy đây là một:

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

Điều đó thật tiện dụng, nhưng thật rõ ràng: điều này dịch Unix -> Windows / DOS, đó là hướng ngược lại với những gì OP yêu cầu.
mkuity0

5
Nó được thực hiện trên mục đích, để lại như một bài tập cho tác giả. mỏi mắt awk -v RS='\r\n' '1' dos.txt > unix.txt
nawK

Tuyệt vời (và danh tiếng cho bạn cho sự tinh tế sư phạm).
mkuity0

1
"b / c awk yêu cầu một khi có tùy chọn." - awk luôn yêu cầu một chương trình, cho dù các tùy chọn được chỉ định hay không.
mkuity0

1
Giải pháp bash thuần là thú vị, nhưng chậm hơn nhiều so với giải pháp tương đương awkhoặc sed. Ngoài ra, bạn phải sử dụng while IFS= read -r lineđể bảo toàn một cách trung thực các dòng đầu vào, nếu không thì khoảng trắng đầu và cuối được cắt bớt (thay vào đó, không sử dụng tên biến trong readlệnh và làm việc với $REPLY).
mkuity0

5

thú vị trong git-bash của tôi trên windows sed ""đã lừa được rồi:

$ 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

Tôi đoán là sed bỏ qua chúng khi đọc các dòng từ đầu vào và luôn ghi các kết thúc dòng unix trên đầu ra.


4

Điều này làm việc cho tôi

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

9
Điều này sẽ chuyển đổi tất cả các đơn DOS xuống dòng vào hai UNIX-dòng mới.
Melebius

4

Chỉ cần suy nghĩ về câu hỏi tương tự (về phía Windows, nhưng cũng có thể áp dụng tương tự với linux.) Đáng ngạc nhiên là không ai đề cập đến một cách rất tự động để thực hiện chuyển đổi CRLF <-> LF cho các tệp văn bản bằng cách sử dụng zip -lltùy chọn cũ (Info-ZIP):

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

LƯU Ý: điều này sẽ tạo ra một tệp zip lưu giữ tên tệp gốc nhưng chuyển đổi các kết thúc dòng thành LF. Sau đó, unzipsẽ trích xuất các tệp dưới dạng zip, nghĩa là với tên gốc của chúng (nhưng với các kết thúc của LF), do đó nhắc nhở ghi đè lên các tệp gốc cục bộ nếu có.

Đoạn trích có liên quan từ zip --help :

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

Câu trả lời tốt nhất, theo tôi, vì nó có thể xử lý toàn bộ thư mục và thư mục con. Tôi rất vui vì tôi đã đào nó xuống rất xa.
caram

2

Đối với Mac osx nếu bạn đã cài đặt homebrew [ http://brew.sh/[[1]

brew install dos2unix

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

Hãy chắc chắn rằng bạn đã tạo các bản sao của các tệp, vì lệnh này sẽ sửa đổi các tệp tại chỗ. Tùy chọn -c mac làm cho công tắc tương thích với osx.


Câu trả lời này thực sự không phải là câu hỏi của người đăng ban đầu.
hlin117

2
Người dùng OS X không nên sử dụng -c mac, để chuyển đổi các CRdòng mới chỉ dành cho OS X. Bạn muốn sử dụng chế độ đó chỉ cho các tệp đến và từ Mac OS 9 trở về trước.
askewchan

2

TIMTOWTDI!

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

Dựa trên @GordonDavisson

Người ta phải xem xét khả năng [noeol]...


2

Bạn có thể sử dụng awk. Đặt dấu phân cách bản ghi ( RS) thành biểu thức chính quy phù hợp với tất cả các ký tự hoặc ký tự dòng mới có thể. Và đặt dấu tách bản ghi đầu ra ( ORS) thành ký tự dòng mới kiểu unix.

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

Đó là cái đã làm việc cho tôi (MacOS, git diffhiển thị ^ M, được chỉnh sửa trong vim)
Dorian

2

Trên Linux, thật dễ dàng để chuyển đổi ^ M (ctrl-M) thành * nix dòng mới (^ J) bằng sed.

Nó sẽ giống như thế này trên CLI, thực sự sẽ có một ngắt dòng trong văn bản. Tuy nhiên, \ vượt qua ^ J cùng với sed:

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

Bạn có được điều này bằng cách sử dụng ^ V (ctrl-V), ^ M (ctrl-M) và \ (dấu gạch chéo ngược) khi bạn nhập:

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

đây là những gì làm việc cho tôi, cảm ơn!
Dan Mantyla

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

Vì câu hỏi đề cập đến sed, đây là cách đơn giản nhất để sử dụng sed để đạt được điều này. Những gì biểu thức nói là thay thế tất cả vận chuyển trở lại và nguồn cấp dữ liệu chỉ bằng nguồn cấp dữ liệu. Đó là những gì bạn cần khi bạn chuyển từ Windows sang Unix. Tôi đã xác minh nó hoạt động.


Này John Paul - câu trả lời này đã được gắn cờ để xóa vì vậy đã đưa ra một hàng đánh giá cho tôi. Nói chung, khi bạn có một câu hỏi như thế này 8 tuổi, với 22 câu trả lời, bạn sẽ muốn giải thích câu trả lời của bạn hữu ích theo cách mà các câu trả lời hiện có khác không có.
zzxyz

0

Là một phần mở rộng cho giải pháp Unix sang DOS của Jonathan Leffler, để chuyển đổi an toàn sang DOS khi bạn không chắc chắn về kết thúc dòng hiện tại của tệp:

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

Điều này kiểm tra rằng dòng chưa kết thúc bằng CRLF trước khi chuyển đổi sang CRLF.


0

Tôi đã tạo một kịch bản dựa trên câu trả lời được chấp nhận để bạn có thể chuyển đổi trực tiếp mà không cần thêm tệp cuối cùng và xóa và đổi tên sau đó.

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

chỉ cần đảm bảo rằng nếu bạn có một tệp như "file1.txt" thì "file1.txt2" không tồn tại hoặc nó sẽ bị ghi đè, tôi sử dụng nó làm nơi tạm thời để lưu trữ tệp.


0

Với bash 4.2 và mới hơn, bạn có thể sử dụng một cái gì đó như thế này để loại bỏ CR, chỉ sử dụng bash dựng sẵn:

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

-3

Tôi đã thử sed 's / ^ M $ //' file.txt trên OSX cũng như một số phương thức khác ( http://www.thingy-ma-jig.co.uk/blog/25-11-2010/fixing- dos-line-endings hoặc http://hintsforums.macworld.com/archive/index.php/t-125.html ). Không có gì hoạt động, tập tin vẫn không thay đổi (btw Ctrl-v Enter là cần thiết để tái tạo ^ M). Cuối cùng, tôi đã sử dụng TextWrangler. Nó không nghiêm chỉnh dòng lệnh nhưng nó hoạt động và nó không phàn nàn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.