การเปลี่ยนแท็บอย่างง่าย ๆ ล้มเหลวอย่างลึกลับ


43

นี่ควรจะง่ายจริงๆ แต่ด้วยเหตุผลบางอย่างมันไม่ทำงาน:

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

แทนที่จะเป็นการแทนที่อักขระแท็บมันเป็นการแทนที่tอักขระ ฉันลองทุกรูปแบบเกี่ยวกับเรื่องนี้ฉันสามารถคิดถึงเล่นกับข้อความ ฯลฯ ฉัน Googled และพบคนอื่น ๆ ที่ใช้คำพูดที่คล้ายกันและดูเหมือนว่าพวกเขาจะได้ผลสำหรับพวกเขา

สิ่ง-Eนี้เป็น OS X ฉันคิดว่าความล้มเหลวอาจเป็นผลมาจากการเล่นโวหารแปลก ๆ ของ OS X sedดังนั้นฉันจึงลองใช้กับ Ruby เช่นกัน (ไม่ใส่-i) และได้รับผลลัพธ์เดียวกัน:

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

ฉันใช้ Bash 3.2.51 บน OS X และ iTerm ถึงแม้ว่าฉันจะไม่เห็นว่าสิ่งเหล่านี้จะเกี่ยวข้องกันมากเพียงใด ฉันไม่ได้ตั้งค่าตัวแปรสภาพแวดล้อมแปลก ๆ แต่ฉันสามารถโพสต์สิ่งที่คุณคิดว่าอาจเกี่ยวข้อง

มีอะไรผิดปกติ?

UPDATE : ฉันต้องทำผิดพลาดหรือพิมพ์ผิดเมื่อฉันลองรุ่น Ruby เนื่องจาก Gilles ชี้ให้เห็นว่ามันใช้งานได้ (และฉันไม่เคยทำให้เขาผิดเลย!) ฉันไม่แน่ใจว่าเกิดอะไรขึ้น แต่ฉันค่อนข้างมั่นใจว่าต้องเป็นความผิดพลาดของฉัน


5
อาจเป็นได้ว่าคุณควรลองแทนที่\tในsedคำสั่งด้วยCTRL-V<TAB>ตำแหน่งที่<TAB>เป็นคีย์แท็บและCTRL-Vเป็นคีย์ควบคุมและvกดด้วยกัน
unxnut

หากทับทิมยังได้รับคำตอบที่ผิดก็อาจเป็นห้องสมุด regexp ของคุณ (ฉันได้ทดสอบทั้งคำสั่งของคุณและแทนที่ทั้งแท็บด้วย 2 ช่องว่าง) ดังนั้นหวังว่าถ้าคุณติดตั้ง Gnu sed มันจะติดตั้งไลบรารี่ที่ถูกต้องด้วย
ctrl-alt-delor

คำตอบ:


63

ไวยากรณ์\tสำหรับอักขระแท็บใน sed ไม่ใช่มาตรฐาน การหลบหนีที่เป็นส่วนขยายของ GNU sed คุณพบตัวอย่างจำนวนมากออนไลน์ที่ใช้งานได้เพราะผู้คนจำนวนมากใช้ GNU sed (เป็นการใช้งานของ sed บน Linux ที่ไม่ได้ฝังตัว) แต่OS X sedเช่น * BSD อื่น ๆ sed ไม่สนับสนุน\tแท็บและแทนที่จะถือว่าเป็นความหมายทับขวาตามด้วย\tt

มีวิธีแก้ปัญหามากมายเช่น:

  • ใช้อักขระแท็บตัวอักษร

    sed -i.bak 's/  /  /' file.txt
    
  • ใช้trหรือprintfสร้างอักขระแท็บ

    sed -i.bak "s/$(printf '\t')/  /" file.txt
    sed -i.bak "s/$(echo a | tr 'a' '\t')/  /" file.txt
    
  • ใช้ทุบตีของไวยากรณ์สตริงช่วยให้หนีทับขวา

    sed -i.bak $'s/\t/  /' file.txt
    
  • ใช้ Perl, Python หรือ Ruby ตัวอย่างข้อมูลทับทิมที่คุณโพสต์ใช้งานได้


สำหรับสคริปต์ sed ที่มีอยู่ใน...sedสคริปต์ (ใช้ผ่าน-fตัวเลือก) อักขระแท็บตัวอักษรดูเหมือนเป็นไปได้สำหรับฉันเท่านั้น เมื่อแก้ไขสิ่งนี้ด้วยเสียงเรียกเข้าset noexpandtabเป็นสิ่งสำคัญ
โทเบียส

คำเตือน:ใช้เทคนิค "แท็บตัวอักษรตามตัวอักษร" เฉพาะในกรณีที่คุณต้องการให้เพื่อนร่วมงานของคุณกลับมาด้านหลังและทำลายสคริปต์ในภายหลัง ใช้trเทคนิคนั้นเฉพาะเมื่อคุณต้องการให้เพื่อนร่วมงานของคุณแทงคุณต่อหน้าเมื่อพวกเขาอ่านสคริปต์ของคุณ
Bruno Bronosky

เครื่องหมายคำพูดคู่ที่สองวางผิดที่ในบล็อกที่สองของรหัสหรือไม่ ฉันต้องย้ายไปที่ที่อ้างถึงคำเดียวปิดปัจจุบัน
Ellen Spertus

ขอบคุณสำหรับลิงก์ไปยังไวยากรณ์สตริง bash ... ฉันไม่ทราบ (และนี่คือตัวเลือกที่ดีที่สุด IMHO)
levigroker

sed $'s/<regex>/\t/' file.txtทำงานได้สำหรับการแทรก แต่$ดูเหมือนว่าจะทำลายสคริปต์ของฉันเมื่อฉันพยายามที่จะรวมเป็นส่วนหนึ่งของ regex ในการทดแทนของฉันคือsed $'s,\(ontology/[0-9]\+\),\t\txxx\1xxx\t\t,'ให้ 'xxxxxx' กับค่าการจับคู่ที่คาดไว้ของฉันแทนที่ด้วย `` มีการเทียบเท่ากับ\1เมื่อใช้ไวยากรณ์สตริงของทุบตี? แก้ไข: มีตัวอักษรยูนิโค้ด U + 231C อยู่ตรงกลางของ xxx <U + 231C> xxx
Josh

14

ใช้ข้อความเฉพาะของ Bash ซึ่งช่วยให้คุณใช้สตริงเหมือนใน C ดังนั้นอักขระแท็บจริงจะถูกส่งผ่านไปยัง sed ไม่ใช่ลำดับ escape:

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

1
หรือที่เรียกว่าการอ้างอิง "ANSI-C" หากผู้อื่นต้องการค้นหาข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้
wisbucky

2
ดูเหมือนว่าจะทำงานบนเชลล์ bourne ใด ๆ , ทำงานบน UNIX ที่ไม่ได้ถูกทุบตีเช่นกัน ไม่ได้ทำงานกับ csh-variants
jornane

1

ตามที่ระบุไว้sedการใช้งานไม่ได้ทั้งหมดสนับสนุนสัญกรณ์ของ\tเป็นแท็บแนวนอน

คุณสามารถทดแทนได้อย่างง่ายดายด้วย:

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

สิ่งนี้จะดำเนินการในแหล่งกำเนิดที่จะรักษาไฟล์ต้นฉบับของคุณเป็น "* .old" Perl อนุญาตให้ตัวคั่นสำรองสำหรับคลาสสิก/ทำให้การแสดงออกสามารถอ่านได้มากขึ้น (เช่นไร้กลุ่มอาการของ "พิงไม้จิ้มฟัน")

การ+กล่าวซ้ำของอักขระแท็บหนึ่งรายการขึ้นไปจะถูกแทนที่ โมดิgฟายเออร์ช่วยให้สามารถแทนที่ทั่วโลกได้ในตอนท้ายของแต่ละบรรทัด


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

ใช้ได้กับฉันใน OS X และเป็นคำสั่งเดียวกับที่ฉันใช้กับ linux ตลอดเวลา


โปรดทราบว่าสิ่งนี้จะแทนที่แท็บทั้งหมดในทุกแถวในขณะที่ OP ตั้งใจจะแทนที่เฉพาะแท็บแรกเท่านั้น (การตัดสินจากคำสั่งที่ใช้)
Kusalananda

0

คุณยังสามารถใช้echoภายในsed:

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


โปรดทราบว่าecho '\t'จะเป็นเพียงแค่การส่งออกในการดำเนินงานบางส่วนเปลือกของ\t echo
Kusalananda

0

ถ้าคุณต้องการให้มีประสิทธิภาพมากขึ้นsed(การสนับสนุน\tและอื่น ๆ ) มากกว่าหนึ่งใน OS X ติดตั้งGNU sed


เนื่องจากมันใช้งานไม่ได้กับ Ruby ฉันไม่แน่ใจว่าทำไมฉันจึงสรุปได้ว่า OS X sedเป็นปัญหา คุณมีเหตุผลที่จะเชื่อว่าเป็นปัญหาหรือไม่ ฉันยินดีที่จะติดตั้ง GNU sed ถ้าฉันมีเหตุผลที่เชื่อว่ามันจะแก้ปัญหาได้ แต่ดูเหมือนว่าฉันได้ตัดสิทธิ์ออกไปแล้ว
iconoclast

ด้วย Ruby คุณจะต้องใช้แบ็กสแลชเดียวเท่านั้น:ruby -pe '$_.gsub!(/\t/," ")' < file.txt
vinc17

0

ถ้ามันโอเคที่จะต้องใช้bashหรือzshเป็นเชลล์แล้วนี่เป็นวิธีที่ง่ายที่สุดที่ฉันคิดได้:

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

อย่างไรก็ตามโปรดทราบว่าechoค่าสถานะ ( -nและ-e) นั้นไม่ได้กำหนดใน POSIX ดังนั้น POSIX ที่สอดคล้องกับเชลล์ไม่จำเป็นต้องทำความเข้าใจกับค่าสถานะเหล่านี้


-1

ฉันประหลาดใจที่ไม่มีใครแนะนำวิธีแก้ปัญหาอย่างง่าย ๆ ของ: sed -i.bak -E 's/\\\t/ /' file.txt นั่นควรทำเคล็ดลับ

คุณต้องหลบหนีการหลบหนี (ดังนั้น 3 \ s) เพื่อให้ sed เข้าใจว่าคุณกำลังพยายามใช้อักขระ \ t ในนิพจน์ปกติเมื่อทุกอย่างถูกแทนที่ ...


ทำไมแบ็กสแลชสามรายการโดยเฉพาะ
Michael Homer

3
หากฉันใช้ GNU sedหนึ่ง\ คือพอไม่มีหนีเป็นสิ่งที่จำเป็น ปัญหาคือ BSD sedไม่สนับสนุนไวยากรณ์นี้สำหรับแท็บ
iconoclast

ไม่ทำงานกับ El Capitan ของฉัน
Franklin Yu

-4

สิ่งนี้ใช้ได้สำหรับฉัน

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


3
นี้เป็นเพราะคุณใช้ sedGNU นี่ไม่ใช่สิ่งที่ OP ใช้
Kusalananda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.