Bir metin dosyasındaki tüm bir satırı satır numarasına göre değiştirme


158

Bir bash betiğinin bir dosyadaki tüm bir satırı değiştirmesini istediğim bir durum var. Satır numarası her zaman aynıdır, dolayısıyla bu sabit kodlanmış bir değişken olabilir.

Bu satırdaki bazı alt dizeleri değiştirmeye çalışmıyorum, sadece bu satırı tamamen yeni bir satırla değiştirmek istiyorum.

Bunu yapmak için herhangi bir bash yöntemi var mı (veya bir .sh betiğine atılabilecek basit bir şey).

Yanıtlar:


242

En iyisi değil, ama bu işe yaramalı:

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

Nhedef satır numaranız nerede değiştirilmelidir. Bu, orijinal dosyadaki satırın yerini alır. Değiştirilen metni farklı bir dosyaya kaydetmek için -iseçeneği bırakın :

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

10
Benim için diyor ki: sed: -e expression #1, char 26: unknown option to ``s've benim çizgisidir: 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. Herhangi bir fikir?
Danijel

4
/Değiştirme metnindeki her biri, sescaped ( \/) değilse , komutun kapanış eğik çizgisi olarak yorumlanır . Yapılması en kolay şey, sınırlayıcı olarak farklı, kullanılmayan bir karakter seçmektir. Örneğin sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk...,.
chepner

4
Bu açıklama biraz kafa karıştırıcı ve işe yaramıyor gibi görünüyor. Sed ve sınırlayıcı ayar yetenekleriyle ilgili sorunlar yaşıyorsanız, şuna bir göz atmak isteyebilirsiniz: grymoire.com/Unix/Sed.html#uh-2 Sınırlayıcıyı arkasındaki ilk karakterin sbelirlediğini söylüyor . Öyleyse, komutunuzu kullanmak için değiştirmek için, örneğin şu #şekilde olacaktır:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der

7
@ Meonlol'ları genişletmek için macOS, bir -eif belirtilmesini -ide gerektirir ; böylece, doğru komut şu olur:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
ELLIOTTCABLE

3
Kullanım çift tırnak yedek hat düzeltilmediği takdirde, bu gibi bir değişken, var örneğin: sed -i "Ns/.*/foo=$myVar" file.txt.
gsamaras

44

Aslında bu betiği bir süre önce şirketimizin UNIX sunucularındaki cron dosyasındaki bir kod satırını değiştirmek için kullandım. Bunu normal bir kabuk betiği olarak çalıştırdık ve hiçbir sorun yaşamadık:

#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

Bu, satır numarasına göre gitmez, ancak satır numarasını satır numarasının önüne s/koyarak ve yerine joker karakter yerleştirerek kolayca satır numarasına dayalı bir sisteme geçebilirsiniz the_original_line.


18
Kedinin sed -e "s/.../.../" /dir/file > /dir/temp_file
gereksiz

24
Yorumlayıcı / derleyici için işe yaramaz olabilir, ancak onu okuyan bir insan için değil. @ Kyle'ın kodu güzelce soldan sağa okuyor; IMO'yu kullandığınız deyim, fiilin isimden önce olması nedeniyle kullanmaz.
Clayton Stanley

1
Bu iki satırı birleştirmenin bir yolu var mı, örneğin,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda

1
@PubuduDodangoda sed -i bunu
başarırdı

22

4. satırı "farklı" metniyle değiştirmek istediğinizi varsayalım. AWK'yi şu şekilde kullanabilirsiniz:

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

AWK, girdiyi "alanlara" bölünmüş "kayıtlar" olarak kabul eder. Varsayılan olarak, bir satır bir kayıttır. NRgörülen kayıtların sayısıdır. $0geçerli tam kaydı temsil eder (kayıttaki $1ilk alandır vb. alanlar varsayılan olarak satırdaki sözcüklerdir).

Dolayısıyla, mevcut satır numarası 4 ise, "farklı" dizesini yazdırın, aksi takdirde satırı değiştirmeden yazdırın.

AWK'da, içine alınan program kodu { }her giriş kaydında bir kez çalışır.

Kabuğun .xml gibi şeyleri yorumlamaya çalışmasını önlemek için AWK programını tek tırnak içinde alıntılamanız gerekir $0.

DÜZENLEME: Aşağıdaki yorumlarda @chepner'dan daha kısa ve daha zarif bir AWK programı:

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

Yalnızca 4 numaralı kayıt (yani satır) için, tüm kaydı "farklı" dizesiyle değiştirin. Ardından her giriş kaydı için kaydı yazdırın.

Açıkçası AWK becerilerim paslanmış! Teşekkürler @chepner.

DÜZENLEME: ve ayrıca @Dennis Williamson'dan daha kısa bir versiyona bakın:

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

Bunun nasıl çalıştığı yorumlarda açıklanmıştır: 1her zaman doğru olarak değerlendirilir, bu nedenle ilişkili kod bloğu her zaman çalışır. Ancak ilişkili bir kod bloğu yoktur, bu da AWK'nın varsayılan eylemini yalnızca tüm satırı yazdırmak gibi yaptığı anlamına gelir. AWK, bunun gibi kısa programlara izin verecek şekilde tasarlanmıştır.


3
Biraz daha kısa: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner

2
@chepner: Biraz daha kısa:awk 'NR==4 {$0="different"}1' input_file.txt
sonraki duyuruya kadar duraklatıldı.

@DennisWilliamson, nasıl çalıştığını bile bilmiyorum! Bu takip 1ne işe yarar ? (Not: Test ettim ve gerçekten işe yarıyor! Nasıl olduğunu anlamıyorum.)
steveha

1
Koşulsuz baskıyı kısaltmanın bir yolu olacağını düşündüm! @stevaha: 1 sadece gerçek bir değerdir, yani her zaman eşleşen bir "kalıp". Bir eşleşme için varsayılan eylem, geçerli satırı yazdırmaktır.
chepner

19

Bu test dosyası (test.txt) verildiğinde

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

aşağıdaki komut ilk satırı "yeni satır metni" olarak değiştirecektir

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

Sonuç:

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

daha fazla bilgiye buradan ulaşabilirsiniz

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


2
Bu doğru cevap olmalı. C bayrağı tam olarak gerekli olanı yapar (numaralı bir satırı verilen metinle değiştirir).
adelphus

Stdout'ta dosyayı yankılamayan sed ile alternatif sözdizimi: stackoverflow.com/a/13438118/4669135
Gabriel Devillers

Doğru cevap bu, ama çirkin satır neden kesiliyor? sed '1 cnewline text' test.txtbunu da yapardı.
dev-null

7

bash'da N, M'yi satır numaralarıyla ve xxx yyy'yi istediğinizle değiştirin

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

DÜZENLE

Aslında bu çözümde "\ 0" "\ t" ve "\" karakterleriyle ilgili bazı sorunlar var.

"\ t", okunmadan önce: "\", -r ile satırın sonuna IFS = koyarak çözülebilir

IFS= read -r line

ancak "\ 0" için değişken kısaltılmıştır, saf bash'de bir çözüm yoktur: Bash'deki bir değişkene null karakter (\ 0) içeren dizge atayın Ancak normal metin dosyasında nul karakteri yoktur \ 0

perl daha iyi bir seçim olurdu

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

Saf bir BASH çözümünü denemeyi bile düşünmedim!
steveha

5
# 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'dan mükemmel cevap. Bash Shell'de benim için çalışıyor.

 # 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`

burada
index- Satır no
newLine- değiştirmek istediğimiz yeni satır dizesi.


Benzer şekilde aşağıdaki kod, dosyadaki belirli bir satırı okumak için kullanılır. Bu, gerçek dosyayı etkilemeyecektir.

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

burada
!d- satır no dışındaki satırları silecek. $index Böylece çıktıyı $indexdosyada no satır dizesi olarak alacağız .


ama bu benim için neden sadece sonuç gösteriyor ${newline}?
sikisis

1

Mac'te kullandım

sed -i '' -e 's/text-on-line-to-be-changed.*/text-to-replace-the=whole-line/' file-name

-2

Parametreleri sed komutuna bile aktarabilirsiniz:

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 dosyasının yürütülmesi yeni output.dat dosyasını verir

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

1
neden bu örnek için bir döngüye ihtiyacınız var, sadece çözümü belirsizleştiriyor? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob

Bunun için bir döngüye ihtiyacınız yok
chandu vutukuri
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.