วิธีการแทนที่ทั้งบรรทัดในไฟล์ข้อความด้วยหมายเลขบรรทัด


152

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

ฉันไม่ได้พยายามที่จะแทนที่สตริงย่อยบางส่วนในบรรทัดนั้นฉันแค่ต้องการแทนที่บรรทัดนั้นด้วยบรรทัดใหม่ทั้งหมด

มีวิธีทุบตีสำหรับทำสิ่งนี้ (หรือสิ่งที่ง่ายที่สามารถโยนลงในสคริปต์. sh)

คำตอบ:


228

ไม่ใช่สิ่งที่ยิ่งใหญ่ที่สุด แต่ควรทำงานได้:

sed -i 'Ns/.*/replacement-line/' file.txt

ตำแหน่งที่Nควรแทนที่ด้วยหมายเลขบรรทัดเป้าหมายของคุณ สิ่งนี้จะแทนที่บรรทัดในไฟล์ต้นฉบับ หากต้องการบันทึกข้อความที่เปลี่ยนแปลงในไฟล์อื่นให้-iเลือกตัวเลือก:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt

1
มีวิธีทำให้ sed แก้ไขไฟล์ที่เป็นปัญหาแทนที่จะทิ้งไปที่หน้าจอหรือไม่?
user788171

8
สำหรับฉันมันพูดว่า: และสายของฉันคือsed: -e expression #1, char 26: unknown option to ``s' sed -i '7s/.*/<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpoint/</param-value>/' $TCE_SVN_HOME\trunk\tce\EWC\WebContent\WEB-INF\web.xmlความคิดใด ๆ
Danijel

4
แต่ละ/ข้อความในการแทนที่จะถูกตีความว่าเป็นเครื่องหมายสแลชปิดของsคำสั่งเว้นแต่จะหลบหนี ( \/) อย่างไรก็ตามสิ่งที่ง่ายที่สุดที่จะทำคือเลือกอักขระที่ไม่ได้ใช้เป็นตัวคั่น ตัวอย่างเช่นsed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk....
chepner

4
คำอธิบายนั้นสับสนเล็กน้อยและดูเหมือนจะไม่ทำงาน หากพวกคุณมีปัญหาเกี่ยวกับ sed และความสามารถในการตั้งค่าตัวคั่นคุณอาจต้องการดูที่: grymoire.com/Unix/Sed.html#uh-2มันบอกว่าตัวอักษรตัวแรกที่อยู่ด้านหลังตัวsกำหนด ดังนั้นในการเปลี่ยนคำสั่งของคุณเพื่อใช้เป็นตัวอย่าง#มันจะเป็นเช่นนี้:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
หากต้องการขยายด้วย @ meonlol ของ macOS ต้องมี-eif ถ้า-iได้รับเช่นกัน ดังนั้นคำสั่งที่ถูกต้องจะกลายเป็น:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

44

ฉันใช้สคริปต์นี้เพื่อแทนที่บรรทัดของรหัสในไฟล์ cron ในเซิร์ฟเวอร์ UNIX ของ บริษัท ของเราสักครู่ เราดำเนินการเป็นเชลล์สคริปต์ปกติและไม่มีปัญหา:

#Create temporary file with new line in place
cat /dir/file | sed -e "s/the_original_line/the_new_line/" > /dir/temp_file
#Copy the new file over the original file
mv /dir/temp_file /dir/file

นี้ไม่ได้ไปจากจำนวนบรรทัด แต่คุณสามารถสลับไปใช้ระบบตามหมายเลขบรรทัดโดยการใส่หมายเลขบรรทัดก่อนและวางตัวแทนในสถานที่ของs/the_original_line


17
การใช้แมวอย่างไร้ประโยชน์:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner

24
ไร้ประโยชน์สำหรับล่าม / คอมไพเลอร์อาจ แต่ไม่ใช่สำหรับมนุษย์ที่อ่านมัน @ ไคล์โค้ดอ่านจากซ้ายไปขวา สำนวนที่คุณใช้ IMO ไม่ได้เนื่องจากความจริงที่ว่าคำกริยาอยู่ตรงหน้าคำนาม
Clayton Stanley

1
มีวิธีที่จะรวมสองบรรทัดเหล่านี้เช่นโดยสิ่งที่ต้องการsed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

22

สมมติว่าคุณต้องการแทนที่บรรทัด 4 ด้วยข้อความ "แตกต่าง" คุณสามารถใช้ AWK ดังนี้:

awk '{ if (NR == 4) print "different"; else print $0}' input_file.txt > output_file.txt

AWK พิจารณาว่าข้อมูลที่ป้อนเป็น "บันทึก" แบ่งออกเป็น "ช่อง" โดยค่าเริ่มต้นหนึ่งบรรทัดคือหนึ่งระเบียน NRคือจำนวนเรคคอร์ดที่เห็น $0แสดงถึงระเบียนที่สมบูรณ์ปัจจุบัน (ในขณะที่$1เป็นเขตข้อมูลแรกจากระเบียนและอื่น ๆ โดยค่าเริ่มต้นเขตข้อมูลเป็นคำจากบรรทัด)

ดังนั้นหากหมายเลขบรรทัดปัจจุบันคือ 4 ให้พิมพ์สตริง "different" แต่ไม่เช่นนั้นให้พิมพ์บรรทัดที่ไม่เปลี่ยนแปลง

ใน AWK รหัสโปรแกรมที่ล้อมรอบ{ }จะทำงานหนึ่งครั้งในแต่ละระเบียนข้อมูล

คุณจำเป็นต้องพูดโปรแกรม AWK $0ในคำพูดเดียวที่จะทำให้เปลือกจากความพยายามในการตีความสิ่งที่ต้องการ

แก้ไข: โปรแกรม AWK ที่สั้นและสวยงามกว่าจาก @chepner ตามความคิดเห็นด้านล่าง:

awk 'NR==4 {$0="different"} { print }' input_file.txt

เฉพาะระเบียน (เช่นบรรทัด) หมายเลข 4 แทนที่ระเบียนทั้งหมดด้วยสตริง "แตกต่าง" จากนั้นสำหรับทุกอินพุตอินพุตให้พิมพ์เร็กคอร์ด

เห็นได้ชัดว่าทักษะ AWK ของฉันเต็มไปด้วยสนิม! ขอบคุณ @chepner

แก้ไข: และดูรุ่นสั้นยิ่งขึ้นจาก @Dennis Williamson:

awk 'NR==4 {$0="different"} 1' input_file.txt

วิธีการทำงานนี้อธิบายไว้ในความคิดเห็น: การ1ประเมินผลจริงเสมอดังนั้นบล็อกโค้ดที่เกี่ยวข้องจะทำงานอยู่เสมอ แต่ไม่มีการบล็อกรหัสที่เกี่ยวข้องซึ่งหมายความว่า AWK ทำการกระทำเริ่มต้นเพียงพิมพ์ทั้งบรรทัด AWK ถูกออกแบบมาเพื่ออนุญาตโปรแกรมสั้น ๆ เช่นนี้


3
สั้นกว่าเล็กน้อย: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner

2
@chepner: สั้นกว่าเล็กน้อย:awk 'NR==4 {$0="different"}1' input_file.txt
หยุดชั่วคราวจนกว่าจะมีประกาศ

@DennisWilliamson ฉันไม่รู้ด้วยซ้ำว่ามันทำงานยังไง! การติดตาม1นั้นทำอะไร (หมายเหตุ: ฉันทดสอบและใช้งานได้จริงฉันไม่เข้าใจเลย)
steveha

1
ฉันคิดว่าจะมีวิธีย่อพิมพ์ที่ไม่มีเงื่อนไข! @stevaha: 1 เป็นเพียงค่าจริงหมายถึง "รูปแบบ" ที่ตรงกับเสมอ และการกระทำเริ่มต้นสำหรับการแข่งขันคือการพิมพ์บรรทัดปัจจุบัน
chepner

19

รับไฟล์ทดสอบนี้ (test.txt)

Lorem ipsum dolor sit amet,
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

คำสั่งต่อไปนี้จะแทนที่บรรทัดแรกเป็น "ข้อความขึ้นบรรทัดใหม่"

$ sed '1 c\
> newline text' test.txt

ผลลัพธ์:

newline text
consectetur adipiscing elit. 
Duis eu diam non tortor laoreet 
bibendum vitae et tellus.

ข้อมูลเพิ่มเติมสามารถดูได้ที่นี่

http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-append-insert-replace-and-count-file-lines/


2
นี่ควรเป็นคำตอบที่ถูกต้อง แฟล็ก c ทำสิ่งที่ต้องการอย่างแท้จริง (แทนที่บรรทัดที่มีตัวเลขด้วยข้อความที่กำหนด)
adelphus

ไวยากรณ์ทางเลือกที่มี sed ที่ไม่ได้สะท้อนไฟล์ใน stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers

มันเป็นคำตอบที่ถูกต้อง แต่ทำไมเส้นแบ่งที่น่าเกลียด? sed '1 cnewline text' test.txtก็จะทำเช่นกัน
dev-null

7

ใน bash ให้แทนที่ N, M ด้วยหมายเลขบรรทัดและ xxx yyy ตามที่คุณต้องการ

i=1
while read line;do
  if((i==N));then
    echo 'xxx'
  elif((i==M));then
    echo 'yyy'
  else
    echo "$line"
  fi
  ((i++))
done  < orig-file > new-file

แก้ไข

ในความเป็นจริงในการแก้ปัญหานี้มีปัญหาบางอย่างกับตัวละคร "\ 0" "\ t" และ "\"

"\ t" สามารถแก้ไขได้โดยใส่ IFS = ก่อนอ่าน: "\" ที่ท้ายบรรทัดด้วย -r

IFS= read -r line

แต่สำหรับ "\ 0" ตัวแปรถูกตัดทอนไม่มีวิธีแก้ปัญหาใน pure bash: กำหนดสตริงที่มี null-character (\ 0) ให้กับตัวแปรใน Bash แต่ในไฟล์ข้อความปกติไม่มีอักขระ nul \ 0

Perl จะเป็นทางเลือกที่ดีกว่า

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file

ฉันไม่เคยคิดที่จะลองใช้วิธีทุบตีอย่างแท้จริง!
steveha

4
# Replace the line of the given line number with the given replacement in the given file.
function replace-line-in-file() {
    local file="$1"
    local line_num="$2"
    local replacement="$3"

    # Escape backslash, forward slash and ampersand for use as a sed replacement.
    replacement_escaped=$( echo "$replacement" | sed -e 's/[\/&]/\\&/g' )

    sed -i "${line_num}s/.*/$replacement_escaped/" "$file"
}

3

คำตอบที่ยอดเยี่ยมจาก Chepner มันใช้งานได้สำหรับฉันใน bash Shell

 # To update/replace the new line string value with the exiting line of the file
 MyFile=/tmp/ps_checkdb.flag

 `sed -i "${index}s/.*/${newLine}/" $MyFile`

ที่นี่
index- หมายเลขบรรทัด
newLine- สตริงบรรทัดใหม่ที่เราต้องการแทนที่


ในทำนองเดียวกันรหัสด้านล่างจะใช้ในการอ่านบรรทัดเฉพาะในไฟล์ สิ่งนี้จะไม่มีผลกับไฟล์จริง

LineString=`sed "$index!d" $MyFile` 

ที่นี่
!d- จะลบบรรทัดอื่นที่ไม่ใช่บรรทัด no $index ดังนั้นเราจะได้ผลลัพธ์เป็นสตริงสตริงที่ไม่มี$indexในไฟล์


แต่มันทุบตีของฉันทำไมผลแสดงเพียง${newline}?
sikisis


-2

คุณสามารถส่งพารามิเตอร์ไปยังคำสั่ง sed:

test.sh

#!/bin/bash
echo "-> start"
for i in $(seq 5); do
  # passing parameters to sed
  j=$(($i+3))
  sed -i "${j}s/.*/replaced by '$i'!/" output.dat
done
echo "-> finished"
exit 

orignial output.dat:

a
b
c
d
e
f
g
h
i
j

การดำเนินการ. /test.shจะให้ output.dat ใหม่

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j

1
ทำไมคุณต้องวนซ้ำสำหรับตัวอย่างนี้เพียงทำให้งงงวยการแก้ปัญหา? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob

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