Bagaimana cara mengganti seluruh baris dalam file teks dengan nomor baris

152

Saya memiliki situasi di mana saya ingin skrip bash untuk mengganti seluruh baris dalam file. Nomor baris selalu sama, sehingga bisa menjadi variabel hard-coded.

Saya tidak mencoba mengganti beberapa sub-string di baris itu, saya hanya ingin mengganti baris itu sepenuhnya dengan baris baru.

Apakah ada metode bash untuk melakukan ini (atau sesuatu yang sederhana yang dapat dilemparkan ke dalam skrip .sh).

pengguna788171
sumber

Jawaban:

228

Bukan yang terbaik, tetapi ini harus bekerja:

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

di mana Nharus diganti dengan nomor baris target Anda. Ini menggantikan baris dalam file asli. Untuk menyimpan teks yang diubah dalam file yang berbeda, jatuhkan -iopsi:

sed 'Ns/.*/replacement-line/' file.txt > new_file.txt
chepner
sumber
1
Apakah ada cara untuk membuat sed memodifikasi file yang dimaksud daripada membuang ke layar?
user788171
8
Bagi saya itu mengatakan: sed: -e expression #1, char 26: unknown option to ``s'dan garis saya adalah: 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. Ada ide?
Danijel
4
Masing-masing /dalam teks pengganti akan ditafsirkan sebagai garis miring penutup dari sperintah kecuali lolos ( \/). Namun, hal termudah untuk dilakukan adalah memilih karakter yang berbeda dan tidak digunakan sebagai pembatas. Sebagai contoh sed -i '7s{.*}{<param-value>http://...}' $TCE_SVN_HOME/trunk...,.
chepner
4
Penjelasan itu agak membingungkan dan sepertinya tidak berhasil. Jika Anda memiliki masalah dengan sed dan kemampuan pengaturan pembatasnya, Anda mungkin ingin melihatnya: grymoire.com/Unix/Sed.html#uh-2 Dikatakan karakter pertama di belakang smenentukan pembatas. Jadi untuk mengubah perintah Anda menggunakan misalnya #akan seperti ini:sed -i '7s#.*#<param-value>http://localhost:8080/ASDF/services/REWS.REWSHttpSoap12Endpo‌​int/</param-value>#'
func0der
7
Untuk memperluas pada @ meonlol's, macOS membutuhkan -eif jika -idiberikan, juga; dengan demikian, perintah yang benar menjadi:sed -e 'Ns/.*/replacement-line/' -i '' file.txt
DAPAT DITERIMA
44

Saya sebenarnya menggunakan skrip ini untuk mengganti sebaris kode dalam file cron di server UNIX perusahaan kami beberapa waktu lalu. Kami menjalankannya sebagai skrip shell normal dan tidak memiliki masalah:

#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

Ini tidak sesuai dengan nomor baris, tetapi Anda dapat dengan mudah beralih ke sistem berbasis nomor baris dengan meletakkan nomor baris sebelum s/dan menempatkan wildcard di tempat the_original_line.

Kyle
sumber
17
Penggunaan kucing yang tidak berguna:sed -e "s/.../.../" /dir/file > /dir/temp_file
chepner
24
Mungkin tidak berguna untuk penerjemah / kompiler, tetapi tidak untuk manusia yang membacanya. @ Kode Kyle dibaca dengan baik dari kiri ke kanan; idiom yang Anda gunakan IMO tidak, karena fakta bahwa kata kerjanya adalah sebelum kata benda.
Clayton Stanley
1
Apakah ada cara untuk menggabungkan kedua garis itu, misalnya dengan sesuatu seperti,sed -e "s/.../.../" /dir/file > /dir/file
Pubudu Dodangoda
22

Misalkan Anda ingin mengganti baris 4 dengan teks "berbeda". Anda dapat menggunakan AWK seperti:

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

AWK menganggap input sebagai "catatan" dibagi menjadi "bidang". Secara default, satu baris adalah satu catatan. NRadalah jumlah catatan yang terlihat. $0mewakili catatan lengkap saat ini (sementara$1 adalah bidang pertama dari catatan dan sebagainya; secara default bidang adalah kata-kata dari baris).

Jadi, jika nomor baris saat ini adalah 4, cetak string "berbeda" tetapi cetak baris tidak berubah.

Dalam AWK, kode program terlampir di { }jalankan sekali pada setiap catatan input.

Anda perlu mengutip program AWK dalam tanda kutip tunggal untuk menjaga shell dari mencoba menafsirkan hal-hal seperti $0.

EDIT: Program AWK yang lebih pendek dan lebih elegan dari @chepner di komentar di bawah:

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

Hanya untuk catatan (yaitu baris) nomor 4, ganti seluruh rekaman dengan string "berbeda". Kemudian untuk setiap record input, cetak record.

Jelas keterampilan AWK saya berkarat! Terima kasih, @ chepner.

Sunting: dan lihat juga versi yang lebih pendek dari @Dennis Williamson:

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

Cara ini dijelaskan dalam komentar: 1selalu bernilai true, sehingga blok kode yang terkait selalu berjalan. Tetapi tidak ada blok kode yang terkait, yang berarti AWK melakukan tindakan standarnya hanya dengan mencetak seluruh baris. AWK dirancang untuk memungkinkan program singkat seperti ini.

steveha
sumber
3
Sedikit lebih pendek: awk 'NR==4 {$0="different"} { print }' input_file.txt.
chepner
2
@ chepner: Sedikit lebih pendek:awk 'NR==4 {$0="different"}1' input_file.txt
Dijeda sampai pemberitahuan lebih lanjut.
@ DennisWilliamson, saya bahkan tidak tahu cara kerjanya! Apa yang 1dilakukan trailing itu? (Catatan: Saya mengujinya dan itu memang berhasil! Saya hanya tidak mengerti caranya.)
steveha
1
Saya pikir akan ada cara untuk menyingkat cetakan tanpa syarat! @stevaha: angka 1 hanyalah nilai sebenarnya, yang berarti "pola" yang selalu cocok. Dan tindakan default untuk pertandingan adalah dengan mencetak baris saat ini.
chepner
19

Diberikan file tes ini (test.txt)

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

perintah berikut akan menggantikan baris pertama menjadi "teks baris baru"

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

Hasil:

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

informasi lebih lanjut dapat ditemukan di sini

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

Eric Robinson
sumber
2
Ini seharusnya jawaban yang benar. Bendera c melakukan apa yang diperlukan (menggantikan baris bernomor dengan teks yang diberikan).
adelphus
Sintaks alternatif dengan sed yang tidak menggemakan file di stdout: stackoverflow.com/a/13438118/4669135
Gabriel Devillers
Itu jawaban yang tepat, tetapi mengapa garis jelek itu putus? sed '1 cnewline text' test.txtakan melakukannya juga.
dev-null
7

di bash, ganti N, M dengan nomor baris dan xxx yyy dengan apa yang Anda inginkan

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

EDIT

Sebenarnya dalam solusi ini ada beberapa masalah, dengan karakter "\ 0" "\ t" dan "\"

"\ t", dapat diselesaikan dengan meletakkan IFS = sebelum membaca: "\", di akhir baris dengan -r

IFS= read -r line

tetapi untuk "\ 0", variabel terpotong, tidak ada solusi di bash murni: Tetapkan string yang berisi karakter nol (\ 0) ke variabel di Bash Tetapi dalam file teks normal tidak ada karakter nul \ 0

perl akan menjadi pilihan yang lebih baik

perl -ne 'if($.==N){print"xxx\n"}elsif($.==M){print"yyy\n"}else{print}' < orig-file > new-file
Nahuel Fouilleul
sumber
Saya bahkan tidak berpikir untuk mencoba solusi BASH murni!
steveha
4
# 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"
}
Daniel Bigham
sumber
3

Jawaban sempurna dari Chepner. Ini bekerja untuk saya di bash Shell.

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

di sini
index- Baris tidak
newLine- string baris baru yang ingin kita ganti.


Demikian pula kode di bawah ini digunakan untuk membaca baris tertentu dalam file. Ini tidak akan mempengaruhi file yang sebenarnya.

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

di sini
!d- akan menghapus baris selain dari baris tidak. $index Jadi kita akan mendapatkan output sebagai string baris tidak $indexdalam file.

Kanagavelu Sugumar
sumber
tetapi saya bash mengapa hasilnya hanya menunjukkan ${newline}?
sikisis
1

Di mac saya gunakan

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

Anda bahkan dapat meneruskan parameter ke perintah sed:

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 

output orignial. data:

a
b
c
d
e
f
g
h
i
j

Executing ./test.sh memberikan output.dat baru

a
b
c
replaced by '1'!
replaced by '2'!
replaced by '3'!
replaced by '4'!
replaced by '5'!
i
j
AndreH
sumber
1
mengapa Anda memerlukan loop untuk contoh ini, hanya mengaburkan solusinya? line=5; sed -i "${line}s/.*/replaced by '$i'!/" output.dat
Rob
Anda tidak perlu lingkaran untuk ini
chandu vutukuri