sed yerinde dosyayı düzenle


300

Elle düzenlenmiş içeriği yeni bir dosyaya akış olmadan ve daha sonra yeni dosyayı orijinal dosya adına yeniden adlandırmadan tek bir sed komutunda bir dosyayı düzenlemek mümkün olup olmadığını bulmaya çalışıyorum . -iSeçeneği denedim ama Solaris sistemim bunun -iyasadışı bir seçenek olduğunu söyledi . Farklı bir yol var mı?


7
-ignu sed'de bir seçenektir, ancak standart sed'de değildir. Ancak, içeriği yeni bir dosyaya aktarır ve sonra dosyayı istediğiniz şekilde yeniden adlandırır.
William Pursell

2
Aslında, istediğim bu, sadece yeni dosyayı orijinal isme yeniden adlandırmak için sıradan bir görevi yerine getirmek zorunda kalmak istemiyorum
amphibient

3
O zaman soruyu yeniden ifade etmelisin.
William Pursell

3
@amphibient: Sorunuzun başlığını 'Solaris' kelimesiyle ön ekler misiniz? Sorunuzun değeri kayboluyor. Lütfen cevabımın altındaki yorumlara bakınız. Teşekkürler.
Steve

1
@Steve: Solaris önekini başlıktan tekrar kaldırdım çünkü bu hiçbir şekilde Solaris'e özel değil.
Üçlü

Yanıtlar:


477

-iSeçenek zaten yeni bir dosya içine düzenlenmiş içeriği akışları ve daha sonra perde arkasında adlandırır.

Misal:

sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

ve

sed -i '' 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' filename

üzerinde MacOS .


3
en azından benim için yapar, bu yüzden yapmak zorunda değilim
amphibient

2
O zaman sisteminizde GNU sed'i derlemeye çalışabilirsiniz.
choroba

3
örnek:sed -i "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>
Thales Ceolin

2
Bu perl çözeltisinden daha iyidir. Nedense herhangi bir .swap dosyası oluşturmaz .... teşekkürler!
matematik

3
@maths: Öyle, ama belki başka bir yerde mi yoksa daha kısa bir süre için mi?
choroba

72

sedDosyaları yerinde düzenleme yeteneğine sahip olmayan bir sistemde , daha iyi bir çözüm kullanmak olacağını düşünüyorum perl:

perl -pi -e 's/foo/bar/g' file.txt

Bu geçici bir dosya oluştursa da, yerine boş bir sonek / uzantı sağlandığı için orijinalin yerini alır.


14
Yalnızca geçici bir dosya oluşturmakla kalmaz, aynı zamanda sabit bağlantıları da koparır (örneğin, dosyanın içeriğini değiştirmek yerine, dosyayı aynı adı taşıyan yeni bir dosyayla değiştirir.) Bazen bu istenen davranıştır, bazen kabul edilebilir, ancak bir dosyaya birden çok bağlantı olduğunda neredeyse her zaman yanlış davranıştır.
William Pursell

3
@DwightSpencer: Perl içermeyen tüm sistemlerin bozulduğuna inanıyorum. Tek bir komut, yükseltilmiş izinler istediğinde ve kullanması gerektiğinde bir komut zincirini koyar sudo. Aklımda soru, en son GNU veya BSD'yi yüklemek zorunda kalmadan geçici bir dosyayı manuel olarak oluşturmayı ve yeniden adlandırmayı önlemekle ilgilidir sed. Sadece Perl kullanın.
Steve

4
@PetrPeller: Gerçekten mi? Soruyu dikkatlice okursanız, OP'nin gibi bir şeyden kaçınmaya çalıştığını anlarsınız sed 's/foo/bar/g' file.txt > file.tmp && mv file.tmp file.txt. Yerinde düzenleme, geçici bir dosya kullanarak yeniden adlandırmayı yaptığı için , seçenek kullanılamadığında manuel olarak yeniden adlandırma yapması gerektiği anlamına gelmez . Orada istediği şeyi yapabilen başka araçlar da var ve Perl bariz bir seçim. Bu orijinal sorunun cevabı nasıl değil?
Steve

2
@a_arias: Haklısın; o yaptı. Ancak Solaris'i kullanarak istediği şeyi başaramaz sed. Soru aslında çok iyi bir soruydu, ancak man sayfalarını okuyamayan veya burada SO'daki soruları gerçekten okumaktan rahatsız olmayan insanlar tarafından değeri azaldı .
Steve

4
@EdwardGarson: IIRC, Solaris Perl ile gönderilir ancak Python veya Ruby ile gönderilmez. Daha önce de söylediğim gibi, OP Solaris sed kullanarak istenen sonucu elde edemez.
Steve

56

OS X'te bu komutu çalıştırırken "geçersiz komut kodu" veya diğer garip hatalar alabileceğinizi unutmayın. Bu sorunu düzeltmek için deneyin

sed -i '' -e "s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g" <file>

Bunun nedeni, OSX sürümünde sed, -iseçeneğin bir extensionbağımsız değişken beklemesidir, böylece komutunuz aslında extensionbağımsız değişken olarak ayrıştırılır ve dosya yolu komut kodu olarak yorumlanır. Kaynak: https://stackoverflow.com/a/19457213


1
Bu OS X'in başka bir tuhaflığı. Neyse ki, brew install gnu-sedbir PATHirade ile birlikte sed'in GNU sürümünü kullanmanıza izin veriyor. Bahsettiğiniz için teşekkürler; kesin bir kafa kaşıyıcı.
Doug

23

Aşağıdaki mac benim iyi çalışıyor

sed -i.bak 's/foo/bar/g' sample

Foo'yu örnek dosyadaki bar ile değiştiriyoruz. Orijinal dosyanın yedeği sample.bak dosyasına kaydedilir

Yedeği yedekleme olmadan düzenlemek için aşağıdaki komutu kullanın

sed -i'' 's/foo/bar/g' sample

Yedek dosyaları tutmayı nasıl önleyebilirim?
Petr Peller

@PetrPeller, Aynı komut için belirtilen komutu kullanabilirsiniz ... sed -i '' 's / foo / bar / g' örneği
minhas23

18

Dikkat edilmesi gereken bir şey sed, sed'in tek amacı olarak dosyaları tek başına yazamamak "akış" (yani stdin, stdout, stderr ve diğer >&ntamponlar, soketler ve benzerlerinin boru hatları) bir editör olarak hareket etmektir . Bunu akılda tutarak teeçıktıyı dosyaya geri yazmak için başka bir komut kullanabilirsiniz . Başka bir seçenek, içeriğin borudan geçirilmesinden bir yama oluşturmaktır diff.

Tee yöntemi

sed '/regex/' <file> | tee <file>

Yama yöntemi

sed '/regex/' <file> | diff -p <file> /dev/stdin | patch

GÜNCELLEME:

Ayrıca, düzeltme ekinin dosyayı diff çıktısının 1. satırından değiştireceğini unutmayın:

Düzeltme ekinin çıktısının ilk satırında bulunduğu için hangi dosyaya erişileceğini bilmek gerekmez:

$ echo foobar | tee fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin
*** fubar   2014-03-15 18:06:09.000000000 -0500
--- /dev/stdin  2014-03-15 18:06:41.000000000 -0500
***************
*** 1 ****
! foobar
--- 1 ----
! fubar

$ sed 's/oo/u/' fubar | diff -p fubar /dev/stdin | patch
patching file fubar

2
/dev/stdin 2014-03-15, yanıtladı 7 Oca - Bir zaman gezgin misiniz?
Adrian Frühwirth

Windows'ta msysgit kullanmak /dev/stdinyoktur, bu nedenle /dev/stdintırnak işaretleri olmayan tek bir tire olan '-' ile değiştirmeniz gerekir, bu nedenle aşağıdaki komutların çalışması gerekir: $ sed 's/oo/u/' fubar | diff -p fubar -ve$ sed 's/oo/u/' fubar | diff -p fubar - | patch
Jim Raden

@ AdrianFrühwirth Etrafta bir yerlerde mavi bir polis kutum var ve nedense bana Doktor'u işyerinde çağırıyorlar. Ama dürüst olmak gerekirse, o zamanlar sistemde gerçekten kötü saat kayması var gibi görünüyor.
Dwight Spencer

Bu çözümler bana özel tamponlama davranışına dayanarak bakıyor. Sed okurken bu kırılabilir daha büyük dosyalar için şüpheleniyorum, dosya yama komutu ile altında değişmeye başlayabilir, ya da daha da kötüsü dosyayı kesecek tee komutu ile. Aslında ben de kısalmayacak emin değilim patch. Ben çıktı başka bir dosyaya kaydetmek ve daha sonra catorijinal içine -ing daha güvenli olacağını düşünüyorum.
akostadinov

@ william-pursell'den gerçek yerinde cevap
akostadinov

11

İstediğinizi yapmak mümkün değildir sed. Bunun sürümleri bile sed, -ibir dosyayı yerinde düzenleme seçeneğini tam olarak istemediğinizi tam olarak belirttiğiniz şeyi destekler: geçici bir dosyaya yazıp dosyayı yeniden adlandırırlar. Ama belki de kullanabilirsiniz ed. Örneğin , dosyadaki tüm oluşumlarını değiştirmek fooiçin şunları yapabilirsiniz:barfile.txt

echo ',s/foo/bar/g; w' | tr \; '\012' | ed -s file.txt

Sözdizimi benzerdir sed, ancak kesinlikle aynı değildir.

Ancak, neden yeniden adlandırılmış geçici bir dosya kullanmak istemediğinizi düşünmelisiniz. Eğer bir bilgisi yoksa bile -idestekleyen sed, kolayca sizin için işi yapmak için bir komut dosyası yazabilirsiniz. Bunun yerine, sed -i 's/foo/bar/g' fileyapabilirsin inline file sed 's/foo/bar/g'. Böyle bir senaryo yazmak önemsizdir. Örneğin:

#!/bin/sh -e
IN=$1
shift
trap 'rm -f $tmp' 0
tmp=$( mktemp )
<$IN "$@" >$tmp && cat $tmp > $IN  # preserve hard links

çoğu kullanım için yeterli olmalıdır.


1
örneğin, bu komut dosyasını nasıl adlandırırsınız ve nasıl adlandırırsınız?
amphibient

2
Ben onu arar inlineve yukarıda açıklandığı gibi çağırır:inline inputfile sed 's/foo/bar/g'
William Pursell

1
vay, edsadece gerçekten yerinde olan cevap. İlk defa ihtiyacım var ed. Teşekkür ederim. Küçük bir optimizasyon kullanmak echo -e ",s/foo/bar/g\012 w"veya echo $',s/foo/bar/g\012 w'kabuk ekstra trçağrı önlemek için izin verir nerede .
akostadinov

2
eddeğişiklikleri yerinde yapmaz. Gönderen straceçıkışı, GNU ed, bir geçici dosya oluşturur geçici dosya değişiklikleri yazar, daha sonra sabit bağlantıları koruyarak tüm orijinal dosyayı yeniden yazar. Gönderen trussçıkışı, Solaris 11 edayrıca geçici dosya kullanır, ancak birlikte kaydedildiğinde orijinal dosya adına geçici dosyayı yeniden adlandırır wherhangi bir sabit bağlantılar tahrip, komuta.
Andrew Henle

@AndrewHenle Bu cesaret kırıcı. edGeçerli macos (Mojave, ne olursa olsun pazarlama jargon aracı) ile birlikte gelen hala sabit bağları korur söyledi. YMMV
William Pursell

10

Vi kullanabilirsiniz

vi -c '%s/foo/bar/g' my.txt -c 'wq'

9

sed yerinde düzenlemeyi destekler. Gönderen man sed:

-i[SUFFIX], --in-place[=SUFFIX]

    edit files in place (makes backup if extension supplied)

Örnek :

hello.txtMetni içeren bir dosyanız olduğunu varsayalım :

hello world!

Eski dosyanın yedeğini tutmak istiyorsanız şunu kullanın:

sed -i.bak 's/hello/bonjour' hello.txt

İki dosya ile karşılaşacaksınız: hello.txtiçerikle:

bonjour world!

ve hello.txt.bakeski içerikle.

Bir kopyasını tutmak istemiyorsanız, extension parametresini geçmeyin.


3
OP özellikle platformunun bu (popüler fakat) standart dışı seçeneği desteklemediğinden bahsediyor.
Üçlü

3

Hangi kabuğu kullandığınızı belirtmediniz, ancak zsh ile bunu =( )gerçekleştirmek için yapıyı kullanabilirsiniz . Çizgileri boyunca bir şey:

cp =(sed ... file; sync) file

=( ), şuna benzer >( )ancak cpsona erdiğinde otomatik olarak silinecek geçici bir dosya oluşturur .


3
mv file.txt file.tmp && sed 's/foo/bar/g' < file.tmp > file.txt

Çıktı orijinal dosyanın içeriğinin üzerine yazmak üzere yönlendirildiğinden ve sed'in özel bir sürümüne ihtiyaç duyulmadığından, tüm sabit bağlantıları korumalıdır.


3

Aynı miktarda karakteri değiştiriyorsanız ve dosyaların "Yerinde" düzenlemesini dikkatlice okuduktan sonra ...

Yeniden <>okuma operatörünü kullanarak dosyayı okumak ve yazmak üzere açabilirsiniz:

sed 's/foo/bar/g' file 1<> file

Canlı izleyin:

$ cat file
hello
i am here                           # see "here"
$ sed 's/here/away/' file 1<> file  # Run the `sed` command
$ cat file
hello
i am away                           # this line is changed now

Gönderen Okuma Yazmaya Bash → 3.6.10 Açılış Dosya Tanımlayıcı :

Yönlendirme operatörü

[n]<>word

dosya tanımlayıcı n'de veya n belirtilmemişse dosya tanımlayıcı 0'da hem okuma hem de yazma için adı word'ün genişletilmesi olan dosyanın açılmasına neden olur. Dosya yoksa, oluşturulur.


Bu dosyaya göre bozuk. Bunun arkasındaki nedenden emin değilim. paste.fedoraproject.org/462017
Nehal J Wani

Bkz. Satır [43-44], [88-89], [135-136]
Nehal J Wani

2
Bu blog yazısı bu cevabın neden kullanılmaması gerektiğini açıklamaktadır. backreference.org/2011/01/29/in-place-editing-of-files
Nehal J Wani

@NehalJWani Makaleyi uzun bir süre okuyacağım, heads-up için teşekkürler! Cevapta bahsetmediğim şey, aynı miktarda karakteri değiştirirse bunun işe yaramasıdır.
fedorqui 'SO' zarar vermeyi durdurun

@NehalJWani bu söylendi, cevabım dosyalarınıza zarar verdiğinde üzgünüm. Verdiğiniz bağlantıları okuduktan sonra düzeltmelerle güncelleyeceğim (veya gerekirse sileceğim).
fedorqui 'SO' zarar vermeyi durdurun

2

Moneypenny'nin Skyfall'da söylediği gibi: "Bazen eski yollar en iyisidir." Kincade daha sonra benzer bir şey söyledi.

$ printf ',s/false/true/g\nw\n' | ed {YourFileHere}

Yerinde mutlu düzenleme. Dosyayı yazmak için '\ nw \ n' eklendi. Gecikmeli cevaplama isteği için özür dileriz.


Bunun ne yaptığını açıklayabilir misiniz?
Matt Montag

1
Bazı karakterlerin stdin'e yazıldığı metin editörü ed (1) 'i çağırır. 30 yaşın altındaki biri olarak, dinozorların bize olduğu gibi ed (1) 'in dinozorlara olduğunu söylememin benim için uygun olduğunu düşünüyorum. Her neyse, ed'i açmak ve bu şeyleri yazmak yerine, jlettvin kullanılan karakterleri oluşturuyor |. @MattMontag
Ari Sweedler

Komutların ne yaptığını soruyorsanız, bunlardan iki tanesi a ile sonlandırılır \n. [aralık] s / <old> / <new> / <flag>, yedek komutun biçimidir - diğer birçok metin editöründe hala görülen bir paradigma (tamam, vim, doğrudan bir ed torbası) Her neyse, gbayrak "global" ve "Baktığınız her satırdaki her örnek" anlamına gelir. Aralık 3,53-5 satırlarına bakar. ,5StartOfFile-5'e 3,bakar, 3-EndOfFile satırlarına ,bakar ve StartOfFile-EndOfFile'a bakar. Her satırda "false" yerine "true" yerine genel bir yerine koyma komutu. Ardından, write komutu,, wgirilir.
Ari Sweedler

1

Çok güzel örnekler. Yerinde birçok dosyayı düzenlemek için zorluk vardı ve -i seçeneği find komutu içinde kullanarak tek makul bir çözüm gibi görünüyor. Her dosyanın ilk satırının önüne "version:" eklenecek komut dosyası:

find . -name pkg.json -print -exec sed -i '.bak' '1 s/^/version /' {} \;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.