วิธีการค้นหา / แทนที่สตริงแบบเรียกซ้ำด้วย awk หรือ sed?


675

ฉันจะค้นหาและแทนที่ทุกเหตุการณ์ที่เกิดขึ้นได้อย่างไร:

subdomainA.example.com

กับ

subdomainB.example.com

ในทุกไฟล์ข้อความภายใต้/home/www/ต้นไม้ไดเรกทอรีซ้ำซ้ำ?


93
เคล็ดลับ: อย่าทำด้านล่างในแผนผังการชำระเงิน svn ... มันจะเขียนทับไฟล์โฟลเดอร์. svn magic
J. Polfer

7
โอ้พระเจ้าของฉันนี่คือสิ่งที่ฉันเพิ่งทำ แต่มันใช้งานได้และดูเหมือนจะไม่ได้ทำอันตรายใด ๆ สิ่งที่เลวร้ายที่สุดที่อาจเกิดขึ้นคืออะไร?
J. Katzwinkel

5
@ J.Katzwinkel: อย่างน้อยที่สุดมันอาจเช็คซัมเสียหายซึ่งอาจทำให้ที่เก็บของคุณเสียหาย
ninjagecko

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

5
คุณสามารถยกเว้นไดเรกทอรีเช่นคอมไพล์จากผลการโดยใช้-path ./.git -prune -oในfind . -path ./.git -prune -o -type f -name '*matchThisText*' -print0ก่อนที่จะท่อ xargs
devinbost

คำตอบ:


850
find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

-print0บอกfindให้พิมพ์แต่ละผลลัพธ์คั่นด้วยอักขระ null แทนที่จะเป็นบรรทัดใหม่ ในกรณีที่ไดเรกทอรีของคุณมีไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อสิ่งนี้ยังช่วยให้สามารถxargsทำงานกับชื่อไฟล์ที่ถูกต้องได้

\( -type d -name .git -prune \).gitคือการแสดงออกที่สมบูรณ์ข้ามมากกว่าไดเรกทอรีทั้งหมดชื่อ คุณสามารถขยายได้อย่างง่ายดายหากคุณใช้ SVN หรือมีโฟลเดอร์อื่น ๆ ที่คุณต้องการเก็บไว้ - เพียงเทียบกับชื่ออื่น ๆ มันเทียบเท่ากับ-not -path .gitแต่มีประสิทธิภาพมากกว่าเพราะแทนที่จะตรวจสอบทุกไฟล์ในไดเรกทอรีมันจะข้ามไปทั้งหมด -oหลังจากที่มันถูกต้องเนื่องจากวิธีการ-pruneใช้งานได้จริง

man findสำหรับข้อมูลเพิ่มเติมโปรดดูที่


132
บน OSX คุณอาจประสบsed: 1: "...": invalid command code .ปัญหา ดูเหมือนว่าตัวเลือก -i คาดว่าส่วนขยายและแยกวิเคราะห์'s/../...'คำสั่ง การแก้ไข: ผ่านการขยาย '' ตัวเลือก -i sed -i '' 's/...เช่น
Robert Lujo

6
หมายเหตุ: หากคุณใช้สิ่งนี้เหนือไดเรกทอรีและสงสัยว่าทำไมsvn stไม่มีการเปลี่ยนแปลงนั่นเป็นเพราะคุณได้แก้ไขไฟล์ในไดเรกทอรี. svn ด้วยเช่นกัน! ใช้find . -maxdepth 1 -type f -print0 | xargs -0 sed -i 's/toreplace/replaced/g'แทน
ACK_stoverflow

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

13
ใช้สิ่งนี้grep -r 'hello' -l --null . | xargs -0 sed -i 's#hello#world#g'เพื่อหลีกเลี่ยงการแก้ไขไฟล์ที่ไม่เกี่ยวข้อง (sed อาจเปลี่ยนการเข้ารหัสไฟล์)
caiguanhao

6
"แต่กลับทำให้ดัชนีคอมไพล์ของฉันเสียหาย" ไม่ต้องกังวลเกี่ยวกับเรื่องนี้มากนักคุณสามารถfind .git ... | ... 'sed -i s/(the opposite from before)/g'แก้ไขดัชนีคอมไพล์ได้
Massey101

259

หมายเหตุ : อย่าเรียกใช้คำสั่งนี้ในโฟลเดอร์รวมถึง git repo การเปลี่ยนแปลง. git อาจทำให้ดัชนี git ของคุณเสียหาย

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

เมื่อเทียบกับคำตอบอื่น ๆ ที่นี่สิ่งนี้ง่ายกว่ามากและใช้ sed แทน perl ซึ่งเป็นคำถามเดิมที่ถาม


50
โปรดทราบว่าหากคุณใช้ BSD sed (รวมถึงใน Mac OS X) คุณจะต้องระบุสตริงว่างที่ชัดเจนเพื่อแย้งกับ-iตัวเลือกของ sed เช่น: sed -i '' 's/original/replacement/g'
Nathan Craike

2
@JohnZwinck ความผิดพลาดของฉันพลาด + แปลกที่โซลูชันของ Nikita นั้นทำงานได้เร็วกว่าสำหรับฉัน
Sam

6
@AoeAoe: การ+ลดจำนวนsedโพรเซสที่เกิดขึ้นอย่างมาก มันมีประสิทธิภาพมากกว่า
John Zwinck

4
ฉันจะทำสิ่งนี้อย่างปลอดภัยในโฟลเดอร์ที่มี repo git ได้อย่างไร?
Hatshepsut

20
มันปลอดภัยที่จะดำเนินการเกี่ยวกับโฟลเดอร์ที่มี repo Git หากคุณยกเว้น repo find . -not -path '*/\.git*' -type f ...ออกจากผลการค้นหาของคุณ:
Dale Anderson

211

วิธีที่ง่ายที่สุดสำหรับฉันคือ

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'

1
@Anatoly: คำถามเดียว: ฉันจะแยกไฟล์ไบนารี(ไฟล์ที่ปฏิบัติการ) ได้อย่างไร?
2284570

3
@ user2284570 ใช้แฟล็ก-Iหรือ--binary-file=without-matchgrep
Zéychin

34
.svnนี้ทำงานได้ดีโดยเฉพาะเมื่อคุณจำเป็นต้องยกเว้นไดเรกทอรีเช่นเดียวกับ ตัวอย่างเช่น:grep -rl oldtext . --exclude-dir=.svn | xargs sed -i 's/oldtext/newtext/g'
phyatt

11
brew install gnu-sedและใช้งานgsedบน OSX เพื่อหลีกเลี่ยงโลกแห่งความเจ็บปวด
P ฉัน

1
พวกคุณโปรดให้ความสนใจถ้าโครงการของคุณมีเวอร์ชันคอมไพล์ให้ใช้สิ่งนี้แทน: git grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'. มันไม่ดีเลยที่จะ f * ck ขึ้น.gitdir ของคุณ
เปาโล

61

เทคนิคทั้งหมดเกือบจะเหมือนกัน แต่ฉันชอบสิ่งนี้:

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>: ค้นหาในไดเรกทอรี

  • -type f:

    ไฟล์เป็นประเภท: ไฟล์ปกติ

  • -exec command {} +:

    ตัวแปรของแอ็คชัน -exec นี้รันคำสั่งที่ระบุบนไฟล์ที่เลือก แต่บรรทัดคำสั่งถูกสร้างโดยการต่อท้ายชื่อไฟล์ที่เลือกแต่ละชื่อที่ท้าย จำนวนการร้องขอคำสั่งทั้งหมดจะน้อยกว่าจำนวนไฟล์ที่ตรงกัน บรรทัดคำสั่งสร้างขึ้นในลักษณะเดียวกับที่ xargs สร้างบรรทัดคำสั่ง อนุญาตเพียงหนึ่งอินสแตนซ์ของ `{} 'ภายในคำสั่ง คำสั่งจะถูกดำเนินการในไดเรกทอรีเริ่มต้น


@ user2284570 ด้วย -exec หรือไม่ ลองตั้งค่าเส้นทางให้สามารถเรียกใช้งานได้แทนชื่อเครื่องมือ
I159

@ I159: ไม่มี: ยกเว้นไบนารีปฏิบัติการ( แต่รวมถึงเชลล์สคริปต์)
user2284570

8
@ I159 คำตอบนี้ไม่เหมือนกับของJohn Zwinckใช่ไหม
Reinstate Monica โปรด

1
@ user2284570 แนวคิดของ "ไฟล์ไบนารี" ไม่ได้กำหนดไว้อย่างสมบูรณ์ คุณสามารถใช้fileคำสั่งเพื่อพยายามกำหนดประเภทของไฟล์แต่ละไฟล์ แต่รูปแบบของ haphazard ในเอาต์พุตอาจมีความสับสนเล็กน้อย -I(aka --mime) ตัวเลือกที่ช่วยให้บ้างหรือ--mime-typeถ้าคุณมีว่า วิธีการที่จะปรับโครงสร้างให้เป็นหนึ่งเดียวอย่างประณีตเพื่อทำสิ่งนี้นั้นน่าเสียใจนอกขอบเขตสำหรับกล่องแสดงความคิดเห็นเล็ก ๆ นี้ อาจโพสต์คำถามแยกต่างหากหากคุณต้องการความช่วยเหลือ (อาจจะเพิ่มความคิดเห็นด้วยลิงค์ไปที่นี่แล้ว)
tripleee

1
คำตอบที่สะอาดที่สุด! ขอบคุณเพื่อน
jukerok

39
cd /home/www && find . -type f -print0 |
  xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

2
ฉันอยากรู้อยากเห็นมีเหตุผลที่จะใช้-print0และxargsแทนที่จะเป็น-execหรือ-execdirไม่?
ฟิลิปป์

4
มี: จาก "man find": คำสั่งที่ระบุจะรันหนึ่งครั้งสำหรับแต่ละไฟล์ที่ตรงกัน นั่นคือหากมีไฟล์ 2,000 ไฟล์ใน / home / www ดังนั้น 'find ... -exec ... ' จะส่งผลให้มีการเรียกใช้ perl 2000 ครั้ง ในขณะที่ 'ค้นหา ... | xargs ... 'จะเรียกใช้ Perl เพียงครั้งเดียวหรือสองครั้ง (สมมติว่า ARG_MAX ประมาณ 32K และความยาวชื่อไฟล์เฉลี่ยเท่ากับ 20)
ลูกจ้างชาวรัสเซีย

2
@Employed Russian: นั่นเป็นเหตุผลที่คุณจะใช้find -exec command {} +- มันจะหลีกเลี่ยงการเรียกใช้คำสั่งที่มากเกินไปเช่น xargs แต่ไม่มีกระบวนการแยกต่างหาก
John Zwinck

2
บนแพลตฟอร์มใด โซลูชัน xargs เป็นแบบพกพาการเรียกใช้ "magic" ของ "find ... -exec" ซึ่งไม่เรียกใช้กระบวนการย่อยสำหรับทุกไฟล์ที่พบไม่ใช่
ลูกจ้างชาวรัสเซีย

4
@EmployedRussian find -exec ... {} +ได้รับการระบุ POSIX ตั้งแต่ปี 2006
Charles Duffy

34

สำหรับฉันทางออกที่ง่ายที่สุดในการจำคือhttps://stackoverflow.com/a/2113224/565525นั่นคือ:

sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)

หมายเหตุ : -i ''แก้ปัญหา OSXsed: 1: "...": invalid command code .

หมายเหตุ : Argument list too longหากมีไฟล์จำนวนมากเกินไปที่จะดำเนินการคุณจะได้รับ วิธีแก้ปัญหา - ใช้find -execหรือxargsวิธีแก้ปัญหาที่อธิบายไว้ข้างต้น


4
workaroundควรจะเป็นไวยากรณ์ที่แนะนำในทุกกรณี
Reinstate Monica โปรด

1
ปัญหาเกี่ยวกับการทดแทนคำสั่ง$(find...)คือไม่มีวิธีที่เชลล์จะจัดการกับชื่อไฟล์ด้วยช่องว่างหรือ metacharacters เชลล์อื่น ๆ ในพวกเขา หากคุณรู้ว่านี่ไม่ใช่ปัญหาวิธีนี้ก็ใช้ได้ แต่เรามี waaaay คำถามมากเกินไปที่ผู้คนไม่ได้รับการเตือนเกี่ยวกับปัญหานี้หรือไม่เข้าใจคำเตือน
tripleee

30

สำหรับทุกคนที่ใช้เครื่องมือค้นหาเงิน ( ag)

ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

เนื่องจาก ag ละเว้นไฟล์ / โฟลเดอร์ git / hg / svn ตามค่าเริ่มต้นจึงปลอดภัยที่จะเรียกใช้ภายในที่เก็บ


16

ออนไลเนอร์ที่ดีคนหนึ่งเป็นพิเศษ ใช้ grep greit

git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"

3
ความคิดที่ดีถ้าทำงานภายใน repit คอมไพล์ในขณะที่คุณไม่เสี่ยงต่อการเขียนทับ. git / เนื้อหา (ตามที่รายงานไว้ในความคิดเห็นเพื่อคำตอบอื่น)
mahemoff

1
ขอบคุณฉันใช้มันเป็นฟังก์ชั่นการทุบตีrefactor() { echo "Replacing $1 by $2 in all files in this git repository." git grep -lz $1| xargs -0 perl -i'' -pE "s/$1/$2/g" }การใช้งานตัวอย่างเช่นแทนที่ 'คำว่า' กับ 'ดาบ': จากนั้นตรวจสอบสิ่งที่มันทำกับrefactor word sword git diff
Paul Rougieux

16

หากต้องการลดไฟล์ให้วนซ้ำsedคุณสามารถทำได้grepกับอินสแตนซ์สตริงของคุณ:

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

หากคุณเรียกใช้man grepคุณจะสังเกตเห็นว่าคุณสามารถกำหนด--exlude-dir="*.git"ค่าสถานะได้หากคุณต้องการละเว้นการค้นหาผ่านไดเรกทอรี. git หลีกเลี่ยงปัญหาดัชนี git เนื่องจากคนอื่น ๆ ชี้ไปอย่างสุภาพ

พาคุณไปที่:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

13

อันนี้เข้ากันได้กับที่เก็บ git และง่ายขึ้นเล็กน้อย:

ลินุกซ์:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'

Mac:

git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'

(ขอบคุณhttp://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/ )


ฉลาดที่จะใช้git-grepของตัวเลือกร่วมกับ-z xargs -0
gniourf_gniourf

git grepเห็นได้ชัดว่าเหมาะสมในgitrepo เท่านั้น grep -rทดแทนโดยทั่วไปจะเป็น
tripleee

@gniourf_gniourf คุณอธิบายได้ไหม
Petr Peller

2
@PetrPeller ด้วย-z, git-grepจะแยกเขตการส่งออกโดย null ไบต์แทนการขึ้นบรรทัดใหม่; และด้วย-0, xargsจะอ่านการป้อนข้อมูลแยกจากกันโดยไบต์ null แทนช่องว่าง (และไม่ได้ทำสิ่งแปลก ๆ กับคำพูด) ดังนั้นหากคุณไม่ต้องการให้คำสั่งที่จะทำลายถ้าชื่อไฟล์มีการเว้นวรรคคำพูดหรือตัวตลกอื่น ๆ git grep -z -l 'original_text' | xargs -0 sed ...คำสั่งคือ:
gniourf_gniourf

10
find /home/www/ -type f -exec perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

find /home/www/ -type f จะแสดงรายการไฟล์ทั้งหมดใน / home / www / (และไดเรกทอรีย่อย) แฟล็ก "-exec" บอกให้ find รันคำสั่งต่อไปนี้ในแต่ละไฟล์ที่พบ

perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

เป็นคำสั่งที่รันบนไฟล์ (หลาย ๆ ไฟล์ในเวลาเดียวกัน) {}ได้รับการแทนที่ด้วยชื่อไฟล์ +ในตอนท้ายของคำสั่งบอกfindในการสร้างหนึ่งคำสั่งชื่อไฟล์จำนวนมาก

ต่อ findหน้า man: "บรรทัดคำสั่งถูกสร้างในลักษณะเดียวกับที่ xargs สร้างบรรทัดคำสั่ง"

ดังนั้นมันจึงเป็นไปได้ที่จะบรรลุเป้าหมายของคุณ (และชื่อไฟล์การจัดการที่มีช่องว่าง) โดยไม่ต้องใช้หรือxargs -0-print0


8

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

cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

Ack-grep มีประสิทธิภาพมากในการค้นหาไฟล์ที่เกี่ยวข้อง คำสั่งนี้แทนที่ไฟล์ประมาณ 145,000 ไฟล์ด้วยความง่ายในขณะที่คนอื่นใช้เวลานานมากฉันจึงอดใจรอไม่ไหว


ดี แต่ไม่มีที่ไหนเลยใกล้เป็นอย่างรวดเร็วgrep -ril 'subdomainA' * grep -Hr 'subdomainA' * | cut -d: -f1
trusktr

@Henno: คำถามเดียว: ฉันจะแยกไฟล์ไบนารี(ไฟล์ปฏิบัติการ) ได้อย่างไร
user2284570

ack-grep ทำสิ่งนั้นให้คุณโดยอัตโนมัติ
Henno

@Henno: มันรวมเชลล์สคริปต์หรือไม่
user2284570

ใช่. นี่คือรายการประเภทไฟล์ที่รองรับทั้งหมด: Beyondgrep.com/documentation
Henno

6

วิธีการส่งตรงหากคุณต้องการแยกไดเรกทอรี ( --exclude-dir=.svn) และอาจมีชื่อไฟล์ที่มีช่องว่าง (ใช้ 0Byte ด้วยgrep -Zและxargs -0

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'

6

วิธีที่ง่ายที่สุดในการแทนที่ ( ไฟล์ทั้งหมด, ไดเรกทอรี, แบบเรียกซ้ำ )

find . -type f -not -path '*/\.*' -exec sed -i 's/foo/bar/g' {} +

หมายเหตุ:บางครั้งคุณอาจต้องเพิกเฉยต่อไฟล์ที่ซ่อนอยู่นั่นคือ.gitคุณสามารถใช้คำสั่งด้านบน

หากคุณต้องการรวมไฟล์ที่ซ่อนอยู่ให้ใช้

find . -type f  -exec sed -i 's/foo/bar/g' {} +

ในทั้งสองกรณีสตริงfooจะถูกแทนที่ด้วยสตริงใหม่bar


5

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

ฉันเดาว่าคนส่วนใหญ่ไม่รู้ว่าพวกเขาสามารถทำให้บางสิ่งบางอย่างกลายเป็น "ในขณะที่อ่านไฟล์" และมันจะหลีกเลี่ยงสิ่งที่น่ารังเกียจ -print0 args ในขณะที่ presevering ช่องว่างในชื่อไฟล์

การเพิ่มechoก่อนหน้า sed ช่วยให้คุณเห็นไฟล์ที่จะเปลี่ยนแปลงก่อนที่จะทำจริง


เหตุผล-print0ก็คือมีประโยชน์เพราะมันจัดการกับกรณีที่while readไม่สามารถจัดการได้ - การขึ้นบรรทัดใหม่เป็นอักขระที่ถูกต้องในชื่อไฟล์ Unix ดังนั้นเพื่อให้รหัสของคุณมีความสมบูรณ์อย่างสมบูรณ์ (นอกจากนี้คุณต้องการread -rหลีกเลี่ยงพฤติกรรมแบบดั้งเดิมที่น่ารำคาญของ POSIX readด้วย)
tripleee

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

5

คุณสามารถใช้ awk เพื่อแก้ปัญหานี้ได้ดังนี้

for file in `find /home/www -type f`
do
   awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file;
done

หวังว่านี่จะช่วยคุณได้ !!!


ใช้งานได้บน MacOs โดยไม่มีปัญหาใด ๆ ! sedคำสั่งพื้นฐานทั้งหมดล้มเหลวเมื่อมีการรวมไบนารีแม้จะมีการตั้งค่าเฉพาะ osx
Jankapunkt

ระวัง ... สิ่งนี้จะระเบิดหากไฟล์ใด ๆ ที่findส่งคืนมีช่องว่างในชื่อ! มันปลอดภัยกว่าการใช้งานมาก ๆwhile read: stackoverflow.com/a/9612560/1938956
Soren

4

ลองสิ่งนี้:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`

1
สวัสดี @RikHic คำแนะนำดี ๆ - กำลังคิดเรื่องนี้อยู่ โชคไม่ดีที่การจัดรูปแบบด้านบนค่อนข้างไม่ถูกต้อง :) ดังนั้นฉันจะลองใช้ pre tag (ใช้งานไม่ได้) - ดังนั้นด้วยการหลีกเลี่ยง backticks ในตอนนั้น: sed -i 's/subdomainA/subdomainB/g'` grep -ril 'subdomainA' /home/www/*` - สิ่งนี้ยังดูไม่ดีเกินไป แต่ควร เอาตัวรอดจากยาสีฟัน :) ไชโย!
sdaau

4
#!/usr/local/bin/bash -x

find * /home/www -type f | while read files
do

sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')

    if [ "${sedtest}" ]
    then
    sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp
    mv "${files}".tmp "${files}"
    fi

done

4

ตามโพสต์บล็อกนี้ :

find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'

คุณหนีสแลชได้/อย่างไร. ตัวอย่างเช่นฉันต้องการแทนที่ที่อยู่ IP: xxx.xxx.xxx.xxxสำหรับxxx.xxx.xxx.xxx/folder
Pathros

คุณสามารถหลบหนี/ด้วย \ ตัวอย่างเช่น:find . -type f | xargs perl -pi -e 's/xxx.xxx.xxx.xxx\/folder/newtext/g;'
J.Hpour

3

หากคุณไม่คำนึงถึงการใช้งานvimร่วมกับgrepหรือfindเครื่องมือคุณสามารถติดตามคำตอบที่ได้รับจากผู้ใช้ Gert ในลิงค์นี้ -> จะทำการแทนที่ข้อความในลำดับชั้นของโฟลเดอร์ขนาดใหญ่ได้อย่างไร? .

นี่คือข้อตกลง:

  • grep ซ้ำแล้วซ้ำอีกสำหรับสตริงที่คุณต้องการแทนที่ในเส้นทางที่แน่นอนและใช้เส้นทางที่สมบูรณ์ของไฟล์ที่ตรงกัน $(grep 'string' 'pathname' -Rl)(ที่จะเป็น

  • (ไม่บังคับ) หากคุณต้องการทำการสำรองข้อมูลล่วงหน้าของไฟล์เหล่านั้นในไดเรกทอรีส่วนกลางคุณอาจใช้สิ่งนี้ด้วย: cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'

  • หลังจากนั้นคุณสามารถแก้ไข / แทนที่ at ในvimรูปแบบที่คล้ายกับที่ให้ไว้ในลิงค์ที่ให้:

    • :bufdo %s#string#replacement#gc | update

2

โรงเรียนเก่าไปหน่อย แต่สิ่งนี้ใช้ได้กับ OS X

มีกลอุบายไม่กี่:

•จะแก้ไขไฟล์ที่มีนามสกุล.slsภายใต้ไดเรกทอรีปัจจุบันเท่านั้น

.ต้องถูกหลบหนีเพื่อให้แน่ใจsedว่าไม่ได้ประเมินว่าเป็น "ตัวละครใด ๆ "

,ใช้เป็นsedตัวคั่นแทนการใช้ตามปกติ/

นอกจากนี้โปรดทราบว่านี่คือการแก้ไขเทมเพลต Jinja เพื่อส่งต่อvariableในเส้นทางของimport(แต่นี่ไม่ใช่หัวข้อ)

ก่อนอื่นให้ตรวจสอบคำสั่ง sed ของคุณว่าคุณต้องการอะไร (สิ่งนี้จะพิมพ์การเปลี่ยนแปลงไปยัง stdout เท่านั้นมันจะไม่เปลี่ยนไฟล์):

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

แก้ไขคำสั่ง sed ตามต้องการเมื่อคุณพร้อมที่จะทำการเปลี่ยนแปลง:

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

หมายเหตุ-i ''ในคำสั่งsedฉันไม่ต้องการสร้างการสำรองข้อมูลของไฟล์ดั้งเดิม (ดังอธิบายในการแก้ไขแบบแทนที่ด้วย sed บน OS Xหรือในความคิดเห็นของ Robert Lujo ในหน้านี้)

มีความสุขคน seding!


2

เพียงเพื่อหลีกเลี่ยงการเปลี่ยนแปลงด้วย

  • NearlysubdomainA.example.com
  • subdomainA.example.comp.other

แต่ยังคง

  • subdomainA.example.com.IsIt.good

(อาจไม่ดีในความคิดที่อยู่เบื้องหลังรูทโดเมน)

find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;

2

ฉันแค่ใช้เสื้อ:

find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 |  xargs -0 tops -verbose  replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" \
replace "check(<b args>)" with "__Check(<args>)" 

บวกหนึ่งสำหรับ `'*. [c | cc | cp | cpp | m | mm | h]' '
FractalSpace

2

นี่คือรุ่นที่ควรจะกว้างกว่าส่วนใหญ่; ตัวอย่างเช่นไม่ต้องการfind(ใช้duแทน) มันต้องการxargsซึ่งจะพบได้ในแผนรุ่น 9 บางรุ่นเท่านั้น (เช่น 9 หน้า)

 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

หากคุณต้องการเพิ่มตัวกรองเช่นใช้นามสกุลไฟล์grep:

 du -a | grep "\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

1

สำหรับ Qshell (qsh) บน IBMi ห้ามทุบตีตามที่ติดแท็กโดย OP

ข้อ จำกัด ของคำสั่ง qsh:

  • ค้นหาไม่มีตัวเลือก -print0
  • xargs ไม่มีตัวเลือก -0
  • sed ไม่มีตัวเลือก -i

ดังนั้นการแก้ปัญหาใน qsh:

    PATH='your/path/here'
    SEARCH=\'subdomainA.example.com\'
    REPLACE=\'subdomainB.example.com\'

    for file in $( find ${PATH} -P -type f ); do

            TEMP_FILE=${file}.${RANDOM}.temp_file

            if [ ! -e ${TEMP_FILE} ]; then
                    touch -C 819 ${TEMP_FILE}

                    sed -e 's/'$SEARCH'/'$REPLACE'/g' \
                    < ${file} > ${TEMP_FILE}

                    mv ${TEMP_FILE} ${file}
            fi
    done

คำเตือน:

  • โซลูชันไม่รวมการจัดการข้อผิดพลาด
  • ไม่ทุบตีตามที่ติดแท็กโดย OP

forนี้มีปัญหาที่น่ารำคาญบางคนที่มีข้อความเช่นเดียวกับการอ่านบรรทัดด้วย
tripleee

1

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

find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'

วงเล็บดูเหมือนจะไม่จำเป็น ก่อนหน้านี้มีข้อผิดพลาดการจัดรูปแบบซึ่งทำให้ใช้ไม่ได้ (การเรนเดอร์ Markdown จะกินอักขระบางตัวจาก regex)
tripleee

1

ใช้การรวมกันของgrepและsed

for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g' "${pp}"
done

@tripleee ฉันแก้ไขสิ่งนี้เล็กน้อย ในกรณีนี้เอาต์พุตสำหรับgrep -Rl patternรายการคำสั่งที่สร้างขึ้นของไฟล์โดยที่ pattern อยู่ ไฟล์ไม่ได้อ่านforแบบวนซ้ำ
Pawel

ฮะ? คุณยังมีforห่วงอยู่ หากชื่อไฟล์ที่ส่งคืนมีช่องว่างมันจะไม่ทำงานอย่างถูกต้องเพราะเชลล์ tokenizes forรายการอาร์กิวเมนต์ แต่จากนั้นคุณใช้ตัวแปรชื่อไฟล์โดยไม่มีเครื่องหมายอัญประกาศอยู่ในลูปดังนั้นมันจะหยุดลงถ้าคุณแก้ไขสิ่งนี้ การแก้ไขข้อผิดพลาดที่เหลือเหล่านี้จะทำให้คุณเหมือนกับคำตอบของ @ MadMan2064
tripleee

@tripleee ใช่นั่นเป็นเรื่องจริงฉันคิดถึงมัน
Pawel

1

เพื่อแทนที่สิ่งที่เกิดขึ้นทั้งหมดในที่เก็บ git คุณสามารถใช้:

git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

ดูรายการไฟล์ใน repo คอมไพล์ท้องถิ่น? สำหรับตัวเลือกอื่น ๆ เพื่อแสดงรายการไฟล์ทั้งหมดในที่เก็บ -zตัวเลือกบอกคอมไพล์ที่จะแยกชื่อไฟล์ที่มีศูนย์ byte ซึ่งมั่นใจว่าxargs(มีตัวเลือก-0) สามารถแยกชื่อไฟล์แม้ว่าพวกเขาจะมีช่องว่างหรือ whatnot


1
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`

1
ไม่ได้ใช้awk/ sedแต่เป็นเรื่องปกติ (ยกเว้นระบบฝังตัว / ระบบที่มี busybox เท่านั้น)
pevik

1

เพื่อเปลี่ยนหลายไฟล์ (และบันทึกเป็นข้อมูลสำรอง*.bak):

perl -p -i -e "s/\|/x/g" *

จะใช้ไฟล์ทั้งหมดในไดเรกทอรีและแทนที่|ด้วย x เรียกว่า "Perl pie" (ง่ายเหมือนพาย)


ไม่เรียกซ้ำผ่านไดเรกทอรีแม้ว่า
PKHunter

มันเป็นไปได้ที่จะไปป์ซึ่งทำให้ปรับได้มากรวมถึงผ่านไดเรกทอรี josephscott.org/archives/2005/08/…และunix.stackexchange.com/questions/101415/…
Stenemo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.