Sed -i (yerinde düzenleme) ile taşınabilirliği nasıl sağlayabilirim?


40

FreeBSD çalıştıran paylaşılan bir barındırma olan sunucum için kabuk komut dosyaları yazıyorum. Ayrıca bilgisayarımda Linux çalıştıran bilgisayarımda bunları yerel olarak test edebilmek istiyorum. Dolayısıyla, onları taşınabilir bir şekilde yazmaya çalışıyorum, ancak sedbunu yapmanın bir yolunu göremiyorum.

Web sitemin bir kısmı oluşturulan statik HTML dosyaları kullanıyor ve bu sed satırı her rejenerasyondan sonra doğru DOCTYPE ekliyor:

sed -i '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

sedLinux'ta GNU ile çalışır , ancak FreeBSD , yedek kopya için bir uzantı olmak sedüzere ilk argümanı bekler -i. Bu nasıl göründüğü:

sed -i '' '1s/^/<!DOCTYPE html> \n/' ${file_name.html}

Ancak, sedsırayla GNU ifadenin hemen sonra gelmesini bekliyor -i. (Ayrıca yeni satır işleme ile düzeltmeler gerektirir, ancak bu zaten burada cevaplanmıştır )

Tabii ki bu değişikliği betiğin sunucu kopyasına ekleyebilirim, ancak VCS'yi sürüm için kullanmam karışıklık getirebilir. Tamamen taşınabilir bir şekilde sed ile bunu başarmanın bir yolu var mı?


Sağladığınız iki pasajı aynı, yazım hatası olmadığından emin misin? Ayrıca, GNU -i
sed'i

Duh, bunu tespit ettiğin için teşekkürler. Sorumu düzelttim. İkinci satır sed'imde hatayla sonuçlandı, '1s / ^ / <! DOCTYPE html> \ n /' nin bir dosya olmasını bekliyor ve bulamamasından şikayet ediyor.
Kırmızı,

1
Çapraz referans: Mac (BSD) ve Linux'ta Yığın Taşması üzerinde çalışan sed yerinde bayrak .
MvG

Yanıtlar:


39

GNU sed sonra isteğe bağlı bir uzantıyı kabul eder -i. Uzantı, araya giren bir boşluk olmadan aynı argüman içinde olmalıdır. Bu sözdizimi BSD sed üzerinde de çalışır.

sed -i.bak -e '…' SOMEFILE

BSD'de, -ibirden fazla giriş dosyası olduğunda da davranışı değiştirdiğine dikkat edin: bağımsız olarak işlenirler (örneğin $, her bir dosyanın son satırına eşleşir). Ayrıca bu BusyBox'ta çalışmaz.

Yedek dosyaları kullanmak istemiyorsanız, hangi sed versiyonunun mevcut olduğunu kontrol edebilirsiniz.

case $(sed --help 2>&1) in
  *GNU*) set sed -i;;
  *) set sed -i '';;
esac
"$@" -e '…' "$file"

Veya alternatif olarak, konumsal parametreleri gizlemekten kaçınmak için bir işlev tanımlayın.

case $(sed --help 2>&1) in
  *GNU*) sed_i () { sed -i "$@"; };;
  *) sed_i () { sed -i '' "$@"; };;
esac
sed_i -e '…' "$file"

Rahatsız etmek istemiyorsanız, Perl kullanın.

perl -i -pe '…' "$file"

Taşınabilir bir komut dosyası yazmak istiyorsanız, kullanmayın -i- POSIX'de değil. Kaputun altında sed'in ne yaptığını manuel olarak yapın - sadece bir kod satırı daha var.

sed -e '…' "$file" >"$file.new"
mv -- "$file.new" "$file"

2
GNU sed -ida ima eder -s. Ve bir GNU için kontrol etmenin en kolay yolu sedile sed vGNU için geçerli bir noop ama başka her yerde başarısız komuta.
mikeserv

Yukarıdaki ipuçlarından esinlenerek, gerçekten bir tane istemek isteyenler için tek satırlı (çirkin) taşınabilir bir sürüm var, ancak bir alt kabuk ortaya sed -i$(sed v < /dev/null 2> /dev/null || echo -n " ''") -e '...' "$file"çıkarsa da : GNU sed değilse, sonradan iki tekli tırnak ekleyen bir boşluk ekler -i. BSD üzerinde çalışıyor. GNU sed sadece alır -i.
Ivan X

2
@IvanX vGNU sed için test etmek için komutun varlığını kullanmaya karşıyım . Ya FreeBSD uygulamaya karar verirse?
Gilles 'SO- kötülük olmayı bırak'

@Gilles Fair point, ancak GNU sed'in man sayfası tam olarak bu amaç için (GNU sed'in olduğunu ve başka bir şey olmadığını tespit ederek) olduğunu açıklar, bu nedenle * BSD'nin bunu yerine getireceğini umarız. GNU sed üzerinde bir işlem yapmazken, BSD sed'de (veya tersi) bir hataya neden olurken, -i'nin kendisinden başka bir test yapmayı düşünemiyorum, ancak önce sahte bir dosya oluşturulmasını gerektiriyor. Sed için yaptığınız sınav tamam, ancak satır içi için hantal. Kaçınılması -i tamamen, önerdiğiniz gibi, kesinlikle en güvenli bahis gibi gözüküyor, ancak varolanın sed vamacı olarak verilenleri kullanmam iyi olur .
Ivan X,

1
Bir seçenek tanımlamak için bir alternatif, düzenlememe bakın.
Gilles 'SO- kötülük' dur

10

sedGüzel bir oyun yapmak için bir numara bulamazsanız , deneyebilirsiniz:

  1. Kullanmayın -i:

    sed '1s/^/<!DOCTYPE html> \n/' "${file_name.html}" > "${file_name.html}.tmp" &&
      mv "${file_name.html}.tmp" "${file_name.html}"
  2. Perl kullanın

    perl -i -pe 'print "<!DOCTYPE html> \n" if $.==1;' "${file_name.html}"

8

ed

edVarolan bir dosyaya bir satır hazırlamak için her zaman kullanabilirsiniz .

$ printf '0a\n<!DOCTYPE html>\n.\nw\n' | ed my.html

ayrıntılar

Çevresindeki bit , dosyaya bu satırı eklemesi talimatını verme <!DOCTYPE html>komutlarıdır .edmy.html

sed

Bu komutun sedda kullanılabileceğine inanıyorum :

$ sed -i '1i<!DOCTYPE html>\n` testfile.csv

Sonunda Perl'e başvurdum, ancak ed kullanmak, olması gerektiği gibi Unix benzeri kullanıcılar arasında popüler olmayan iyi bir alternatif.
Kırmızı,

@Red - Sorununuzu çözdüğünüzü duyduğuma sevindim. Evet, bunu daha önce görmedim, googling ortaya çıktı ve bunu yapmanın en taşınabilir, en uygun yolu gibi görünüyordu.
slm

7

perl -iKaputun altında ne olduğunu manuel olarak da yapabilirsiniz :

{ rm -f file && { echo '<!DOCTYPE html>'; cat; } > file;} < file

Gibi perl -i, hiçbir yedekleme yok ve burada verilen çoğu çözüm gibi, dosyanın izinlerini, sahipliğini etkileyebileceğini ve bir sembolik diziyi normal bir dosyaya dönüştürebileceğini unutmayın.

İle:

sed '1i\
<!DOCTYPE html>' file 1<> file

seddosyanın üzerine yazacaktır, dolayısıyla mülkiyeti ve izinleri veya sembolik bağlantıları etkilemeyecektir. GNU ile çalışır, sedçünkü komutun üzerine yazmadan önce sedtipik olarak verilerden file(benim durumumda 4k) veri dolu bir tampon okuyacaktır i. Dosya sedçıktısını da tamponlayan gerçeği dışında dosya 4k'dan fazla olsaydı bu işe yaramazdı .

Temelde sedokuma ve yazma için 4k blokları üzerinde çalışır. Eklenecek satır 4k'den küçükse sed, henüz okumadığı bir bloğun üzerine asla yazmaz.

Buna rağmen güvenmezdim.


echo '<!DOCTYPE html>'"" Tırnak işaretleri olmadan alınmalı veya kaçılmalıdır.
AD

@AD İyi nokta. Bu hatayı unutalım. Genelde kendim için devre dışı bıraktığım için etkileşimli bash / zsh 'ın özelliği.
Stéphane Chazelas 22:15

Bu, burada var olup olmadığını kontrol etmeden, statik, tahmin edilebilir bir isimle "geçici dosya" ya yönlendiren geniş açık bir güvenlik boşluğuna sahip olmayan çok az cevaptan biridir . Bunun kabul edilen cevap olması gerektiğine inanıyorum. Ayrıca grup komutlarının kullanımının çok güzel gösterilmesi.
Joker

3

Mac OS X'te de kullanılan FreeBSD sed , aşağıdaki (regex) komutunu doğru ve açık bir şekilde tanımlamak ve tanımak için anahtardan -esonra bir seçeneğe ihtiyaç duyuyor -i.

Başka bir deyişle, sed -i -e ...FreeBSD ve GNU ile çalışmalı sed.

Daha genel olarak, FreeBSD'den sonra yedekleme uzantısını atlamak, komut satırı argümanlarını ayrıştırırken FreeBSD'nin bir bölümünde karışıklığı önlemek için sed -iaçık bir sedseçenek gerektirir veya bunu değiştirmeyi gerektirir .-ised

(Ancak, sedyerinde dosya düzenlemelerinin dosya inode değişikliklerine yol açtığını unutmayın, bkz. "Dosyaları yerinde düzenleme" .

(Genel bir ipucu olarak, FreeBSD son sürümlerini sedsahip -rGNU ile uyumluluğu arttırmak için düğmeyi sed).

echo a > testfile.txt
ls -li testfile.txt
#gsed -i -e 's/a/A/' testfile.txt
#bsdsed -i 's/a/A/' testfile.txt  # does not work
bsdsed -i -e 's/a/A/' testfile.txt
ls -li testfile.txt
cat testfile.txt

5
Hayır, bsdsed -i -e 's/a/A/'bir değil yerinde düzenleme, bir "-e" soneki ile orijinal tasarrufu ile düzenleme var ( testfile.txt-e).
Stéphane Chazelas 22:15

GNU sed'in ayrıca FreeBSD ile uyumluluk için -E'yi (-r'ye ek olarak) desteklediğini unutmayın. -E'nin bir sonraki POSIX sürümünde belirtilmesi muhtemeldir, bu yüzden hepimiz bunu unutmalıyız -r mantıklı değil ve hiç yokmuş gibi davran.
Stéphane Chazelas

2

sed -iYarış koşullarından mümkün olduğunca kaçınılarak tek bir dosyaya taşınabilir bir şekilde taklit etmek için:

sed 'script' <<FILE >file
$(cat file)
FILE

Bu arada, bu aynı zamanda sed -i, dizin ve dosya izinlerine bağlı olarak sed -i, kullanıcının düzenleme iznine sahip olmayan bir dosyanın üzerine yazmasına olanak verebilecek olası bir sorunu da ele alıyor .

Ayrıca aşağıdaki gibi yedeklemeler de yapabilirsiniz:

sed 'script' <<FILE >file
$(tee file.old <file)
FILE

2

Vim'i Ex modunda kullanabilirsiniz:

ex -sc '1i|<!DOCTYPE html>' -cx file
  1. 1 ilk satırı seç

  2. i metin ve newline ekle

  3. x kaydet ve kapat

Hatta yorumlarda olduğu gibi eski standartlarda ex :

printf '%s\n' 1i '<!DOCTYPE html>' . x | ex file 

Burada Vim'e özgü hiçbir şey yok. Bu, uygulamaların çoklu bayrakları desteklemesi gerekmediği durumlar haricinde POSIX teknik özellikleriyleex tamamen uyumludur . Kesin taşınabilirlik için kullanacağım-cprintf '%s\n' 1i '<!DOCTYPE html>' . x | ex file
Wildcard
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.