Thay thế sed đơn giản của các tab thất bại một cách bí ẩn


43

Điều này nên thực sự đơn giản, nhưng vì một số lý do, nó không hoạt động:

sed -i.bak -E 's/\t/  /' file.txt

Thay vì thay thế các ký tự tab, nó thay thế các tký tự. Tôi đã thử mọi biến thể về điều này mà tôi có thể nghĩ ra, chơi với trích dẫn, v.v. Tôi đã Googled và thấy mọi người khác sử dụng các biểu thức khá giống nhau và họ dường như làm việc với chúng.

Đây -Elà một điều OS X. Tôi nghĩ rằng sự thất bại có thể là kết quả của một số điều kỳ quặc kỳ lạ của OS X sed, vì vậy tôi cũng đã thử nó với Ruby (không có -i) và nhận được kết quả tương tự:

ruby -pe '$_.gsub!(/\t/,"  ")' < file.txt > file.new

Tôi đang sử dụng Bash 3.2.51 trên OS X và iTerm, mặc dù tôi không thể thấy bất kỳ thứ nào trong số đó có thể liên quan khủng khiếp như thế nào. Tôi chưa đặt bất kỳ biến môi trường kỳ lạ nào, mặc dù tôi có thể đăng bất kỳ biến nào mà bạn nghĩ có thể có liên quan.

Điều gì có thể sai?

CẬP NHẬT : Tôi phải thực hiện một số sai lầm khác hoặc typo khi tôi thử phiên bản Ruby, kể từ khi Gilles chỉ ra rằng nó không làm việc (và tôi đã không bao giờ có ông chỉ đạo cho tôi sai!). Tôi không chắc chuyện gì đã xảy ra, nhưng tôi khá chắc chắn đó là lỗi của tôi.


5
Có thể là bạn nên cố gắng thay thế \ttrong sedtuyên bố với CTRL-V<TAB>nơi <TAB>là phím tab và CTRL-Vlà chìa khóa điều khiển và vép lại với nhau.
unxnut

nếu ruby ​​cũng nhận được câu trả lời sai, thì đó có thể là thư viện regrec của bạn. (Tôi đã kiểm tra cả hai lệnh của bạn và cả hai thẻ thay thế bằng 2 khoảng trắng.) Vì vậy, hy vọng nếu bạn cài đặt Gnu sed, nó cũng sẽ cài đặt đúng thư viện.
ctrl-alt-delor

Câu trả lời:


64

Cú pháp \tcho một ký tự tab trong sed là không chuẩn. Lối thoát đó là một phần mở rộng GNU sed . Bạn tìm thấy rất nhiều ví dụ trực tuyến sử dụng nó vì rất nhiều người sử dụng GNU sed (đó là triển khai sed trên Linux không nhúng). Nhưng sed X của OS X , giống như sed * BSD khác, không hỗ trợ \ttab và thay vào đó được coi \tlà dấu gạch chéo ngược có nghĩa t.

Có nhiều giải pháp, như:

  • Sử dụng một ký tự tab theo nghĩa đen.

    sed -i.bak 's/  /  /' file.txt
    
  • Sử dụng trhoặc printfđể tạo ra một ký tự tab.

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • Sử dụng cú pháp chuỗi của bash cho phép thoát dấu gạch chéo ngược .

    sed -i.bak $'s/\t/  /' file.txt
    
  • Sử dụng Perl, Python hoặc Ruby. Đoạn mã Ruby mà bạn đã đăng không hoạt động.


Đối với các tập lệnh sed được chứa trong một ...sedtập lệnh (được sử dụng thông qua -ftùy chọn), các ký tự tab theo nghĩa đen dường như là khả năng duy nhất đối với tôi. Khi chỉnh sửa điều này với vim, set noexpandtabrất quan trọng.
Tobias

Cảnh báo: Chỉ sử dụng kỹ thuật "ký tự tab" đó nếu bạn muốn đồng nghiệp quay lại phía sau bạn và phá vỡ tập lệnh của bạn sau đó. Chỉ sử dụng trkỹ thuật đó nếu bạn muốn đồng nghiệp đâm vào mặt bạn khi họ đọc kịch bản của bạn.
Bruno Bronosky

Là dấu ngoặc kép thứ hai bị đặt sai trong khối mã thứ hai? Tôi đã phải chuyển nó đến nơi mà trích dẫn đơn hiện tại đang đóng.
Ellen Spertus

Cảm ơn liên kết đến cú pháp chuỗi bash ... Tôi không có ý tưởng nào (và đây là tùy chọn tốt nhất, IMHO).
levigroker

sed $'s/<regex>/\t/' file.txthoạt động để chèn, nhưng $dường như phá vỡ tập lệnh của tôi khi tôi cố gắng đưa một phần của biểu thức chính vào thay thế, nghĩa là sed $'s,\(ontology/[0-9]\+\),\t\txxx\1xxx\t\t,'cung cấp `xxxxxx` với giá trị khớp mong đợi của tôi được thay thế bằng` `. Có một tương đương với \1khi sử dụng cú pháp chuỗi của bash không? Chỉnh sửa: có nghĩa là ký tự unicode U + 231C ở giữa xxx <U + 231C> xxx.
Josh

14

Sử dụng một trích dẫn cụ thể của Bash cho phép bạn sử dụng các chuỗi như trong C, để một ký tự tab thực được truyền cho sed, không phải là một chuỗi thoát:

sed -i.bak -E $'s/\t/  /' file.txt

1
Cũng được gọi là trích dẫn "ANSI-C" nếu người khác muốn tìm kiếm thêm thông tin về nó.
wvducky

2
Có vẻ như hoạt động trên bất kỳ shell bourne nào, cũng hoạt động trên các UNIX không bash. Không hoạt động trên các biến thể csh mặc dù.
jornane

1

Như đã lưu ý, không phải tất cả các sedcài đặt đều hỗ trợ ký hiệu \tdưới dạng tab ngang.

Bạn có thể dễ dàng đạt được sự thay thế của mình bằng:

 perl -pi.old -e 's{\t+}{ }g' file.txt

Điều này thực hiện thay thế tại chỗ để bảo vệ tệp gốc của bạn là "* .old". Perl cho phép các dấu phân cách thay thế cho kiểu cổ điển /làm cho biểu thức dễ đọc hơn nhiều (nghĩa là không có hội chứng "tăm nghiêng").

Việc +nói một hoặc nhiều lần lặp lại của một ký tự tab sẽ được thay thế. Công cụ gsửa đổi cho phép thay thế toàn cầu trong suốt cuối mỗi dòng.


1
sed -i $'s/\t/  /g' file.txt 

hoạt động với tôi trên OS X và là lệnh tôi sử dụng trên linux mọi lúc.


Lưu ý rằng điều này thay thế tất cả các tab trên mỗi hàng trong khi OP dự định chỉ thay thế đầu tiên (đánh giá từ lệnh họ sử dụng).
Kusalananda

0

Bạn cũng có thể sử dụng echobên trong sed:

sed -i "s/$(echo '\t')//g"


Lưu ý rằng echo '\t'sẽ chỉ xuất ra \ttrong việc thực hiện một số shell echo.
Kusalananda

0

Nếu bạn muốn một thứ mạnh hơn sed(hỗ trợ \tvà nhiều hơn) so với trên X, hãy cài đặt GNU sed .


Vì nó cũng không hoạt động với Ruby, tôi không chắc tại sao tôi lại kết luận rằng OS X sedlà vấn đề. Bạn có lý do để tin rằng đó là vấn đề? Tôi rất vui khi cài đặt GNU sed nếu tôi có lý do để tin rằng nó sẽ giải quyết được vấn đề, nhưng có vẻ như tôi đã loại trừ được điều đó.
iconoclast

Với Ruby, bạn sẽ chỉ phải sử dụng một dấu gạch chéo ngược:ruby -pe '$_.gsub!(/\t/," ")' < file.txt
vinc17 18/07/14

0

Nếu bạn có thể yêu cầu bashhoặc zshlàm vỏ thì không sao, đây là giải pháp đơn giản nhất tôi có thể nghĩ ra:

sed "s/$(echo -n -e "\t")/ /" file.txt

Tuy nhiên, xin lưu ý rằng các echocờ ( -n-e) không được xác định trong POSIX, do đó, vỏ tuân thủ POSIX không yêu cầu hiểu các cờ này, nhưng nhiều lý do sẽ tương thích.


-1

Tôi ngạc nhiên không ai đề xuất giải pháp rất đơn giản: sed -i.bak -E 's/\\\t/ /' file.txt Điều đó nên làm.

Bạn cần thoát khỏi lối thoát (do đó 3 \ s) để cho phép sed hiểu rằng bạn đang cố sử dụng ký tự \ t trong biểu thức thông thường khi mọi thứ được thay thế ...


Tại sao ba dấu gạch chéo cụ thể?
Michael Homer

3
Nếu tôi sử dụng GNU sed, một cái \ là đủ, vì không cần thoát. Vấn đề là BSD sedkhông hỗ trợ cú pháp này cho các tab.
iconoclast

Không hoạt động trên El Capitan của tôi.
Franklin Yu

-4

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

sed -e 's / [\ t] / / g'


3
Điều này là do bạn sử dụng GNU sed. Đây không phải là những gì OP sử dụng.
Kusalananda
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.