วิธีค้นหาข้อความไฟล์สำหรับรูปแบบและแทนที่ด้วยค่าที่กำหนด


117

ฉันกำลังมองหาสคริปต์เพื่อค้นหาไฟล์ (หรือรายการไฟล์) สำหรับรูปแบบและหากพบให้แทนที่รูปแบบนั้นด้วยค่าที่กำหนด

คิด?


1
ในคำตอบด้านล่างโปรดทราบว่าคำแนะนำในการใช้งานFile.readจำเป็นต้องมีการปรับแต่งข้อมูลใน stackoverflow.com/a/25189286/128421ว่าเหตุใดการเบลอไฟล์ขนาดใหญ่จึงไม่ดี นอกจากนี้แทนที่จะFile.open(filename, "w") { |file| file << content }ใช้รูปแบบFile.write(filename, content)ต่างๆ
The Tin Man

คำตอบ:


190

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

นี่เป็นวิธีง่ายๆสั้น ๆ ในการทำ

file_names = ['foo.txt', 'bar.txt']

file_names.each do |file_name|
  text = File.read(file_name)
  new_contents = text.gsub(/search_regexp/, "replacement string")

  # To merely print the contents of the file, use:
  puts new_contents

  # To write changes to the file, use:
  File.open(file_name, "w") {|file| file.puts new_contents }
end

ทำให้เขียนการเปลี่ยนแปลงกลับไปที่ไฟล์หรือไม่ ฉันคิดว่าจะพิมพ์เนื้อหาลงในคอนโซล
Dane O'Connor

ใช่มันพิมพ์เนื้อหาไปยังคอนโซล
sepp2k

7
ใช่ฉันไม่แน่ใจว่านั่นคือสิ่งที่คุณต้องการ ในการเขียนให้ใช้ File.open (file_name, "w") {| file | file.puts output_of_gsub}
Max Chernyak

7
ฉันต้องใช้ file.write: File.open (file_name, "w") {| file | file.write (text)}
ออสเตน

3
ในการเขียนไฟล์ให้แทนที่ puts 'line ด้วยFile.write(file_name, text.gsub(/regexp/, "replace")
แน่น

106

อันที่จริง Ruby มีคุณสมบัติการแก้ไขแบบแทนที่ เช่นเดียวกับ Perl คุณสามารถพูดได้

ruby -pi.bak -e "gsub(/oldtext/, 'newtext')" *.txt

การดำเนินการนี้จะใช้รหัสในเครื่องหมายอัญประกาศคู่กับไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันที่มีชื่อลงท้ายด้วย ".txt" สำเนาสำรองของไฟล์ที่แก้ไขจะถูกสร้างขึ้นด้วยนามสกุล ".bak" (ฉันคิดว่า "foobar.txt.bak")

หมายเหตุ: ดูเหมือนว่าสิ่งนี้จะใช้ไม่ได้กับการค้นหาแบบหลายบรรทัด สำหรับสิ่งนั้นคุณต้องทำอีกวิธีที่ไม่ค่อยสวยด้วยสคริปต์ wrapper รอบ regex


1
pi.bak คืออะไร? หากไม่มีสิ่งนั้นฉันได้รับข้อผิดพลาด -e: 1: ใน<main>': undefined method gsub 'สำหรับ main: Object (NoMethodError)
Ninad

15
@NinadPachpute -iแก้ไขในสถานที่. .bakเป็นส่วนขยายที่ใช้สำหรับไฟล์สำรอง (ทางเลือก) เป็นสิ่งที่ต้องการ-p while gets; <script>; puts $_; end( $_เป็นบรรทัดการอ่านสุดท้าย แต่คุณสามารถกำหนดให้เป็นบางอย่างecho aa | ruby -p -e '$_.upcase!'ได้)
Lri

1
นี่เป็นคำตอบที่ดีกว่าคำตอบที่ยอมรับ IMHO หากคุณต้องการแก้ไขไฟล์
Colin K

6
ฉันจะใช้สิ่งนี้ในสคริปต์ทับทิมได้อย่างไร?
Saurabh

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

49

โปรดทราบว่าเมื่อคุณทำเช่นนี้ระบบไฟล์อาจไม่มีพื้นที่เหลือและคุณอาจสร้างไฟล์ที่มีความยาวเป็นศูนย์ นี่เป็นหายนะหากคุณกำลังทำบางอย่างเช่นการเขียนไฟล์ / etc / passwd เป็นส่วนหนึ่งของการจัดการการกำหนดค่าระบบ

โปรดทราบว่าการแก้ไขไฟล์ในสถานที่เช่นในคำตอบที่ยอมรับจะตัดทอนไฟล์และเขียนไฟล์ใหม่ตามลำดับเสมอ มักจะมีเงื่อนไขการแข่งขันที่ผู้อ่านพร้อมกันจะเห็นไฟล์ที่ถูกตัดทอน หากกระบวนการถูกยกเลิกไม่ว่าด้วยเหตุผลใด ๆ (ctrl-c, OOM killer, ระบบขัดข้อง, ไฟดับ ฯลฯ ) ระหว่างการเขียนไฟล์ที่ถูกตัดทอนก็จะถูกทิ้งไว้ซึ่งอาจเป็นภัยพิบัติได้ นี่คือสถานการณ์จำลอง dataloss ที่นักพัฒนาต้องพิจารณาเพราะมันจะเกิดขึ้น ด้วยเหตุนี้ฉันจึงคิดว่าคำตอบที่ได้รับการยอมรับน่าจะไม่ใช่คำตอบที่ยอมรับ อย่างน้อยที่สุดให้เขียนลงใน tempfile และย้าย / เปลี่ยนชื่อไฟล์ให้เข้าที่เหมือนคำตอบ "ง่ายๆ" ที่ท้ายคำตอบนี้

คุณต้องใช้อัลกอริทึมที่:

  1. อ่านไฟล์เก่าและเขียนไปยังไฟล์ใหม่ (คุณต้องระวังเกี่ยวกับการแปลงไฟล์ทั้งหมดในหน่วยความจำ)

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

  3. แก้ไขการอนุญาตไฟล์และโหมดในไฟล์ใหม่

  4. เปลี่ยนชื่อไฟล์ใหม่และวางลงในตำแหน่ง

ด้วยระบบไฟล์ ext3 คุณรับประกันได้ว่าการเขียนข้อมูลเมตาเพื่อย้ายไฟล์เข้าที่จะไม่ถูกจัดเรียงใหม่โดยระบบไฟล์และเขียนก่อนที่จะเขียนบัฟเฟอร์ข้อมูลสำหรับไฟล์ใหม่ดังนั้นสิ่งนี้ควรสำเร็จหรือล้มเหลว ระบบไฟล์ ext4 ยังได้รับการแก้ไขเพื่อรองรับลักษณะการทำงานประเภทนี้ หากคุณหวาดระแวงมากควรเรียกfdatasync()ระบบเรียกตามขั้นตอน 3.5 ก่อนที่จะย้ายไฟล์เข้าที่

นี่คือแนวทางปฏิบัติที่ดีที่สุดโดยไม่คำนึงถึงภาษา ในภาษาที่การโทรclose()ไม่ทำให้เกิดข้อยกเว้น (Perl หรือ C) คุณต้องตรวจสอบการส่งคืนclose()ข้อยกเว้นอย่างชัดเจนหากล้มเหลว

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

การพิจารณาขั้นสุดท้ายคือการจัดวางไฟล์ชั่วคราว หากคุณเปิดไฟล์ใน / tmp คุณต้องพิจารณาปัญหาเล็กน้อย:

  • หากติดตั้ง / tmp บนระบบไฟล์อื่นคุณอาจเรียกใช้ / tmp ไม่เพียงพอก่อนที่คุณจะเขียนไฟล์ที่จะนำไปใช้กับปลายทางของไฟล์เก่าได้

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

    สิ่งนี้ไม่มีส่วนเกี่ยวข้องกับการนำไปใช้ของ Ruby ระบบmvและcpคำสั่งทำงานคล้ายกัน

สิ่งที่ดีกว่าคือเปิด Tempfile ในไดเร็กทอรีเดียวกับไฟล์เก่า เพื่อให้แน่ใจว่าจะไม่มีปัญหาการย้ายข้ามอุปกรณ์ mvตัวเองไม่ควรล้มเหลวและคุณก็ควรจะได้รับไฟล์ที่สมบูรณ์และ untruncated ควรพบข้อผิดพลาดใด ๆ เช่นอุปกรณ์ไม่มีพื้นที่ว่างข้อผิดพลาดการอนุญาต ฯลฯ ในระหว่างการเขียน Tempfile out

ข้อเสียเพียงประการเดียวของวิธีการสร้าง Tempfile ในไดเร็กทอรีปลายทางคือ:

  • บางครั้งคุณอาจไม่สามารถเปิด Tempfile ที่นั่นได้เช่นหากคุณกำลังพยายาม 'แก้ไข' ไฟล์ใน / proc ด้วยเหตุนี้คุณอาจต้องการถอยกลับและลอง / tmp หากการเปิดไฟล์ในไดเร็กทอรีปลายทางล้มเหลว
  • คุณต้องมีพื้นที่เพียงพอบนพาร์ติชันปลายทางเพื่อเก็บทั้งไฟล์เก่าและไฟล์ใหม่ อย่างไรก็ตามหากคุณมีพื้นที่ไม่เพียงพอที่จะเก็บสำเนาทั้งสองสำเนาคุณอาจมีเนื้อที่ดิสก์สั้นและความเสี่ยงที่แท้จริงในการเขียนไฟล์ที่ถูกตัดทอนนั้นสูงกว่ามากดังนั้นฉันจะโต้แย้งว่านี่เป็นการแลกเปลี่ยนที่ไม่ดีมากนอกบางส่วนที่แคบเกินไป (และดี -monitored) ขอบกรณี

นี่คือรหัสบางส่วนที่ใช้อัลกอริทึมแบบเต็ม (รหัส windows ยังไม่ทดสอบและยังไม่เสร็จ):

#!/usr/bin/env ruby

require 'tempfile'

def file_edit(filename, regexp, replacement)
  tempdir = File.dirname(filename)
  tempprefix = File.basename(filename)
  tempprefix.prepend('.') unless RUBY_PLATFORM =~ /mswin|mingw|windows/
  tempfile =
    begin
      Tempfile.new(tempprefix, tempdir)
    rescue
      Tempfile.new(tempprefix)
    end
  File.open(filename).each do |line|
    tempfile.puts line.gsub(regexp, replacement)
  end
  tempfile.fdatasync unless RUBY_PLATFORM =~ /mswin|mingw|windows/
  tempfile.close
  unless RUBY_PLATFORM =~ /mswin|mingw|windows/
    stat = File.stat(filename)
    FileUtils.chown stat.uid, stat.gid, tempfile.path
    FileUtils.chmod stat.mode, tempfile.path
  else
    # FIXME: apply perms on windows
  end
  FileUtils.mv tempfile.path, filename
end

file_edit('/tmp/foo', /foo/, "baz")

และนี่คือเวอร์ชันที่เข้มงวดกว่าเล็กน้อยซึ่งไม่ต้องกังวลกับทุกกรณีขอบที่เป็นไปได้ (หากคุณใช้ Unix และไม่สนใจที่จะเขียนถึง / proc):

#!/usr/bin/env ruby

require 'tempfile'

def file_edit(filename, regexp, replacement)
  Tempfile.open(".#{File.basename(filename)}", File.dirname(filename)) do |tempfile|
    File.open(filename).each do |line|
      tempfile.puts line.gsub(regexp, replacement)
    end
    tempfile.fdatasync
    tempfile.close
    stat = File.stat(filename)
    FileUtils.chown stat.uid, stat.gid, tempfile.path
    FileUtils.chmod stat.mode, tempfile.path
    FileUtils.mv tempfile.path, filename
  end
end

file_edit('/tmp/foo', /foo/, "baz")

กรณีการใช้งานที่เรียบง่ายจริงๆสำหรับเมื่อคุณไม่สนใจเกี่ยวกับสิทธิ์ระบบไฟล์ (คุณไม่ได้ใช้งานในฐานะรูทหรือคุณกำลังใช้งานในฐานะรูทและไฟล์นั้นเป็นของรูท)

#!/usr/bin/env ruby

require 'tempfile'

def file_edit(filename, regexp, replacement)
  Tempfile.open(".#{File.basename(filename)}", File.dirname(filename)) do |tempfile|
    File.open(filename).each do |line|
      tempfile.puts line.gsub(regexp, replacement)
    end
    tempfile.close
    FileUtils.mv tempfile.path, filename
  end
end

file_edit('/tmp/foo', /foo/, "baz")

TL; DR : ควรใช้แทนคำตอบที่ยอมรับเป็นอย่างน้อยในทุกกรณีเพื่อให้แน่ใจว่าการอัปเดตเป็นแบบอะตอมและโปรแกรมอ่านพร้อมกันจะไม่เห็นไฟล์ที่ถูกตัดทอน ดังที่ฉันได้กล่าวไว้ข้างต้นการสร้าง Tempfile ในไดเร็กทอรีเดียวกันกับไฟล์ที่แก้ไขเป็นสิ่งสำคัญที่นี่เพื่อหลีกเลี่ยงการดำเนินการ mv ข้ามอุปกรณ์ที่ถูกแปลเป็นการดำเนินการ cp หาก / tmp ติดตั้งบนอุปกรณ์อื่น การเรียก fdatasync เป็นการเพิ่มชั้นของความหวาดระแวง แต่มันจะได้รับผลกระทบด้านประสิทธิภาพดังนั้นฉันจึงละเว้นจากตัวอย่างนี้เนื่องจากไม่ได้รับการฝึกฝนโดยทั่วไป


แทนที่จะเปิดไฟล์ temp ในไดเร็กทอรีที่คุณอยู่มันจะสร้างขึ้นมาโดยอัตโนมัติในไดเรกทอรีข้อมูลแอพ (บน Windows แต่อย่างใด) และจากไฟล์เหล่านั้นคุณสามารถทำ file.unlink เพื่อลบมัน ..
13aal

3
ฉันชื่นชมความคิดพิเศษที่ใส่ลงไปในนี้ ในฐานะผู้เริ่มต้นเป็นเรื่องที่น่าสนใจมากที่จะได้เห็นรูปแบบความคิดของนักพัฒนาที่มีประสบการณ์ซึ่งไม่เพียงแค่ตอบคำถามเดิม แต่ยังแสดงความคิดเห็นในบริบทที่กว้างขึ้นของความหมายของคำถามเดิมด้วย
ramijames

การเขียนโปรแกรมไม่ได้เป็นเพียงแค่การแก้ไขปัญหาเฉพาะหน้าเท่านั้น แต่ยังเป็นการคิดล่วงหน้าเพื่อหลีกเลี่ยงปัญหาอื่น ๆ ที่รออยู่ ไม่มีสิ่งใดที่จะทำให้นักพัฒนารุ่นพี่ต้องหงุดหงิดได้มากไปกว่าการเจอโค้ดที่วาดอัลกอริทึมเข้ามุมบังคับให้กากตะกอนที่น่าอึดอัดเมื่อการปรับเล็กน้อยก่อนหน้านี้จะส่งผลให้เกิดกระแสที่ดี มักใช้เวลาหลายชั่วโมงหรือหลายวันในการวิเคราะห์เพื่อทำความเข้าใจเป้าหมายจากนั้นบรรทัดสองสามบรรทัดจะแทนที่หน้าโค้ดเดิม มันเหมือนกับเกมหมากรุกกับข้อมูลและระบบในบางครั้ง
The Tin Man

11

ไม่มีวิธีแก้ไขไฟล์ในสถานที่จริงๆ สิ่งที่คุณมักจะทำเมื่อคุณสามารถหลีกเลี่ยงมันได้ (เช่นถ้าไฟล์ไม่ใหญ่เกินไป) คือคุณอ่านไฟล์ลงในหน่วยความจำ ( File.read) ทำการแทนที่ของคุณในสตริงการอ่าน ( String#gsub) จากนั้นเขียนสตริงที่เปลี่ยนแปลงกลับไปที่ ไฟล์ ( File.open, File#write).

หากไฟล์มีขนาดใหญ่พอที่จะทำไม่ได้สิ่งที่คุณต้องทำคืออ่านไฟล์เป็นชิ้น ๆ (หากรูปแบบที่คุณต้องการแทนที่ไม่ครอบคลุมหลายบรรทัดจากนั้นหนึ่งกลุ่มมักจะหมายถึงหนึ่งบรรทัด - คุณสามารถใช้File.foreachเพื่อ อ่านไฟล์ทีละบรรทัด) และสำหรับแต่ละกลุ่มให้ทำการแทนที่และต่อท้ายไฟล์ชั่วคราว เมื่อคุณทำซ้ำบนไฟล์ต้นฉบับเสร็จแล้วให้ปิดและใช้FileUtils.mvเพื่อเขียนทับไฟล์ชั่วคราว


1
ฉันชอบแนวทางการสตรีม เราจัดการกับไฟล์ขนาดใหญ่ไปพร้อม ๆ กันดังนั้นเราจึงไม่มีพื้นที่ว่างใน RAM เพื่ออ่านไฟล์ทั้งหมด
เชน

" เหตุใดไฟล์" slurping "จึงไม่ใช่แนวทางปฏิบัติที่ดี " อาจเป็นประโยชน์ในการอ่านเกี่ยวกับเรื่องนี้
The Tin Man

9

อีกวิธีหนึ่งคือการใช้การแก้ไขแบบแทนที่ภายใน Ruby (ไม่ใช่จากบรรทัดคำสั่ง):

#!/usr/bin/ruby

def inplace_edit(file, bak, &block)
    old_stdout = $stdout
    argf = ARGF.clone

    argf.argv.replace [file]
    argf.inplace_mode = bak
    argf.each_line do |line|
        yield line
    end
    argf.close

    $stdout = old_stdout
end

inplace_edit 'test.txt', '.bak' do |line|
    line = line.gsub(/search1/,"replace1")
    line = line.gsub(/search2/,"replace2")
    print line unless line.match(/something/)
end

หากคุณไม่ต้องการที่จะสร้างการสำรองข้อมูลแล้วเปลี่ยนไป'.bak'''


1
สิ่งนี้จะดีกว่าการพยายาม slurp ( read) ไฟล์ มันปรับขนาดได้และควรจะเร็วมาก
คนดีบุก

มีข้อผิดพลาดที่ทำให้ Ruby 2.3.0p0 บน Windows ล้มเหลวโดยปฏิเสธการอนุญาตหากมีบล็อก inplace_edit หลายบล็อกที่ทำงานในไฟล์เดียวกัน ในการสร้างการทดสอบแยกการค้นหา 1 และการค้นหา 2 ออกเป็น 2 บล็อก ปิดไม่สนิท?
mlt

ฉันคาดว่าจะมีปัญหากับการแก้ไขไฟล์ข้อความหลายไฟล์พร้อมกัน หากไม่มีอะไรอื่นคุณอาจได้รับไฟล์ข้อความที่ยุ่งเหยิง
The Tin Man

7

สิ่งนี้ใช้ได้กับฉัน:

filename = "foo"
text = File.read(filename) 
content = text.gsub(/search_regexp/, "replacestring")
File.open(filename, "w") { |file| file << content }

6

นี่คือวิธีการค้นหา / แทนที่ในไฟล์ทั้งหมดของไดเร็กทอรีที่กำหนด โดยพื้นฐานแล้วฉันเอาคำตอบจาก sepp2k และขยายมัน

# First set the files to search/replace in
files = Dir.glob("/PATH/*")

# Then set the variables for find/replace
@original_string_or_regex = /REGEX/
@replacement_string = "STRING"

files.each do |file_name|
  text = File.read(file_name)
  replace = text.gsub!(@original_string_or_regex, @replacement_string)
  File.open(file_name, "w") { |file| file.puts replace }
end

4
require 'trollop'

opts = Trollop::options do
  opt :output, "Output file", :type => String
  opt :input, "Input file", :type => String
  opt :ss, "String to search", :type => String
  opt :rs, "String to replace", :type => String
end

text = File.read(opts.input)
text.gsub!(opts.ss, opts.rs)
File.open(opts.output, 'w') { |f| f.write(text) }

2
จะช่วยได้มากขึ้นหากคุณให้คำอธิบายว่าเหตุใดจึงเป็นโซลูชันที่ต้องการและอธิบายวิธีการทำงาน เราต้องการให้ความรู้ไม่ใช่แค่ให้รหัส
คนดีบุก

หญิงโสเภณีถูกเปลี่ยนชื่อ optimist github.com/manageiq/optimist นอกจากนี้ยังเป็นเพียงตัวแยกวิเคราะห์ตัวเลือก CLI ที่ไม่จำเป็นต้องตอบคำถามจริงๆ
noraj

1

หากคุณจำเป็นต้องทำการแทนที่ข้ามขอบเขตของบรรทัดการใช้ruby -pi -eจะไม่ได้ผลเนื่องจากการpประมวลผลทีละบรรทัด ฉันขอแนะนำสิ่งต่อไปนี้แทนแม้ว่าอาจล้มเหลวด้วยไฟล์หลาย GB:

ruby -e "file='translation.ja.yml'; IO.write(file, (IO.read(file).gsub(/\s+'$/, %q('))))"

กำลังมองหาพื้นที่สีขาว (อาจรวมถึงบรรทัดใหม่) ตามด้วยเครื่องหมายคำพูดซึ่งในกรณีนี้มันจะกำจัดช่องว่าง วิธี%q(')นี้เป็นเพียงวิธีแฟนซีในการอ้างถึงอักขระเครื่องหมายคำพูด


1

นี่เป็นอีกทางเลือกหนึ่งของซับจาก jim คราวนี้เป็นสคริปต์

ARGV[0..-3].each{|f| File.write(f, File.read(f).gsub(ARGV[-2],ARGV[-1]))}

บันทึกไว้ในสคริปต์เช่น replace.rb

คุณเริ่มต้นในบรรทัดคำสั่งด้วย

replace.rb *.txt <string_to_replace> <replacement>

* .txt สามารถแทนที่ด้วยการเลือกอื่นหรือด้วยชื่อไฟล์หรือเส้นทางบางส่วน

แยกย่อยออกเพื่อที่ฉันจะได้อธิบายว่าเกิดอะไรขึ้น แต่ยังสามารถใช้งานได้

# ARGV is an array of the arguments passed to the script.
ARGV[0..-3].each do |f| # enumerate the arguments of this script from the first to the last (-1) minus 2
  File.write(f,  # open the argument (= filename) for writing
    File.read(f) # open the argument (= filename) for reading
    .gsub(ARGV[-2],ARGV[-1])) # and replace all occurances of the beforelast with the last argument (string)
end

แก้ไข: หากคุณต้องการใช้นิพจน์ทั่วไปให้ใช้สิ่งนี้แทนแน่นอนว่านี่ใช้สำหรับจัดการไฟล์ข้อความที่ค่อนข้างเล็กเท่านั้นไม่มีมอนสเตอร์ Gigabyte

ARGV[0..-3].each{|f| File.write(f, File.read(f).gsub(/#{ARGV[-2]}/,ARGV[-1]))}

รหัสนี้ใช้ไม่ได้ ฉันขอแนะนำให้ทดสอบก่อนโพสต์จากนั้นคัดลอกและวางโค้ดที่ใช้งานได้
คนดีบุก

@theTinMan ฉันมักจะทดสอบก่อนเผยแพร่ถ้าเป็นไปได้ ฉันทดสอบแล้วและใช้งานได้ทั้งแบบสั้นตามเวอร์ชันที่แสดงความคิดเห็น ทำไมคุณถึงคิดว่ามันจะไม่?
เตอร์

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