มีทางเลือกอื่นสำหรับคำสั่ง "sed -i" ใน Solaris หรือไม่?


11

ฉันมีข้อกำหนดในโครงการของฉันเพื่อแทนที่ข้อความที่มีอยู่ในไฟล์เช่นเดียวfooกับข้อความอื่น ๆ เช่นfooofoo:

abc.txt
name
foo
foo1

ดังนั้นฉันจึงพยายาม:

sed -i "s/foo/fooofoo/g" abc.txt

อย่างไรก็ตามฉันได้รับข้อผิดพลาดนี้:

sed: ตัวเลือกที่ผิดกฎหมาย - i

ฉันพบในคู่มือที่ต้องใช้:

sed -i\ "s/foo/fooofoo/g" abc.txt

อย่างไรก็ตามสิ่งนี้ไม่ทำงานเช่นกัน

ฉันได้พบทางเลือกในperlและawkอีกวิธีหนึ่ง แต่โซลูชั่นใน Solaris sedจะได้รับการชื่นชมมาก

ฉันใช้ทุบตีรุ่นนี้:

GNU bash รุ่น 3.2.57 (1) - ปล่อย (sparc-sun-solaris2.10)


บน Solaris 11 และใหม่กว่าเพียงใช้/usr/gnu/bin/sedเพื่อรับการสนับสนุน -i
alanc

คำตอบ:


15

edใช้ สามารถใช้งานได้บนแพลตฟอร์มส่วนใหญ่และสามารถแก้ไขไฟล์ของคุณได้ในสถานที่
เนื่องจากsedเป็นไปตามedไวยากรณ์สำหรับการแทนที่รูปแบบที่คล้ายกัน:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

ทำไมedแทนที่จะexอยากรู้ (ฉันรู้ว่าทั้งสองสอดคล้องกับ POSIX และฉันเชื่อมโยงกับรายละเอียด;))
Wildcard

1
@Wildcard - ฉันสามารถขอตรงข้าม - ทำไมex? เพื่อตอบคำถามของคุณ: เนื่องจากฉันไม่เคยใช้ex ฉันไม่คุ้นเคย ครับผมเคยเห็นการตั้งค่าที่edเป็นปัจจุบัน แต่exไม่ได้ ( แต่ไม่ตรงข้าม) ... โดดเด่นที่สุดแล็ปท็อปของฉัน :)
don_crissti

คำตอบที่ดี. :) คำตอบของฉัน: เนื่องจากฉันใช้เป็นกลุ่มตลอดเวลาฉันคุ้นเคยกับexคำสั่งมาก viคำสั่งทั้งหมดที่คุณสามารถพิมพ์ที่ขึ้นต้นด้วยโคลอนคือexคำสั่ง แน่นอนว่ามีส่วนขยายเฉพาะ Vim บางส่วน แต่ชุดคำสั่งหลักมีประสิทธิภาพและยืดหยุ่นอย่างเหลือเชื่อและมีความหลากหลายสำหรับการแก้ไขสคริปต์
Wildcard

ระบบของฉัน Manjaro มีและยังไม่ex ed
กลาง

8

หากคุณไม่สามารถติดตั้ง GNU sed ให้ใช้:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

ใช้การเปลี่ยนเส้นทางเพื่อส่งผลลัพธ์ของ sed ไปยังไฟล์ชั่วคราว หาก sed เสร็จสมบูรณ์แล้วการเขียนทับนี้จะabc.txtมีไฟล์ชั่วคราว

ดังที่เห็นได้จากซอร์สโค้ดสำหรับ GNU sedนี่คือสิ่งที่sed -iทำ sed -iดังนั้นนี้เป็นเพียงเกี่ยวกับการเป็นที่มีประสิทธิภาพ

หากมีโอกาสที่abc.tmpมีอยู่แล้วคุณอาจต้องการที่จะใช้mktempหรือยูทิลิตี้ที่คล้ายกันเพื่อสร้างชื่อที่ไม่ซ้ำสำหรับชั่วคราว


ขอบคุณที่ช่วยเหลือ. อย่างไรก็ตามมีตัวเลือกใน solaris ซึ่งเราสามารถอัปเดตไฟล์ที่มีอยู่โดยไม่ต้องสร้างไฟล์ temp หรือไม่?
tpsaitwal

10
เกลียดที่จะทำลายมันให้คุณ แต่ถึงใจสร้างไฟล์ชั่วคราว git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller

1
คุณสามารถทำได้หากคุณไม่สนใจลิงค์ยาก - ดูตัวอย่างของฉัน ยังคงมีไฟล์ชั่วคราว แต่ไฟล์นั้นถูกซ่อนไว้ (ไม่มีชื่อ) หากไม่มีชั่วคราวคุณจะต้องใช้edเนื่องจากจะอ่านไฟล์ทั้งหมดในหน่วยความจำ
Toby Speight

3

ถ้าคุณต้องการเทียบเท่าsed -i.bakมันค่อนข้างง่าย

พิจารณาสคริปต์นี้สำหรับ GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

เราสามารถแทนที่บรรทัดที่ทำเครื่องหมายด้วย

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

สิ่งนี้จะย้ายไฟล์ที่มีอยู่เพื่อสำรองและเขียนไฟล์ใหม่

ถ้าเราต้องการเทียบเท่า

sed -i -e "$script" "$dir"  # no backup

มันซับซ้อนกว่าเล็กน้อย เราสามารถเปิดไฟล์เพื่ออ่านเป็นอินพุตมาตรฐานจากนั้นยกเลิกการเชื่อมโยงไฟล์ก่อนที่จะสั่งเอาต์พุตของ sed เพื่อแทนที่:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

เราทำสิ่งนี้ใน sub-shell เพื่อให้ stdin ดั้งเดิมของเรายังคงมีอยู่หลังจากนี้ เป็นไปได้ที่จะสลับอินพุตและสลับกลับโดยไม่ต้องแบ่งย่อย แต่วิธีนี้ดูเหมือนชัดเจนสำหรับฉัน

โปรดทราบว่าเราระมัดระวังในการคัดลอกก่อนแทนที่จะสร้างfooไฟล์ใหม่ซึ่งเป็นสิ่งสำคัญหากไฟล์นั้นมีชื่อมากกว่าหนึ่งชื่อ (เช่นมีฮาร์ดลิงก์) และคุณต้องการแน่ใจว่าคุณจะไม่ทำลายลิงก์ .


3

ขั้นแรกคุณควรรู้ว่าi\คำสั่งที่คุณอ้างถึงนั้นมีไว้สำหรับการแทรกบรรทัดข้อความ - ไม่ใช้สำหรับการบันทึกข้อความที่แก้ไขกลับไปที่ไฟล์ ไม่มีวิธีที่POSIX ระบุให้ใช้sedสำหรับสิ่งนั้น

สิ่งที่คุณสามารถทำได้คือการใช้งานexที่ถูกระบุโดย POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

xคำสั่งสำหรับ "บันทึกและออก." นอกจากนี้คุณยังสามารถใช้wqแต่xจะสั้นกว่า

%สัญญาณที่เริ่มต้นของวิธีการคำสั่งแทน "นำไปใช้คำสั่งนี้เพื่อบรรทัดในบัฟเฟอร์ทุก." คุณสามารถใช้1,$s/find/replace/gยัง


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


1

การใช้sedและไม่มีไฟล์ชั่วคราวที่มองเห็นได้ :

คุณสามารถหลีกเลี่ยงการสร้าง"ไฟล์ชั่วคราว" ที่มองเห็นได้ :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

คำอธิบาย

Unix เหมือนระบบไม่จริงเอาเนื้อหาของไฟล์จากดิสก์จนกว่าจะมีทั้งยกเลิกการเชื่อมโยงในระบบแฟ้มและไม่ได้เปิดในกระบวนการใด ๆ ดังนั้นคุณสามารถexec 3<เปิดไฟล์ในเชลล์เพื่ออ่านไฟล์ descriptor 3, rmไฟล์ (ซึ่งยกเลิกการลิงก์จากระบบไฟล์) จากนั้นเรียกsedใช้ file descriptor 3 ที่ใช้เป็นอินพุต

โปรดทราบว่านี้มากแตกต่างจากนี้

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

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

จากนั้นเมื่อเสร็จแล้วคุณสามารถปิดตัวอธิบายไฟล์ที่คุณเปิดไว้ก่อนหน้า (นั่นคือสิ่งที่exec 3<&-ไวยากรณ์พิเศษทำ) ซึ่งจะปล่อยไฟล์ต้นฉบับเพื่อให้ระบบปฏิบัติการสามารถลบ (ทำเครื่องหมายว่าไม่ได้ใช้) พื้นที่ว่างในดิสก์

คำเตือน

มีบางสิ่งที่ควรคำนึงถึงเกี่ยวกับโซลูชันนี้:

  1. คุณจะได้รับ "go" ผ่านทางเนื้อหา - ไม่มีวิธีพกพาสำหรับเชลล์ที่จะ "ค้นหา" กลับมาใน file descriptor - ดังนั้นเมื่อโปรแกรมอ่านเนื้อหาบางส่วนแล้วโปรแกรมอื่น ๆ จะเห็นเฉพาะส่วนที่เหลือของไฟล์ และsedจะอ่านไฟล์ทั้งหมด

  2. มีโอกาสเล็กน้อยที่ไฟล์ต้นฉบับของคุณจะหายไปหาก shell / script / sed ของคุณถูกฆ่าก่อนที่จะเสร็จสิ้น


เพื่อความชัดเจน @TobySpeight ได้โพสต์ตัวอย่างของวิธีการแก้ปัญหานี้ในตอนท้ายของคำตอบของเขา แต่ฉันคิดว่ามันอาจใช้ความละเอียดในเชิงลึกมากขึ้น
mtraceur

สำหรับผู้ลงคะแนน: แม้ว่าฉันจะแน่ใจว่าการออกจาก downvote ทำให้คุณรู้สึกดีกับตัวเอง แต่ฉันก็ยินดีถ้าคุณมีส่วนร่วมมากขึ้นโดยให้ข้อเสนอแนะเกี่ยวกับปัญหาที่คุณมีกับคำตอบนี้วิธีปรับปรุงให้ดีขึ้นเป็นต้น .
mtraceur

บางทีคุณสามารถใช้ perl -i
c4f4t0r

@ c4f4t0r: ขออภัยที่ตอบช้า: ใช่perl -iเป็นอีกตัวเลือกที่ดี คำถามดังกล่าวได้ขอวิธีการแก้ปัญหาที่เข้ากันได้กับ Solaris sedซึ่งตรงกันข้ามกับวิธีแก้ปัญหาด้วยperlและawkพวกเขากล่าวถึงการค้นพบแล้ว นอกจากนี้คำตอบของฉันยังมีวัตถุประสงค์เพื่อเพิ่มสิ่งที่ฉันคิดว่าเป็นประโยชน์ในการรู้และหายไปจากคำตอบอื่น ๆ
mtraceur
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.