วิธีรายงานการเปลี่ยนแปลงแบบ "sed" ในสถานที่


23

เมื่อใช้sedเพื่อแทนที่สตริงในสถานที่มีวิธีที่จะรายงานการเปลี่ยนแปลงที่เกิดขึ้น (โดยไม่ต้องอาศัยความแตกต่างของไฟล์เก่าและใหม่)?

ตัวอย่างเช่นฉันจะเปลี่ยนบรรทัดคำสั่งได้อย่างไร

find . -type f | xargs sed -i 's/abc/def/g'

ดังนั้นฉันสามารถเห็นการเปลี่ยนแปลงที่เกิดขึ้นทันที?

คำตอบ:


15

คุณสามารถใช้sed's wธงกับทั้ง/dev/stderr, /dev/tty, /dev/fd/2ถ้าได้รับการสนับสนุนบนระบบของคุณ เช่นการป้อนข้อมูลfileเช่น:

foo first
second: missing
third: foo
none here

วิ่ง

sed -i '/foo/{
s//bar/g
w /dev/stdout
}' file

เอาท์พุท:

bar first
third: bar

แม้ว่าfileเนื้อหาจะเปลี่ยนเป็น:

bar first
second: missing
third: bar
none here

ดังนั้นในกรณีของคุณทำงาน:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
s//bar/g
w /dev/fd/2
}' {} \;

จะแก้ไขไฟล์ในสถานที่และเอาท์พุท:

./file1:
สิ่งที่บาร์
แถบเพิ่มเติม

./file2:

./file3:
แถบแรก
ที่สาม: แถบ

คุณสามารถพิมพ์บางอย่างoriginal line >>> modified lineเช่น:

find . -type f -printf '\n%p:\n' -exec sed -i '/foo/{
h
s//bar/g
H
x
s/\n/ >>> /
w /dev/fd/2
x
}' {} \;

แก้ไขไฟล์ในสถานที่และเอาท์พุท:

./file1:
foo stuff >>> bar bar
foo มากขึ้น >>> บาร์มากขึ้น

./file2:

./file3:
foo first >>> บาร์ก่อน
ที่สาม: foo >>> สาม: บาร์

16

คุณสามารถทำได้สองรอบโดยใช้pแอ็คชั่น rint ในรอบแรกด้วย:

find . -type f | xargs sed --quiet 's/abc/def/gp'

ที่--quietทำให้ sed ไม่แสดงทุกบรรทัดและpคำต่อท้ายจะแสดงเฉพาะบรรทัดที่ทดแทนได้

นี้มีข้อ จำกัด ที่ sed จะไม่แสดงซึ่งไฟล์ที่มีการเปลี่ยนแปลงซึ่งแน่นอนอาจจะคงมีบางส่วนซับซ้อนเพิ่มเติม


มันไม่ได้ทำสิ่งที่ฉันต้องการเพราะคุณต้องการสองรอบ แต่ฉันลงคะแนนเพราะฉันเพิ่งเรียนรู้สิ่งนี้และมันมีประโยชน์มาก โดยเฉพาะอย่างยิ่งถ้าอย่างที่คุณพูดไฟล์ก็จะถูกพิมพ์เช่นกัน บางสิ่งบางอย่างเช่นfor x in `find . -type f`; do echo ///File $x: ; sed --quiet 's/abc/def/gp' $x; done
ricab

@msw ฉันมีsedการแสดงออกหลายอย่างฉันไม่ต้องการที่จะเขียน/gpสำหรับแต่ละคน วิธีการตั้งค่ามัน globaly?
alhelal

8

ฉันไม่คิดว่าเป็นไปได้ แต่วิธีแก้ปัญหาอาจใช้ Perl แทน:

find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 

สิ่งนี้จะพิมพ์บรรทัดที่เปลี่ยนแปลงไปเป็นข้อผิดพลาดมาตรฐาน ตัวอย่างเช่น:

$ cat foo
fooabcbar
$ find . -type f | xargs perl -i -ne 's/abc/def/ && print STDERR' 
foodefbar

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

$ find . -type f | 
   xargs perl -i -ne '$was=$_; chomp($was);
                      s/abc/def/ && print STDERR "$ARGV($.): $was : $_"' 
./foo(1): fooabcbar : foodefbar

+1 นอกจากนี้คุณยังสามารถใช้$ARGVสำหรับชื่อไฟล์ที่ใช้งาน
โจเซฟอาร์

@JosephR จุดดีเข้ามา
terdon

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

@ricab ไม่เป็นไรคุณไม่ควรยอมรับคำตอบเว้นแต่จะตอบคำถามของคุณจริงๆ จำไว้ว่าที่สำหรับสิ่งที่ง่ายเช่นนี้perl's ไวยากรณ์จะคล้ายกับsed' s sedและฉันไม่คิดว่าสิ่งที่คุณขอเป็นจริงไปได้ด้วย
terdon

3

เป็นไปได้โดยใช้แฟล็ก wซึ่งเขียนรูปแบบปัจจุบันลงในไฟล์ ดังนั้นโดยการเพิ่มลงในคำสั่ง replace เราสามารถรายงานการทดแทนต่อเนื่องไปยังไฟล์และพิมพ์หลังจากงานเสร็จสิ้น ฉันยังต้องการ colorize สตริงแทนที่ด้วย grep

sed -i -e "s/From/To/gw /tmp/sed.done" file_name
grep --color -e "To" /tmp/sed.done

โปรดทราบว่าจะต้องมีช่องว่างเดียวระหว่างwและชื่อไฟล์

สิ่งนี้ดียิ่งกว่า diff เนื่องจาก diffing อาจแสดงการเปลี่ยนแปลงแม้ว่าพวกเขาจะไม่ได้ทำโดย sed


เพิ่งผ่านการทดสอบมันออกมาและ SED เพียงเขียนเส้นมัน substituded sed.doneไป สายกับ "ถึง" ในไฟล์ต้นฉบับจึงไม่ได้พิมพ์ไปsed.doneดังนั้นเมื่อคุณgrep "To" sed.doneคุณจะเห็นเฉพาะเส้นที่มีการsedเปลี่ยนแปลงได้โดย คุณจะไม่เห็นบรรทัดต้นฉบับในไฟล์ก่อนที่จะถูกแทนที่ถ้านั่นคือสิ่งที่คุณกำลังเล็งที่ ...
aairey

1

ฉันชอบ @terdon solution - perl ดีสำหรับเรื่องนี้

นี่คือเวอร์ชันที่ปรับแต่งแล้วของฉัน:

  • จะไม่พยายามแก้ไขไฟล์ที่ไม่มีสตริงที่ตรงกัน
  • จะสำรองไฟล์รุ่นที่เปลี่ยนแปลงก่อน (สร้างรุ่น. bak)
  • จะแสดงรายการไฟล์ / lineno ทุกรายการที่มีการเปลี่ยนแปลงและแสดง OLD และเวอร์ชันใหม่ของบรรทัดนั้นที่ย่อหน้าและด้านล่างเพื่อให้อ่านง่าย

รหัส

find /tmp/test -type f ! -name "*.bak" -exec grep -l '/opt/gridmon' {} \; | xargs -L1 perl -ni'.bak' -e'$old=$_; s/\/opt\/gridmon/~/g && print STDERR "$ARGV($.):\n\tOLD:$old\tNEW:$_"'

เอาท์พุทตัวอย่าง

/tmp/test/test4.cfg(13):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
/tmp/test/test4.cfg(24):
    OLD:    ENVFILE /opt/gridmon/server/etc/gridmonserver.cfg
    NEW:    ENVFILE ~/server/etc/gridmonserver.cfg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.