Ganti seluruh baris yang berisi string menggunakan Sed

319

Saya memiliki file teks yang memiliki garis seperti sesuatu

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

Saya perlu mengganti seluruh baris di atas dengan

This line is removed by the admin.

Kata kunci pencarian adalah TEXT_TO_BE_REPLACED

Saya perlu menulis skrip shell untuk ini. Bagaimana saya bisa mencapai ini dengan menggunakan sed?

Rahul
sumber
@ Skarron Saya mencoba sed -rne 's / (TEXT_TO_BE_REPLACED) \ s + \ w + / \ 1 yyy / xxx' Tapi itu hanya akan mengganti yyy dengan xxx dalam string jika ada yyy hadir. Dalam kasus saya teks tidak diperbaiki ... yang saya miliki adalah TEXT_TO_BE_REPLACED.
Rahul
itu bukan ekspresi sed yang valid. Apakah Anda yakin Anda menginginkannya dengan sed? Bahasa pemrograman apa pun dengan regexps yang baik dapat menggantikan Anda (mencari sub fungsi). Itu akan lebih mudah daripada memahami diri sendiri
Scharron
@ Skarron Yah, itu harus dilakukan dengan skrip shell jadi saya kira sed adalah taruhan terbaik saya.
Rahul

Jawaban:

483

Anda dapat menggunakan perintah perubahan untuk mengganti seluruh baris, dan -ibendera untuk melakukan perubahan di tempat. Misalnya, menggunakan sed GNU:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
Todd A. Jacobs
sumber
4
Perhatikan bahwa Anda memerlukan spasi sebelum c \. Saya baru saja mengedit untuk menambahkan ini.
Marcus Downing
27
@MarcusDowning GNU sed tidak memerlukan ruang; itu berfungsi dengan baik seperti aslinya diposting. Jika sed khusus Anda membutuhkan ruang, maka tentu saja perhatikan sed yang tidak kompatibel dan tambahkan doa yang diperlukan sebagai komentar. Namun, tolong jangan mengubah kode kerja dalam jawaban yang diterima.
Todd A. Jacobs
7
Bagaimana saya bisa menggunakan variabel alih-alih teks "Ini ..."? Jika saya menggantinya dengan $ variabel, itu tidak mencetak kontennya tetapi nama variabelnya.
Steven
18
Ada masalah dengan c\ ketika diikuti langsung oleh variabel: …c\$VAR…Garis miring terbalik akan keluar dari dolar. Dalam hal ini saya (bash / sed di Ubuntu 15.10) harus menulis…c\\$VAR…
Januari
6
pada penggunaan mac: sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (ketika param pertama kosong itu diedit dalam file, jika tidak membuat cadangan)
AndreDurao
152

Anda perlu menggunakan wildard ( .*) sebelum dan sesudah untuk mengganti seluruh baris:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
Thor
sumber
Terima kasih, buat saya berfungsi: sed 's /.* <expression>. * / <expression> SE_LABEL = ABC <expression> / g' MYR2.xml> test.txt
Nasri Najib
9
Ini bekerja pada Mac OS X Yosemite dengan pengecualian bahwa saya menggunakan flag -i dan -e sebagai berikut:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
Kent Bull
1
@ KentJohnson Saya pikir Anda memiliki penawaran yang tidak cocok dalam perintah Anda.
HashHazard
@ MBarnett Anda benar, saya harus memiliki dua kutipan ganda di sana.
Kent Bull
2
Hanya untuk info lengkapnya. Untuk membuatnya inplace, seseorang dapat menambahkan -iopsi
Temak
20

Jawaban yang diterima tidak berfungsi untuk saya karena beberapa alasan:

  • versi sed saya tidak suka -idengan ekstensi nol panjang
  • sintaks c\perintahnya aneh dan saya tidak bisa menjalankannya
  • Saya tidak menyadari beberapa masalah saya berasal dari tebasan yang tidak terhindarkan

Jadi di sini adalah solusi yang saya temukan yang menurut saya harus bekerja untuk kebanyakan kasus:

function escape_slashes {
    sed 's/\//\\\//g' 
}

function change_line {
    local OLD_LINE_PATTERN=$1; shift
    local NEW_LINE=$1; shift
    local FILE=$1

    local NEW=$(echo "${NEW_LINE}" | escape_slashes)
    sed -i .bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
    mv "${FILE}.bak" /tmp/
}

Jadi penggunaan sampel untuk memperbaiki masalah yang ditimbulkan:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
Skensell
sumber
18

Jawaban di atas:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

Berfungsi dengan baik jika string / jalur pengganti bukan variabel.

Masalahnya adalah bahwa pada Redhat 5 \setelah clolos $. Dobel \\juga tidak bekerja (setidaknya pada Redhat 5).

Melalui hit dan trial, saya menemukan bahwa \setelah citu berlebihan jika string / garis pengganti Anda hanya satu baris. Jadi saya tidak menggunakan \setelah itu c, menggunakan variabel sebagai garis pengganti tunggal dan itu adalah sukacita.

Kode akan terlihat seperti:

sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo

Perhatikan penggunaan tanda kutip ganda sebagai ganti tanda kutip tunggal.

pengguna4256255
sumber
Anda masih dapat menggunakan tanda kutip tunggal seperti ini: sed -i '/ TEXT_TO_BE_REPLACED / c' "$ VARIABLE" '' / tmp / foo
Alistair McIntyre
4

Semua jawaban yang diberikan sejauh ini mengasumsikan bahwa Anda tahu sesuatu tentang teks yang akan diganti yang masuk akal, karena itulah yang ditanyakan OP. Saya memberikan jawaban yang mengasumsikan Anda tidak tahu apa-apa tentang teks yang akan diganti dan bahwa mungkin ada baris terpisah dalam file dengan konten yang sama atau serupa yang Anda tidak ingin diganti. Selanjutnya, saya berasumsi Anda tahu nomor baris dari baris yang akan diganti.

Contoh berikut menunjukkan penghapusan atau perubahan teks dengan nomor baris tertentu:

# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
#   just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE

# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
b_laoshi
sumber
3

untuk manipulasi file konfigurasi

saya datang dengan solusi ini yang terinspirasi oleh jawaban skensell

configLine [searchPattern] [replaceLine] [filePath]

itu akan:

  • buat file jika tidak ada
  • ganti seluruh baris (semua baris) di mana searchPattern cocok
  • tambahkan replaceLine di akhir file jika pola tidak ditemukan

Fungsi:

function configLine {
  local OLD_LINE_PATTERN=$1; shift
  local NEW_LINE=$1; shift
  local FILE=$1
  local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
  touch "${FILE}"
  sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
  if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
  then
    echo "${NEW_LINE}" >> "${FILE}"
  fi
}

keajaiban status keluar gila berasal dari https://stackoverflow.com/a/12145797/1262663

Gandum
sumber
2
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$ 
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'

ini bekerja dengan baik

nkl
sumber
1

Di makefile saya, saya menggunakan ini:

@sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md

PS: JANGAN lupa bahwa -i sebenarnya mengubah teks dalam file ... jadi jika pola yang Anda tetapkan sebagai "Revisi" akan berubah, Anda juga akan mengubah pola untuk diganti.

Contoh output:

Proyek-Abc ditulis oleh John Doe

Revisi: 1190

Jadi jika Anda menetapkan pola "Revisi: 1190" itu jelas tidak sama dengan yang Anda definisikan sebagai "Revisi:" saja ...

Martin Pfeffer
sumber
1
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file    
done 

file find_replace berisi 2 kolom, c1 dengan pola untuk mencocokkan, c2 dengan penggantian, lingkaran sed menggantikan setiap baris yang menggabungkan salah satu pola variabel 1

sjordi
sumber
Tidak, ini salah dalam beberapa hal. Jalankan sedsekali dengan file skrip yang berisi semua penggantian yang ingin Anda lakukan. Menjalankan sed -ifile yang sama berulang kali adalah antipattern yang mengerikan.
tripleee
0

Ini mirip dengan yang di atas ..

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
Annapureddy Hari
sumber
3
Bahwa perubahan FOO=TEXT_TO_BE_REPLACEDuntuk FOO=This line ...jadi tidak memenuhi spesifikasi.
Jens
Ya .. Persyaratan kami adalah mengganti seluruh baris dengan 'Baris ini dihapus oleh admin.' jika kami menemukan pattren kunci 'TEXT_TO_BE_REPLACED'. Perintah di atas memuaskan. Koreksi saya jika pemahaman saya salah .. @ Jan
Annapureddy Hari
@AnnapureddyHari jawaban ini tidak berfungsi jika teks sebelum atau sesudah string pencarian memiliki sesuatu di dalamnya selain A-Za-z0-9. Gagal jika ada tanda sama dengan, seperti yang ditunjukkan Jens. Bagian "FOO =" akan tetap; Anda belum mengganti seluruh baris. Kode ini tidak memperhatikan apa yang mungkin ada dalam file. Jika maksud Anda wildcard, Anda harus meletakkan wildcard, seperti yang ditunjukkan Thor.
msouth
0

Perintah di bawah ini berfungsi untuk saya. Yang bekerja dengan variabel

sed -i "/\<$E\>/c $D" "$B"
Ritesh Borkar
sumber
Tetapi persyaratan baru saya adalah untuk SKIP berkomentar (dimulai dengan #) saat mengganti. Saat kami mengganti baris lengkap, ini akan menggantikan baris yang dikomentari juga dan Anda akan berakhir dengan properti duplikat. Jika ada yang punya solusi untuk ini, beri tahu saya.
Ritesh Borkar
-1

Saya sangat sering menggunakan regex untuk mengekstrak data dari file yang baru saja saya gunakan untuk mengganti kutipan literal \"dengan //tidak ada :-)

cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt
staf
sumber