sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
ฉันคาดหวังว่าsed
สคริปต์นี้จะแทรกtab
ด้านหน้าของทุกบรรทัด$filename
แต่มันไม่ใช่ ด้วยเหตุผลบางประการจึงเป็นการแทรกt
แทน
sed "s/\(.*\)/\t\1/" $filename > $sedTmpFile && mv $sedTmpFile $filename
ฉันคาดหวังว่าsed
สคริปต์นี้จะแทรกtab
ด้านหน้าของทุกบรรทัด$filename
แต่มันไม่ใช่ ด้วยเหตุผลบางประการจึงเป็นการแทรกt
แทน
คำตอบ:
ไม่ได้ทุกรุ่นเข้าใจsed
\t
เพียงใส่แท็บลิเทอรัลแทน (กดCtrl- Vแล้วTab)
\t
ในส่วนทดแทนของนิพจน์ (จำได้\t
ในส่วนการจับคู่รูปแบบได้ดี)
การใช้ Bash คุณสามารถแทรกอักขระ TAB โดยทางโปรแกรมได้ดังนี้:
TAB=$'\t'
echo 'line' | sed "s/.*/${TAB}&/g"
echo 'line' | sed 's/.*/'"${TAB}"'&/g' # use of Bash string concatenation
$'string'
แต่ขาดคำอธิบาย อันที่จริงฉันสงสัยว่าเนื่องจากการใช้งานที่น่าอึดอัดใจอย่างมากซึ่งคุณอาจมีความเข้าใจที่ไม่สมบูรณ์ (เหมือนที่พวกเราส่วนใหญ่ทำกับ bash) ดูคำอธิบายของฉันด้านล่าง: stackoverflow.com/a/43190120/117471
$TAB
ในเครื่องหมายคำพูดเดี่ยวดังนั้นคุณจะต้องใช้เครื่องหมายคำพูดคู่
*
เครื่องหมายคำพูดคู่ภายใน ... สิ่งนี้จะถือว่าเป็นรูปแบบเดียวไม่ใช่ regex ที่คุณต้องการ
@ แก้ไขมาถูกทางแล้ว แต่การกำหนดตัวแปรมันค่อนข้างอึดอัด
วิธีทำใน bash คือใส่เครื่องหมายดอลลาร์ไว้หน้าสตริงที่ยกมา
$ echo -e '1\n2\n3'
1
2
3
$ echo -e '1\n2\n3' | sed 's/.*/\t&/g'
t1
t2
t3
$ echo -e '1\n2\n3' | sed $'s/.*/\t&/g'
1
2
3
หากสตริงของคุณต้องการรวมส่วนขยายตัวแปรคุณสามารถรวมสตริงที่ยกมาเข้าด้วยกันดังนี้:
$ timestamp=$(date +%s)
$ echo -e '1\n2\n3' | sed "s/.*/$timestamp"$'\t&/g'
1491237958 1
1491237958 2
1491237958 3
ในการทุบตี$'string'
ทำให้เกิด "การขยาย ANSI-C" และนั่นคือสิ่งที่พวกเราส่วนใหญ่คาดหวังว่าเมื่อเราใช้สิ่งที่ชอบ\t
, \r
, \n
ฯลฯ จาก: https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
คำในรูปแบบ$ 'string'ได้รับการปฏิบัติเป็นพิเศษ คำขยายเป็นสตริงโดยแทนที่อักขระที่ใช้เครื่องหมายแบ็กสแลชจะถูกแทนที่ตามที่ระบุโดยมาตรฐาน ANSI C ลำดับการหลีกเลี่ยงแบ็กสแลชหากมีอยู่จะถูกถอดรหัส ...
ผลลัพธ์ที่ขยายเป็นเครื่องหมายคำพูดเดี่ยวราวกับว่าไม่มีเครื่องหมายดอลลาร์
โดยส่วนตัวแล้วฉันคิดว่าความพยายามส่วนใหญ่ในการหลีกเลี่ยงการทุบตีเป็นเรื่องไร้สาระเพราะการหลีกเลี่ยงการทุบตีไม่ได้ทำให้โค้ดของคุณพกพาได้ (รหัสของคุณจะเปราะน้อยกว่าถ้าคุณใช้มันbash -eu
มากกว่าถ้าคุณพยายามหลีกเลี่ยงการทุบตีและใช้sh
[เว้นแต่คุณจะเป็นนินจา POSIX ที่แท้จริง]) แต่แทนที่จะมีข้อโต้แย้งทางศาสนาเกี่ยวกับเรื่องนั้นฉันจะให้สิ่งที่ดีที่สุดแก่คุณ * คำตอบ
$ echo -e '1\n2\n3' | sed "s/.*/$(printf '\t')&/g"
1
2
3
* คำตอบที่ดีที่สุด? ใช่เพราะหนึ่งในตัวอย่างของสิ่งที่มากที่สุดป้องกันทุบตี scripters เปลือกจะทำผิดในรหัสของพวกเขาคือการใช้echo '\t'
ในขณะที่@ robrecord ของคำตอบ ซึ่งจะใช้ได้กับ GNU echo แต่ไม่ใช่ BSD echo ที่อธิบายโดย The Open Group ที่http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html#tag_20_37_16และนี่คือตัวอย่างว่าทำไมการพยายามหลีกเลี่ยงการทุบตีมักจะล้มเหลว
ฉันเคยใช้อะไรแบบนี้กับ Bash shell บน Ubuntu 12.04 (LTS):
ในการต่อท้ายบรรทัดใหม่ด้วยแท็บอันดับที่สองเมื่อจับคู่ครั้งแรก :
sed -i '/first/a \\t second' filename
ในการแทนที่แท็บแรกด้วยแท็บที่สอง :
sed -i 's/first/\\t second/g' filename
\\t
\t
ใช้$(echo '\t')
. คุณจะต้องมีเครื่องหมายคำพูดรอบรูปแบบ
เช่น. ในการลบแท็บ:
sed "s/$(echo '\t')//"
echo '\t'
จะแสดงอักขระแยกกัน 2 ตัว วิธีแบบพกพา POSIX printf '\t'
คือการใช้งาน นี่คือเหตุผลที่ฉันพูดว่า: อย่าพยายามทำให้โค้ดของคุณพกพาได้โดยไม่ใช้ bash มันยากกว่าที่คุณคิด การใช้bash
เป็นสิ่งที่พกพาสะดวกที่สุดที่พวกเราส่วนใหญ่ทำได้
คุณไม่จำเป็นต้องใช้sed
เพื่อทำการแทนที่ในความเป็นจริงคุณแค่ต้องการแทรกแท็บที่ด้านหน้าของบรรทัด การทดแทนสำหรับกรณีนี้เป็นการดำเนินการที่มีราคาแพงเมื่อเทียบกับการพิมพ์ออกมาโดยเฉพาะเมื่อคุณทำงานกับไฟล์ขนาดใหญ่ อ่านง่ายกว่าด้วยเพราะไม่ใช่ regex
เช่นใช้ awk
awk '{print "\t"$0}' $filename > temp && mv temp $filename
sed
ไม่รองรับ\t
หรือลำดับการหลบหนีอื่น ๆ เช่น\n
สำหรับเรื่องนั้น วิธีเดียวที่ฉันพบว่าทำได้คือการแทรกอักขระแท็บในสคริปต์โดยใช้sed
.
ที่กล่าวมาคุณอาจต้องการพิจารณาใช้ Perl หรือ Python นี่คือสคริปต์ Python สั้น ๆ ที่ฉันเขียนว่าฉันใช้สำหรับ regex'ing ของสตรีมทั้งหมด:
#!/usr/bin/env python
import sys
import re
def main(args):
if len(args) < 2:
print >> sys.stderr, 'Usage: <search-pattern> <replace-expr>'
raise SystemExit
p = re.compile(args[0], re.MULTILINE | re.DOTALL)
s = sys.stdin.read()
print p.sub(args[1], s),
if __name__ == '__main__':
main(sys.argv[1:])
แทนที่จะใช้ BSD sed ฉันใช้ perl:
ct@MBA45:~$ python -c "print('\t\t\thi')" |perl -0777pe "s/\t/ /g"
hi
ผมคิดว่าคนอื่น ๆ ได้ชี้แจงนี้เพียงพอสำหรับวิธีการอื่น ๆ ( sed
, AWK
ฯลฯ ) อย่างไรก็ตาม - bash
คำตอบเฉพาะของฉัน(ทดสอบบน macOS High Sierra และ CentOS 6/7) เป็นไปตาม
1) หาก OP ต้องการใช้วิธีการค้นหาและแทนที่ที่คล้ายกับที่พวกเขาเสนอในตอนแรกฉันขอแนะนำให้ใช้perl
สำหรับสิ่งนี้ดังนี้ หมายเหตุ:เครื่องหมายวงเล็บก่อนสำหรับ regex ไม่ควรจะเป็นสิ่งที่จำเป็นและสายรหัสนี้สะท้อนให้เห็นถึงวิธีการที่$1
ดีกว่าที่จะใช้งานกว่า\1
กับperl
ผู้ประกอบการเปลี่ยนตัว (เช่นต่อPerl 5 เอกสาร )
perl -pe 's/(.*)/\t$1/' $filename > $sedTmpFile && mv $sedTmpFile $filename
2) อย่างไรก็ตามตามที่ghostdog74ชี้ให้เห็นเนื่องจากการดำเนินการที่ต้องการจริงๆแล้วเพียงแค่เพิ่มแท็บที่จุดเริ่มต้นของแต่ละบรรทัดก่อนที่จะเปลี่ยนไฟล์ tmp เป็นไฟล์อินพุต / เป้าหมาย ( $filename
) ฉันขอแนะนำperl
อีกครั้ง แต่ด้วยการปรับเปลี่ยนต่อไปนี้ (s):
perl -pe 's/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
## OR
perl -pe $'s/^/\t/' $filename > $sedTmpFile && mv $sedTmpFile $filename
3) แน่นอนว่าไฟล์ tmp นั้นฟุ่มเฟือยดังนั้นจึงเป็นการดีกว่าที่จะทำทุกอย่าง 'ในสถานที่' (เพิ่ม-i
ค่าสถานะ) และลดความซับซ้อนของสิ่งต่าง ๆ ให้เป็นหนึ่งซับที่หรูหรายิ่งขึ้นด้วย
perl -i -pe $'s/^/\t/' $filename