apakah penghapusan baris in-place pada sistem file lengkap?


11

Karena bug aplikasi belum terdiagnosis, saya memiliki beberapa ratus server dengan disk penuh. Ada satu file yang telah diisi dengan duplikat baris - bukan file log, tetapi file lingkungan pengguna dengan definisi variabel (jadi saya tidak bisa hanya menghapus file).

Saya menulis sedperintah sederhana untuk memeriksa baris yang ditambahkan secara salah dan menghapusnya, dan mengujinya pada salinan lokal file tersebut. Ini berfungsi sebagaimana dimaksud.

Namun, ketika saya mencobanya di server dengan disk penuh, saya mendapatkan kira-kira kesalahan berikut (ini dari memori, bukan salin dan tempel):

sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname

Tentu saja, saya tahu tidak ada ruang yang tersisa. Itu sebabnya saya mencoba menghapus barang! ( sedPerintah yang saya gunakan akan mengurangi 4000 baris file menjadi sekitar 90 baris.)

sedPerintah saya adilsed -i '/myregex/d' /path/to/file/filename

Apakah ada cara saya dapat menerapkan perintah ini meskipun disk penuh?

(Ini harus otomatis, karena saya perlu menerapkannya ke beberapa ratus server sebagai perbaikan cepat.)

(Jelas bug aplikasi perlu didiagnosis, tetapi sementara itu server tidak berfungsi dengan benar ....)


Pembaruan: Situasi yang saya hadapi diselesaikan dengan menghapus sesuatu yang saya temukan dapat saya hapus, tetapi saya masih menginginkan jawaban untuk pertanyaan ini , yang akan membantu di masa depan dan untuk orang lain.

/tmpadalah no-go; ada di sistem file yang sama.

Sebelum saya membebaskan ruang disk, saya melakukan pengujian dan menemukan bahwa saya dapat menghapus baris videngan membuka file dan menjalankan :g/myregex/ddan kemudian berhasil menyimpan perubahan dengan :wq. Tampaknya mungkin untuk mengotomatisasi ini, tanpa menggunakan sistem file terpisah untuk menyimpan file temp .... (?)



1
sed -imembuat salinan sementara untuk beroperasi. Saya menduga itu edakan lebih baik untuk ini, meskipun saya tidak cukup akrab untuk melarang solusi yang sebenarnya
Eric Renouf

2
Dengan edAnda menjalankan: printf %s\\n g/myregex/d w q | ed -s infiletetapi perlu diingat beberapa implementasi juga menggunakan file sementara seperti sed(Anda dapat mencoba busybox ed - afaik itu tidak membuat file sementara)
don_crissti

1
@ Kartu Memori - tidak andal w / echo. gunakan printf. dan sedtambahkan beberapa char yang kamu jatuhkan di baris terakhir agar kamu tidak kehilangan trailing blank. juga, shell Anda harus dapat menangani seluruh file dalam satu baris perintah. itu risiko Anda - tes dulu. bashsangat buruk pada saat itu (saya pikir itu untuk melakukan ruang w / stack?) dan mungkin sakit pada Anda setiap saat. kedua yang seddirekomendasikan setidaknya akan menggunakan buffer pipa kernel untuk efek yang baik di antara mereka, tetapi metode ini cukup mirip. Sub perintah Anda juga akan memotong fileapakah sed tidak berhasil.
mikeserv

1
@ Kartu Memori - coba sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}dan jika berhasil baca sisa jawaban saya. '
mikeserv

Jawaban:


10

The -ipilihan tidak benar-benar menimpa file asli. Itu membuat file baru dengan output, kemudian mengganti nama ke nama file asli. Karena Anda tidak memiliki ruang pada sistem file untuk file baru ini, gagal.

Anda harus melakukannya sendiri di skrip Anda, tetapi buat file baru di sistem file yang berbeda.

Juga, jika Anda hanya menghapus baris yang cocok regexp, Anda dapat menggunakan grepbukan sed.

grep -v 'myregex' /path/to/filename > /tmp/filename && mv /tmp/filename /path/to/filename

Secara umum, sangat jarang bagi program untuk menggunakan file yang sama dengan input dan output - segera setelah mulai menulis ke file, bagian dari program yang membaca dari file tidak akan lagi melihat konten asli. Jadi harus menyalin file asli di suatu tempat terlebih dahulu, atau menulis ke file baru dan mengganti nama ketika selesai.

Jika Anda tidak ingin menggunakan file sementara, Anda bisa mencoba menyimpan konten file dalam memori:

file=$(< /path/to/filename)
echo "$file" | grep -v 'myregex' > /path/to/filename

1
Apakah itu mempertahankan izin, kepemilikan, dan stempel waktu? Mungkin rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"dari sini
Hastur

@ Rushur - maksud Anda menyiratkan bahwa sed -ihal itu tidak terjadi?
mikeserv

2
@ Rushur sed -itidak melindungi hal-hal itu. Saya baru saja mencobanya dengan file yang tidak saya miliki, tetapi terletak di direktori yang saya miliki sendiri, dan itu membiarkan saya mengganti file. Pengganti dimiliki oleh saya, bukan pemilik aslinya.
Barmar

1
@ RalphRönnquist Yang pasti, Anda harus melakukannya dalam dua langkah:var=$(< FILE); echo "$FILE" | grep '^"' > FILE
Barmar

1
@Barmar - Anda tidak berfungsi - Anda bahkan tidak tahu Anda berhasil membuka input. The sangat setidaknya Anda bisa lakukan adalah v=$(<file)&& printf %s\\n "$v" >filetetapi Anda bahkan tidak menggunakan &&. Penanya berbicara tentang menjalankannya dalam skrip - mengotomatisasi menimpa file dengan sebagian dari dirinya sendiri. Anda setidaknya harus memvalidasi Anda berhasil membuka input dan output. Shell juga bisa meledak.
mikeserv

4

Begitulah cara sedkerjanya. Jika digunakan dengan -i(di tempat sunting) sedmembuat file sementara dengan konten baru dari file yang diproses. Setelah selesai sed, ganti file yang aktif saat ini dengan yang sementara. Utilitas tidak mengedit file di tempat . Itulah perilaku setiap editor.

Ini seperti Anda melakukan tugas berikut dalam sebuah shell:

sed 'whatever' file >tmp_file
mv tmp_file file

Pada titik ini sed, cobalah untuk menyiram data yang disangga ke file yang disebutkan dalam pesan kesalahan dengan fflush()panggilan sistem:

Untuk aliran keluaran, fflush()memaksa penulisan dari semua data buffer-ruang pengguna untuk output yang diberikan atau memperbarui aliran melalui fungsi tulis yang mendasari aliran.


Untuk masalah Anda, saya melihat solusi dalam memasang sistem file separte (misalnya a tmpfs, jika Anda memiliki cukup memori, atau perangkat penyimpanan eksternal) dan memindahkan beberapa file di sana, memprosesnya di sana, dan memindahkannya kembali.


3

Sejak memposting pertanyaan ini saya telah belajar bahwa exini adalah program yang sesuai dengan POSIX. Ini hampir secara universal terhubung ke vim, tetapi bagaimanapun juga, berikut ini (saya pikir) adalah titik kunci exdalam kaitannya dengan sistem file (diambil dari spesifikasi POSIX):

Bagian ini menggunakan buffer edit istilah untuk menggambarkan teks yang sedang bekerja. Tidak ada implementasi spesifik yang tersirat oleh istilah ini. Semua perubahan pengeditan dilakukan pada buffer edit, dan tidak ada perubahan pada itu akan mempengaruhi file apa pun sampai perintah editor menulis file.

"... akan memengaruhi file apa pun ..." Saya percaya bahwa meletakkan sesuatu pada sistem file (sama sekali, bahkan file temp) akan dianggap sebagai "memengaruhi file apa pun." Mungkin?*

Studi cermat terhadap spesifikasi POSIX untukex mengindikasikan beberapa "gotchas" tentang penggunaan portabel yang dimaksud bila dibandingkan dengan penggunaan scripted umum yang exditemukan secara online (yang dikotori dengan vimperintah-spesifik.)

  1. Implementasi +cmdadalah opsional sesuai dengan POSIX.
  2. Mengizinkan beberapa -copsi juga opsional.
  3. Perintah global :g"memakan" semuanya hingga baris baru yang tidak diloloskan (dan karenanya menjalankannya setelah setiap kecocokan ditemukan untuk regex daripada sekali pada akhir). Jadi -c 'g/regex/d | x'hanya hapus satu instance dan kemudian keluar file.

Jadi menurut apa yang saya teliti, metode yang sesuai dengan POSIX untuk mengedit file pada sistem file lengkap untuk menghapus semua baris yang cocok dengan regex tertentu, adalah:

ex -sc 'g/myregex/d
x' /path/to/file/filename

Ini akan berfungsi asalkan Anda memiliki memori yang cukup untuk memuat file ke buffer.

* Jika Anda menemukan sesuatu yang menunjukkan sebaliknya, tolong sebutkan di komentar.


2
tapi ex menulis ke tmpfiles ... selalu. itu spec'd untuk menulis buffer ke disk secara berkala. bahkan ada perintah khusus untuk menemukan buffer file tmp pada disk.
mikeserv

@ Kartu Memori Terima kasih telah berbagi, saya telah menautkan kembali ke pos serupa di SO . Saya berasumsi ex +g/match/d -scx fileapakah POSIX-compliant juga?
kenorb

@kenorb, tidak cukup, menurut membaca spesifikasi saya - lihat poin 1 saya di jawaban di atas. Kutipan yang tepat dari POSIX adalah "Ex utility harus sesuai dengan Pedoman Sintaks XBD Utility, kecuali untuk penggunaan '-' yang tidak ditentukan, dan bahwa '+' dapat dikenali sebagai pembatas pilihan dan juga '-'."
Wildcard

1
Saya tidak bisa membuktikannya, kecuali dengan menggunakan akal sehat, tetapi saya percaya Anda membaca lebih banyak pernyataan dari spesifikasi daripada yang ada di sana. Saya menyarankan bahwa interpretasi yang lebih aman adalah bahwa tidak ada perubahan pada buffer edit akan memengaruhi file apa pun yang ada sebelum sesi edit dimulai, atau yang dinamai pengguna. Lihat juga komentar saya pada jawaban saya.
G-Man Mengatakan 'Reinstate Monica'

@ G-Man, aku benar-benar berpikir kamu benar; interpretasi awal saya mungkin adalah angan-angan. Namun, karena mengedit file dalam vi bekerja pada sistem file lengkap, saya percaya bahwa dalam kebanyakan kasus itu akan bekerja dengan exbaik - meskipun mungkin tidak untuk file yang ginormous. sed -itidak bekerja pada sistem file lengkap tanpa memperhatikan ukuran file.
Wildcard

2

Gunakan pipanya, Luke!

Baca file | filter | menulis kembali

sed 's/PATTERN//' BIGFILE | dd of=BIGFILE conv=notrunc

dalam hal sedini tidak membuat file baru dan hanya mengirim output yang disalurkan ke ddmana membuka file yang sama . Tentu saja orang dapat menggunakannya grepdalam kasus tertentu

grep -v 'PATTERN' BIGFILE | dd of=BIGFILE conv=notrunc

kemudian potong sisanya.

dd if=/dev/null of=BIGFILE seek=1 bs=BYTES_OF_SED_OUTPUT

1
Apakah Anda memperhatikan bagian "sistem file lengkap" dari pertanyaan?
Wildcard

1
@ Kartu Memori, apakah sedselalu menggunakan file temp? greptoh tidak akan
Leben Gleben

Ini tampaknya merupakan alternatif untuk spongeperintah. Ya, seddengan -iselalu membuat file lilke "seduyUdmw" dengan 000 hak.
Pablo A

1

Seperti dicatat dalam jawaban lain, sed -iberfungsi dengan menyalin file ke file baru di direktori yang sama , membuat perubahan dalam proses, dan kemudian memindahkan file baru di atas yang asli. Itu sebabnya itu tidak berhasil.  ed(editor baris asli) bekerja dengan cara yang agak mirip, tetapi, terakhir kali saya memeriksa, ini digunakan /tmpuntuk file awal. Jika Anda /tmpmenggunakan sistem file yang berbeda dari yang penuh, eddapat melakukan pekerjaan untuk Anda.

Coba ini (di prompt shell interaktif Anda):

$ ed / path / ke / file / nama file
P
g / myregex / d
w
q

The P(yang merupakan ibukota P) tidak benar-benar diperlukan. Menyala saat diminta; tanpanya, Anda bekerja dalam kegelapan, dan beberapa orang merasa ini membingungkan. The wdan qyang w ritus dan q uit.

edterkenal karena diagnostik samar. Jika pada suatu saat ia menampilkan sesuatu selain prompt (yang ada *) atau sesuatu yang jelas merupakan konfirmasi operasi yang berhasil ( terutama jika mengandung a ?), jangan menulis file (dengan w). Berhenti saja ( q). Jika itu tidak membuat Anda keluar, coba katakan qlagi.

Jika /tmpdirektori Anda berada di sistem file yang penuh (atau jika sistem file penuh, juga), cobalah untuk menemukan ruang di suatu tempat. chaos disebutkan memasang tmpfs atau perangkat penyimpanan eksternal (misalnya, flash drive); tetapi, jika Anda memiliki beberapa filesystem, dan mereka tidak semua penuh, Anda dapat hanya menggunakan salah satu dari yang sudah ada lainnya. chaos menyarankan untuk menyalin file ke sistem file lain, mengeditnya di sana (dengan sed), dan kemudian menyalinnya kembali. Pada titik ini, itu mungkin solusi paling sederhana. Tetapi alternatifnya adalah membuat direktori yang dapat ditulis pada sistem file yang memiliki beberapa ruang kosong, mengatur variabel lingkungan TMPDIRuntuk menunjuk ke direktori itu, dan kemudian jalankan ed. (Pengungkapan: Saya tidak yakin apakah ini akan berhasil, tetapi tidak ada salahnya.)

Setelah Anda mulai edbekerja, Anda dapat mengotomatisasi ini dengan melakukannya

ed nama file << EOF
g / myregex / d
w
q
EOF

dalam naskah. Atau , seperti yang disarankan oleh don_crissti.printf '%s\n' 'g/myregex/d' w q | ed -s filename


Hmmm. Dapatkah hal yang sama dilakukan (baik dengan edatau dengan ex) sedemikian rupa sehingga memori digunakan daripada sistem file yang terpisah? Itulah tujuan saya sebenarnya (dan alasan saya belum menerima jawaban.)
Wildcard

Hmm. Ini mungkin lebih rumit daripada yang saya sadari. Saya mempelajari sumber dari edbanyak tahun yang lalu. Masih ada hal-hal seperti komputer 16-bit, di mana proses dibatasi pada ruang alamat 64K (!), Sehingga gagasan editor membaca seluruh file ke dalam memori adalah non-starter. Sejak itu, tentu saja, memori menjadi lebih besar - tetapi begitu juga disk dan file. Karena disk sangat besar, orang tidak merasa perlu berurusan dengan kemungkinan /tmpkehabisan ruang. Saya hanya melihat sekilas kode sumber versi terbaru ed, dan sepertinya ... (Lanjutan)
G-Man Berkata 'Reinstate Monica'

(Lanjutkan) ... untuk menerapkan "edit buffer" sebagai file temp, tanpa syarat - dan saya tidak dapat menemukan indikasi bahwa versi ed(atau exatau vi) versi apa pun menawarkan opsi untuk menjaga buffer dalam memori.  Di sisi lain, Penyuntingan Teks dengan ed dan vi - Bab 11: Pemrosesan Teks - Bagian II: Menjelajahi Red Hat Linux - Red Hat Linux 9 Rahasia Profesional - Sistem Linux mengatakan bahwa edpenyunting edit berada di memori, ... (Lanjutan) )
G-Man Mengatakan 'Reinstate Monica'

(Lanjutkan) ... dan Pemrosesan dan Penataan Huruf UNIX oleh Balasubramaniam Srinivasan mengatakan hal yang sama tentang vi(yang merupakan program yang sama dengan ex). Saya percaya bahwa mereka hanya menggunakan kata-kata yang ceroboh dan tidak tepat - tetapi, jika itu ada di Internet (atau dalam bentuk cetak), itu pasti benar, bukan? Anda membayar uang Anda dan mengambil pilihan Anda.
G-Man Mengatakan 'Reinstate Monica'

Tapi bagaimanapun, saya telah menambahkan jawaban baru.
G-Man Mengatakan 'Reinstate Monica'

1

Anda dapat memotong file dengan cukup mudah jika Anda bisa mendapatkan jumlah byte ke offset Anda dan garis Anda muncul dari titik awal hingga akhir.

o=$(sed -ne'/regex/q;p' <file|wc -c)
dd if=/dev/null of=file bs="$o" seek=1

Atau jika Anda ${TMPDIR:-/tmp}menggunakan beberapa sistem file lain, mungkin:

{   cut -c2- | sed "$script" >file
} <file <<FILE
$(paste /dev/null -)
FILE

Karena (kebanyakan) shell meletakkan dokumen-dokumennya di sini dalam file temp yang dihapus. Ini sangat aman selama <<FILEdeskriptor dipertahankan dari awal hingga akhir dan ${TMPDIR:-/tmp}memiliki ruang sebanyak yang Anda butuhkan.

Kerang yang tidak menggunakan file temp menggunakan pipa, jadi tidak aman untuk menggunakan cara ini. Kerang ini biasanya ashderivatif seperti busybox, dash, BSD sh- zsh, bash, ksh, dan Bourne shell, bagaimanapun, semua file menggunakan temp.

rupanya saya menulis program shell kecil Juli lalu untuk melakukan sesuatu yang sangat seperti ini


Jika /tmptidak layak, maka selama Anda dapat memuat file dalam memori seperti ...

sed 'H;$!d;x' <file | { read v &&
sed "$script" >file;}

... sebagai kasus umum paling tidak akan memastikan bahwa file telah sepenuhnya disangga oleh sedproses pertama sebelum mencoba untuk memotong file in / out.

Solusi yang lebih tepat sasaran dan efisien adalah:

sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}

... karena toh tidak akan mengganggu garis penyangga yang ingin Anda hapus.

Tes kasus umum:

{   nums=/tmp/nums
    seq 1000000 >$nums
    ls -lh "$nums"
    wc -l  "$nums"
    sed 'H;$!d;x' <$nums | { read script &&  ### read always gets a blank
    sed "$script" >$nums;}
    wc -l  "$nums"
    ls -lh "$nums"
}

-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums
1000000 /tmp/nums
1000000 /tmp/nums
-rw-r--r-- 1 mikeserv mikeserv 6.6M Dec 22 20:26 /tmp/nums

Saya akui saya belum pernah membaca jawaban Anda secara mendetail sebelumnya, karena ini dimulai dengan solusi yang tidak dapat dijalankan (untuk saya) yang melibatkan jumlah byte (berbeda di antara masing-masing banyak server) dan /tmpyang berada pada sistem file yang sama. Saya suka sedversi ganda Anda . Saya pikir kombinasi jawaban Barmar dan jawaban Anda mungkin yang terbaik, kira-kira seperti: myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar (Untuk kasus ini saya tidak peduli tentang menjaga jalur baru.)
Wildcard

2
@ Kartu Memori - bisa jadi itu. tetapi Anda tidak harus menggunakan shell seperti database. itu sed| cathal di atas tidak pernah membuka output kecuali sedtelah buffered seluruh file dan siap untuk mulai menulis semuanya untuk output. Jika mencoba untuk buffer file dan gagal - readtidak berhasil karena menemukan EOF di |pipa sebelum membaca baris pertama pertama dan karenanya cat >out tidak pernah terjadi sampai waktunya untuk menuliskannya dari memori sepenuhnya. meluap atau sesuatu seperti itu baru saja gagal. juga seluruh pipa mengembalikan keberhasilan atau kegagalan setiap saat. menyimpannya di var lebih berisiko.
mikeserv

@Wildcard - jika saya benar - benar menginginkannya dalam variabel juga, saya pikir id melakukannya seperti: file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shitejadi file output dan var akan ditulis secara bersamaan, yang akan membuat salah satu atau cadangan yang efektif , yang merupakan satu-satunya alasan Anda ingin mempersulit hal-hal lebih jauh dari yang Anda butuhkan.
mikeserv

@ mikeserv: Saya menangani masalah yang sama dengan OP sekarang dan saya menemukan solusi Anda sangat berguna. Tapi saya tidak mengerti penggunaan read scriptdan read vjawaban Anda. Jika Anda dapat menguraikan lebih lanjut tentang itu saya akan sangat dihargai, terima kasih!
sylye

1
@sylye - $scriptadalah sedskrip yang akan Anda gunakan untuk menargetkan bagian file apa pun yang Anda inginkan; ini skrip yang memberi Anda hasil akhir yang Anda inginkan dalam aliran. vhanyalah pengganti untuk baris kosong. dalam sebuah bashshell tidak diperlukan karena bashsecara otomatis akan menggunakan $REPLYvariabel shell sebagai gantinya jika Anda tidak menentukan satu, tetapi POSIXly Anda harus selalu melakukannya. Saya senang Anda menemukannya berguna. semoga sukses dengan itu. im mikeserv @ gmail jika Anda membutuhkan sesuatu secara mendalam. saya harus memiliki komputer lagi dalam beberapa hari
mikeserv

0

Jawaban ini meminjam ide-ide dari jawaban lain ini dan jawaban lain ini tetapi dibangun di atasnya, menciptakan jawaban yang lebih umum berlaku:

num_bytes = $ (sed '/ myregex / d' / path / ke / file / nama file | wc -c)
sed '/ myregex / d' / path / ke / file / nama file 1 <> / path / ke / file / nama file 
dd jika = / dev / null dari = / path / ke / file / nama file bs = "$ num_bytes" seek = 1

Baris pertama menjalankan sedperintah dengan output ditulis ke output standar (dan bukan ke file); khusus, ke pipa wcuntuk menghitung karakter. Baris kedua juga menjalankan sedperintah dengan keluaran ditulis ke keluaran standar, yang, dalam hal ini diarahkan ke file input dalam mode baca / tulis timpa (tanpa terpotong), yang dibahas di sini . Ini agak berbahaya untuk dilakukan; aman hanya ketika perintah filter tidak pernah meningkatkan jumlah data (teks); yaitu, untuk setiap n byte yang dibaca, ia menulis n atau lebih sedikit byte. Ini tentu saja berlaku untuk sed '/myregex/d'perintah; untuk setiap baris yang dibacanya, ia menulis baris yang sama persis, atau tidak sama sekali. (Contoh lain:s/foo/fu/atau s/foo/bar/akan aman, tetapi s/fu/foo/dan s/foo/foobar/tidak akan.)

Sebagai contoh:

$ cat filename
It was
a dark and stormy night.
$ sed '/was/d' filename 1<> filename
$ cat filename
a dark and stormy night.
night.

karena 32 byte data ini:

I  t     w  a  s \n  a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

ditimpa dengan 25 karakter ini:

a     d  a  r  k     a  n  d     s  t  o  r  m  y     n  i  g  h  t  . \n

meninggalkan tujuh byte yang night.\ntersisa di akhir.

Akhirnya, ddperintah mencari ke bagian akhir dari data yang baru digosok (byte 25 dalam contoh ini) dan menghapus sisa file; yaitu, itu memotong file pada saat itu.


Jika, karena alasan apa pun, 1<>triknya tidak berfungsi, Anda dapat melakukannya

sed '/ myregex / d' / path / ke / file / nama file | dd = / path / ke / file / nama file conv = notrunc

Juga, perhatikan bahwa, selama semua yang Anda lakukan adalah menghapus garis, yang Anda butuhkan adalah grep -v myregex(seperti yang ditunjukkan oleh Barmar ).


-3

sed-i 'd' / path / ke / file / nama file


1
Hai! Akan lebih baik untuk menjelaskan sedetail yang relevan bagaimana solusi Anda bekerja dan menjawab pertanyaan.
Dhag

2
Ini bukan jawaban yang mengerikan. (a) Ini akan gagal pada sistem file lengkap, seperti perintah asli saya; (B) Jika itu berhasil, itu akan mengosongkan file SELURUH, bukan hanya baris yang cocok dengan regex saya.
Wildcard
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.