Jika Anda ingin mengganti kata kunci dengan string menggunakan sed, sed berusaha keras untuk mengartikan string pengganti Anda. Jika string pengganti kebetulan memiliki karakter yang dianggap spesial, seperti karakter '/', itu akan gagal, kecuali tentu saja Anda bermaksud string pengganti Anda memiliki karakter yang memberi tahu bagaimana bertindak.
Ex:
VAR="hi/"
sed "s/KEYWORD/$VAR/g" somefile
Apakah ada cara untuk memberitahu sed untuk tidak mencoba menafsirkan string pengganti untuk karakter khusus? Yang saya inginkan adalah dapat mengganti kata kunci dalam file dengan konten variabel, apa pun kontennya.
bash
shell-script
sed
Tal
sumber
sumber
sed
dan membuatnya tidak istimewa, cukup melarikan diri backslash mereka.VAR='hi\/'
tidak memberikan masalah seperti itu.sed(1)
hanya menafsirkan apa yang didapatnya. Dalam kasus Anda, ia mendapatkannya melalui interpolasi shell. Saya percaya Anda tidak dapat melakukan apa yang Anda inginkan, tetapi periksa manualnya. Saya tahu di Perl (yang membuatsed
penggantian lumayan , dengan ekspresi reguler lebih kaya) Anda dapat menentukan string yang harus diambil secara harfiah, sekali lagi, periksa manual.Jawaban:
Hanya ada 4 karakter khusus di bagian pengganti: \, &, baris baru dan pembatas ( ref )
sumber
s///
adalah tidak ekspresi reguler, itu benar-benar hanya string (kecuali untuk backslash-lolos dan&
). Jika string pengganti terlalu panjang, shell satu-liner bukan solusi Anda.Anda dapat menggunakan Perl bukannya sed dengan
-p
(menganggap loop over input) dan-e
(berikan program pada baris perintah). Dengan Perl, Anda dapat mengakses variabel lingkungan tanpa menyisipkan ini di shell. Perhatikan bahwa variabel perlu diekspor :Jika Anda tidak ingin mengekspor variabel di mana-mana, maka berikan saja untuk proses itu saja:
Perhatikan, bahwa sintaks ekspresi reguler Perl secara default sedikit berbeda dari sed.
sumber
PATTERN
variabel lingkungan , bukan argumen. Bagaimanapun, kesalahan ini akan menjadiE2BIG
, yang Anda akan dapatkan jika Anda digunakansed
.Solusi yang paling sederhana yang masih akan menangani sebagian besar nilai variabel dengan benar, adalah dengan menggunakan karakter non-cetak sebagai pembatas untuk
sed
perintah pengganti.Di dalamnya
vi
Anda dapat menghindari karakter kontrol apa pun dengan mengetikkan Ctrl-V (lebih umum ditulis sebagai^V
). Jadi, jika Anda menggunakan beberapa karakter kontrol (saya sering menggunakan^A
sebagai pembatas dalam kasus ini) makased
perintah Anda hanya akan pecah jika karakter yang tidak tercetak itu ada dalam variabel yang Anda masukkan .Jadi, Anda mengetik
"s^V^AKEYWORD^V^A$VAR^V^Ag"
dan apa yang akan Anda dapatkanvi
:Ini akan berfungsi selama
$VAR
tidak mengandung karakter non-cetak^A
— yang sangat tidak mungkin.Tentu saja, jika Anda memasukkan input pengguna ke nilai
$VAR
, maka semua taruhan dimatikan dan Anda sebaiknya membersihkan input Anda secara menyeluruh daripada mengandalkan karakter kontrol yang sulit diketik untuk rata-rata pengguna.Sebenarnya ada lebih banyak yang harus diperhatikan daripada string pembatas. Misalnya,
&
ketika ada dalam string pengganti, berarti "seluruh teks yang cocok." Misalnya,s/stu../my&/
akan menggantikan "barang" dengan "mystuff", "tersengat" dengan "mystung", dll Jadi jika Anda mungkin memiliki setiap karakter dalam variabel yang Anda menjatuhkan berada di sebagai string pengganti, tetapi Anda ingin menggunakan literal yang nilai variabel saja, maka Anda memiliki beberapa sanitasi data yang harus dilakukan sebelum Anda dapat menggunakan variabel sebagai string penggantised
. (Namun, sanitasi data dapat dilakukan dengansed
juga.)sumber
sed
'si
perintah nsert. Tetapised
bukan alat yang baik untuk memproses sejumlah besar teks dengan cara yang kompleks. Saya akan mengirim jawaban lain yang menunjukkan bagaimana melakukan iniawk
.Anda bisa menggunakan a
,
atau|
sebaliknya dan itu akan menganggapnya sebagai pemisah dan secara teknis Anda bisa menggunakan apa sajadari halaman manual
Seperti yang Anda lihat, Anda harus mulai dengan \ sebelum pemisah Anda di awal, kemudian Anda dapat menggunakannya sebagai pemisah.
dari dokumentasi http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command :
Contoh:
sed -e 'somevar|s|foo|bar|'
echo "Hello all" | sed "s_all_user_"
echo "Hello all" | sed "s,all,user,"
echo "Hello/ World" | sed "s,Hello/,Neo,"
sumber
/
dan itu akan mengabaikan/
bahagia karena saya baru saja menunjukkan .. pada kenyataannya, Anda bahkan dapat mencarinya dan menggantinya dalam string >>> saya telah diedit dengan contoh >>> ini hal-hal tidak begitu aman dan Anda selalu akan menemukan pria yang lebih pintarsed
di tempat pertama, apa proyek Anda?bash
adalah TIDAK untuk manipulasi string. Sama sekali, sama sekali, sama sekali. Ini untuk manipulasi file dan koordinasi perintah . Kebetulan memiliki beberapa fungsionalitas berguna untuk string, tetapi sangat terbatas dan tidak terlalu cepat sama sekali jika itu hal utama yang Anda lakukan. Lihat "Mengapa menggunakan shell loop untuk memproses teks yang dianggap praktik buruk?" Beberapa alat yang yang dirancang untuk pengolahan teks, dalam urutan dari yang paling dasar untuk paling kuat:sed
,awk
dan Perl.Jika berbasis garis dan hanya satu baris untuk diganti, saya sarankan untuk menggunakan file itu sendiri dengan menggunakan baris pengganti
printf
, menyimpan baris pertama dised
ruang penahanan, dan meletakkannya sesuai kebutuhan. Dengan cara ini Anda tidak perlu khawatir tentang karakter khusus sama sekali. (Satu-satunya asumsi di sini adalah yang$VAR
berisi satu baris teks tanpa baris baru, yang sudah Anda katakan di komentar.) Selain baris baru, VAR dapat berisi apa pun dan ini akan berfungsi apa pun.printf '%s\n'
akan mencetak konten$VAR
sebagai string literal, terlepas dari kontennya, diikuti oleh baris baru. (echo
dalam beberapa kasus akan melakukan hal-hal lain, misalnya jika isi$VAR
diawali dengan tanda hubung — itu akan ditafsirkan sebagai bendera opsi yang diteruskanecho
.)Kawat gigi digunakan untuk menambahkan output
printf
ke isisomefile
saat dilewatkansed
. Ruang putih yang memisahkan kurung kurawal dengan sendirinya penting di sini, seperti halnya titik koma sebelum kurung kurawal penutupan.1{h;d;};
sebagaised
perintah akan menyimpan baris teks pertamased
di ruang penahanan , lalud
hapus baris (daripada mencetaknya)./KEYWORD/
menerapkan tindakan berikut untuk semua baris yang berisiKEYWORD
. Tindakannya adalahg
et, yang mendapatkan konten dari ruang penahan dan menjatuhkannya sebagai ganti ruang pola — dengan kata lain, seluruh baris saat ini. (Ini bukan untuk mengganti hanya bagian dari garis.) Ruang penahanan tidak dikosongkan, dengan cara, hanya disalin ke ruang pola, menggantikan apa pun yang ada.Jika Anda ingin melabuhkan regex Anda sehingga tidak akan cocok dengan garis yang hanya berisi KEYWORD tetapi hanya garis di mana tidak ada yang lain di baris itu selain KEYWORD, tambahkan awal jangkar baris (
^
) dan akhir jangkar baris ($
) ke regex Anda:sumber
Anda dapat melakukan backslash-escape dari garis miring di string pengganti Anda, menggunakan ekspansi parameter substitusi pola Bash. Agak berantakan karena garis miring ke depan juga harus diloloskan ke Bash.
keluaran
Anda bisa menempatkan ekspansi parameter langsung ke perintah sed Anda:
tapi saya pikir bentuk pertama sedikit lebih mudah dibaca. Dan tentu saja jika Anda akan menggunakan kembali pola penggantian yang sama dalam beberapa perintah sed, masuk akal untuk hanya melakukan konversi sekali.
Pilihan lain adalah menggunakan skrip yang ditulis dengan awk, perl atau Python, atau program C, untuk melakukan pergantian Anda alih-alih menggunakan sed.
Berikut adalah contoh sederhana dalam Python yang berfungsi jika kata kunci yang akan diganti adalah baris lengkap dalam file input (tidak termasuk baris baru). Seperti yang Anda lihat, ini pada dasarnya algoritma yang sama dengan contoh Bash Anda, tetapi membaca file input lebih efisien.
sumber
\x
urutan escape -style. Atau menggunakan program yang dapat menangani input sewenang-wenang, seperti yang saya sebutkan di paragraf terakhir saya.Inilah cara saya pergi:
ini berfungsi dengan baik dalam kasus saya karena kata kunci saya ada pada satu baris dengan sendirinya. Jika kata kunci sejalan dengan teks lain, ini tidak akan berfungsi.
Saya masih sangat ingin tahu apakah ada cara mudah untuk melakukan ini yang tidak melibatkan pengkodean solusi saya sendiri.
sumber
echo
sama sekali. Gunakanprintf
sebagai gantinya. Dan melakukan pemrosesan teks dalam shell loop adalah ide yang buruk.read
agak lambat. Ini dimaksudkan untuk memproses input pengguna interaktif, bukan pemrosesan file teks. Ini lambat karena membaca stdin char oleh char, membuat panggilan sistem untuk setiap char.printf "hi\n"
akan membuat printf mencetak baris baru saatecho "hi\n"
mencetak apa adanya.printf
singkatan untuk "format" - argumen pertamaprintf
adalah penentu format . Jika specifier itu%s\n
, yang berarti "string diikuti oleh baris baru", tidak ada dalam argumen berikutnya yang akan ditafsirkan atau diterjemahkanprintf
sama sekali . (Shell masih dapat mengartikannya, tentu saja; terbaik tempelkan semuanya dalam tanda kutip tunggal jika itu string literal, atau tanda kutip ganda jika Anda ingin ekspansi variabel.) Lihat jawaban saya menggunakanprintf
untuk rincian lebih lanjut.