Saya sedang menggunakan vim (args dan argdo) setiap kali saya perlu 'konfirmasi', tetapi bertanya-tanya apakah ada 'sederhana' cara
Yuvi
1
Ini bertentangan dengan tujuan dasar sed - untuk mengotomatisasi pengeditan melalui aliran.
teppic
@teppic tidak terlalu karena Anda masih harus pergi mencari contoh dalam file teks, yang sed dapat dilakukan untuk Anda. Saya pikir pertanyaannya masuk akal, kalau-kalau Anda menambahkan file yang salah ke daftar dan ingin melihat file apa yang sedang Anda edit
Kolob Canyon
Jawaban:
37
Melakukannya dengan sedmungkin tidak akan mungkin karena itu adalah editor aliran non-interaktif. Membungkus sednaskah akan membutuhkan pemikiran terlalu banyak. Lebih mudah melakukannya dengan vim:
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' file.in
Karena disebutkan dalam komentar di bawah, inilah cara ini akan digunakan pada banyak file yang cocok dengan pola globbing nama file tertentu di direktori saat ini:
for fname in file*.txt; do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Atau, jika Anda pertama kali ingin memastikan bahwa file tersebut benar-benar berisi garis yang cocok dengan pola yang diberikan terlebih dahulu, sebelum melakukan penggantian,
for fname in file*.txt; do
grep -q 'PATTERN' "$fname" &&
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' "$fname"
done
Dua loop shell di atas diubah menjadi findperintah yang melakukan hal yang sama tetapi untuk semua file dengan nama tertentu di suatu tempat di dalam atau di bawah beberapa top-dirdirektori,
find top-dir -type f -name 'file*.txt' \
-exec vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' {} \;
Atau, menggunakan loop shell asli dan findmemasukkan nama path ke dalamnya:
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
find top-dir -type f -name 'file*.txt' -exec sh -c '
for pathname do
grep -q "PATTERN" "$pathname" &&
vim -c "%s/PATTERN/REPLACEMENT/gc" -c "wq" "$pathname"
done' sh {} +
Bagaimanapun, Anda tidak ingin melakukan sesuatu seperti for filename in $( grep -rl ... )itu
itu akan membutuhkan grepselesai berjalan bahkan sebelum memulai iterasi pertama dari loop, yang tidak elegan , dan
nama path yang dikembalikan oleh grepakan dibagi menjadi kata-kata di spasi putih, dan kata-kata ini akan mengalami globbing nama file (ini mendiskualifikasi nama path yang berisi spasi dan karakter khusus).
searchandreplace () {
if [ -z $2 ] ; then
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
else
realpath * | xargs grep -ins --directories=skip --color=always "$1" | nl -s "."
exp=$(realpath * | xargs grep -ins --directories=skip --color=never "$1" | cut -d':' -f1,2 | awk -F':' '{print $2 $1}' | sed -E "s|^[0-9]+|sed -i \'&s/|; s|//|/"$1"/"$2"/g\' /|")
IFS=$'\n'
for d in $exp
do
read -p "Exec $(echo -e -n "\033[1;31m$d\033[0m")? " -e REP
if [ "$REP" = y ]; then
echo `bash -c $d`;
fi
done
fi
}
Jika Anda memberi makan ini dengan argumen tunggal - searchandreplace "AAA"- itu hanya mencari string itu di direktori saat ini, tetapi jika Anda memberi makan dengan argumen ganda - searchandreplace AAA BBB- maka di samping itu juga meminta Anda untuk mengganti yang pertama dengan yang kedua "baris oleh baris "untuk setiap baris.
Jawaban:
Melakukannya dengan
sed
mungkin tidak akan mungkin karena itu adalah editor aliran non-interaktif. Membungkussed
naskah akan membutuhkan pemikiran terlalu banyak. Lebih mudah melakukannya denganvim
:Karena disebutkan dalam komentar di bawah, inilah cara ini akan digunakan pada banyak file yang cocok dengan pola globbing nama file tertentu di direktori saat ini:
Atau, jika Anda pertama kali ingin memastikan bahwa file tersebut benar-benar berisi garis yang cocok dengan pola yang diberikan terlebih dahulu, sebelum melakukan penggantian,
Dua loop shell di atas diubah menjadi
find
perintah yang melakukan hal yang sama tetapi untuk semua file dengan nama tertentu di suatu tempat di dalam atau di bawah beberapatop-dir
direktori,Atau, menggunakan loop shell asli dan
find
memasukkan nama path ke dalamnya:Bagaimanapun, Anda tidak ingin melakukan sesuatu seperti
for filename in $( grep -rl ... )
itugrep
selesai berjalan bahkan sebelum memulai iterasi pertama dari loop, yang tidak elegan , dangrep
akan dibagi menjadi kata-kata di spasi putih, dan kata-kata ini akan mengalami globbing nama file (ini mendiskualifikasi nama path yang berisi spasi dan karakter khusus).Terkait:
sumber
sed
adalah dapat beroperasi pada banyak file, misalnya,sed -i 's/old/new/g' /path/to/*.txt
atau sesuatu yang serupa.for i in $(grep -rl "old"); do vim -c "%s/old/new/gc" -c "wq" "$i"; done
Anda bisa mendapatkan ini dengan melakukan seperti:
Secara khusus, menambahkan
c
pembatas setelah ketiga.Perhatikan bahwa opsi 'c' hanya berfungsi di Vim; Anda tidak akan dapat menggunakannya dengan
sed
di baris perintah.sumber
sed
.vim
, sehingga jawaban ini berlaku; meskipun, jelas vim dan sed memiliki kemampuan yang berbedaAnda dapat membiarkannya
sed
melakukan hal tersebut pada file dan kemudian menyimpan hasilnya ke file sementara yang kemudian dapat Anda tempel secara interaktif ke dalam file asli menggunakansdiff
(lihat http://www.gnu.org/software/diffutils/manual/diffutils.html # Invoking-sdiff ):sumber
Jika Anda memberi makan ini dengan argumen tunggal -
searchandreplace "AAA"
- itu hanya mencari string itu di direktori saat ini, tetapi jika Anda memberi makan dengan argumen ganda -searchandreplace AAA BBB
- maka di samping itu juga meminta Anda untuk mengganti yang pertama dengan yang kedua "baris oleh baris "untuk setiap baris.sumber