ฉันสามารถใช้ `sed` เพื่อแปลอักขระเช่นเดียวกับ` tr 'ได้หรือไม่


14

ฉันต้องการแทนที่ชุดอักขระด้วยอักขระที่เกี่ยวข้องจากอีกชุดหนึ่งดังนี้:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

การแปล / การทับศัพท์เช่นนี้เป็นtrคำสั่งพิเศษ:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

น่าเสียดายที่trไม่รองรับการเปลี่ยนไฟล์แบบsedเดิม
ฉันต้องการที่จะใช้sedดังนั้นฉันไม่จำเป็นต้องบูรณาการล้อของไฟล์ชั่วคราวสำหรับเล่นปาหี่


ตอบคำถามนี้ด้วยตนเองเนื่องจากดูเหมือนว่าฉันจะไม่พบผลลัพธ์ใด ๆ สำหรับ "ตัวละครที่แปลแล้ว" คำหลักวิเศษนั้นกลายเป็น "นักแปล" แต่ฉันคิดว่ามันคุ้มค่าที่จะทำให้คุณลักษณะนี้หาได้ง่ายที่สุด
n.st

สิ่งที่จะเก็บไว้ในใจเมื่อพยายามที่จะใช้วิธีการแก้ปัญหาสำหรับเรื่องนี้: tr(ถูกต้อง) ละเว้นการเรียกซ้ำในชุดทดแทน: →echo 'abc' | tr ab bx bxcวิธีการแก้ปัญหาดั้งเดิมอาจเขียงที่xxcเพราะมันใช้การแปลอีกครั้งกับตัวละครที่ได้รับการแปลแล้ว
n.st

เกี่ยวข้อง: tr analog สำหรับอักขระ Unicode หรือไม่ (GNU sedตรงกันข้ามกับ GNU trสามารถแปลตัวอักษรแบบหลายไบต์)
Stéphane Chazelas

ถ้าคุณต้องการความเป็นไปได้อื่น: Perl สามารถแปลและ -i และ (ยกเว้นในกรณีโบราณ) มัลติไบต์ ไม่ใช่ POSIX แต่เป็นเรื่องธรรมดา
dave_thompson_085

คำตอบ:


24

sedมีyคำสั่งที่ทำงานเหมือนtr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

yคำสั่งเป็นส่วนหนึ่งPOSIX sedเปคจึงควรจะทำงานในเพียงเกี่ยวกับแพลตฟอร์มใด ๆ

และเนื่องจากมันsedคุณสามารถให้มันแทนที่ไฟล์ด้วยเวอร์ชันที่ถูกแก้ไขทำให้ธุรกิจไฟล์ temp น่ารำคาญ (ให้การติดตั้งsedรองรับ-iตัวเลือกซึ่งไม่ได้ระบุโดย POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt

@ StéphaneChazelasขอบคุณที่ชี้ให้เห็น; ฉันไม่ได้ตระหนักถึงการทำงานภายในจนถึงตอนนี้ ฉันได้แก้ไขคำตอบของฉันเพื่อพูดถึงเรื่องนั้น
n.st

ขอบคุณสิ่งนี้มีประโยชน์มาก! ฉันคาดหวังว่ามันจะทำงานใน VIM (8.0.1092 บน CentOS 7.3) แต่ก็ไม่เป็นเช่นนั้น สิ่งใดที่ไม่ควรทำ VIM ทำเช่นนั้น?
dotancohen

1
@Dotancohen เพียงเพราะฟังก์ชั่นการแทนที่ของ Vim นั้นเป็นแบบอย่างหลังจากนั้นsedก็ไม่ได้หมายความว่าฟังก์ชั่นอื่น ๆ ก็เช่นกัน ;) รายชื่อผู้รับจดหมายเป็นกลุ่มมีหัวข้อเกี่ยวกับการค้นหาy/abc/def/เทียบเท่า; :%call setline(".", tr(getline("."),"abc","def"))เลือกที่ดีที่สุดน่าจะเป็น
n.st

8

หากเหมือนในกรณีของคุณคุณกำลังถอดเสียงตัวอักษรโดยไม่ต้องเปลี่ยนขนาด (อย่างไรก็ตามการใช้งานบางอย่างเช่น GNU trรองรับเฉพาะอักขระไบต์เดียว) คุณสามารถทำได้:

tr 'ots' 'u.x' < file 1<> file

นั่นคือtrเขียนทับไฟล์ทับตัวมันเอง

ดีกว่าsed -iในหลายบัญชี:

  • ไม่จำเป็นต้องใช้พื้นที่ดิสก์เพิ่มเติม (ยกเว้นไฟล์แบบกระจัดกระจาย, กรณีพิเศษแบบ copy-on-write)
  • มันรักษาหมายเลข inode, ความเป็นเจ้าของ, สิทธิ์, ACLs ...
  • มันใช้งานได้ดีกับ symlink แต่ก็ไม่ได้ทำให้ลิงก์เสียหาย
  • มันไม่ปล่อยให้ไฟล์ temp โกหกเมื่อถูกฆ่า

ข้อเสียเปรียบประการหนึ่งคือหากไฟล์ถูกขัดจังหวะไฟล์จะจบลงด้วยการแปลครึ่งภาษา (ในกรณีนี้แม้ว่าคุณจะสามารถเรียกใช้อีกครั้งเพื่อสิ้นสุดไฟล์) sedการใช้งานบางอย่างจะจัดการอย่างถูกต้องโดยทำให้แน่ใจว่าไฟล์ต้นฉบับยังคงไม่เปลี่ยนแปลงเว้นแต่คำสั่งจะประสบความสำเร็จ


3
echo 'abc' | tr ab bxระวังเรื่องการทำงานการแปลถ้าคุณได้มีการเรียกซ้ำในชุดการแปลเช่น
n.st

1
@ n.st ใช่นั่นคือเหตุผลที่ฉันพูดในกรณีนี้แม้ว่าฉันจะยอมรับว่ามันคุ้มค่าที่จะสะกดคำ
Stéphane Chazelas

ในท้ายที่สุดผมต้องทำงานกับไฟล์ temp หลังจากทั้งหมด: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - ตัวละคร multibyte ทำให้มันเป็นไปไม่ได้ที่จะใช้ GNU trและในสภาพแวดล้อม PXE symlink หนักของเราsed -iเป็นรอสกรูขึ้น จะเกิดขึ้น…: /
n.st

@ n.st iconv -t cp437ดูเหมือนจะเหมาะสมกว่าสำหรับสิ่งนั้น
Stéphane Chazelas

iconvหยุดพักเมื่อไฟล์อินพุตมีไบต์ที่เข้ารหัส cp437 หรือมีการเข้ารหัสหลายแบบ ดังนั้นแม้ว่าจะดีกว่าในกรณีทั่วไป แต่ก็มีประสิทธิภาพมากกว่าในการเปลี่ยนด้วยตนเองในกรณีนี้
n.st

4

อีกทางเลือกหนึ่งหากปัญหาหลักของคุณคือการขาดการสนับสนุนการเปลี่ยนไฟล์ในสถานที่คุณอาจสนใจspongeเครื่องมือจากแพ็คเกจ moreutils :

tr 'ots' 'u.x' < file | sponge file

จะเขียนถึงfileแต่เปิดเฉพาะfileสำหรับการเขียนเมื่ออินพุตเสร็จสมบูรณ์ จากmanpage :

spongeอ่านอินพุตมาตรฐานและเขียนลงในไฟล์ที่ระบุ แตกต่างจากการเปลี่ยนเส้นทางของเชลล์ฟองน้ำดูดซับอินพุตทั้งหมดก่อนที่จะเปิดไฟล์เอาต์พุต สิ่งนี้อนุญาตให้สร้างท่อที่อ่านและเขียนไปยังไฟล์เดียวกัน

ถ้าคุณไม่มีไฟล์ขนาดใหญ่ที่ไม่สามารถเก็บไว้ในหน่วยความจำspongeได้ก็สามารถใช้ได้กับคุณ


2
ปัญหาหนึ่งspongeคือมันยังคงเขียนทับfileหากtrล้มเหลว (ตัวอย่างเช่นถ้าคุณมีการเขียน แต่ไม่สามารถเข้าถึงการอ่านfile)
Stéphane Chazelas

โอ้ใช่แล้ว ฉันไม่ได้คาดหวังสิ่งนั้น ขอบคุณ
mindriot

ดูcat file >; fileโอเปอเรเตอร์ของ ksh93 ซึ่งเขียนเอาต์พุตไปยัง tempfile ซึ่งถูกเปลี่ยนชื่อเป็นปลายทางต่อเมื่อคำสั่งประสบความสำเร็จเท่านั้น (แต่จะเหมือนsed -iกันซึ่งจะสร้างไฟล์ใหม่แทนการเขียนทับต้นฉบับ)
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.