Bir dosyanın bash içinde oluşturulabileceğini veya kısaltılabileceğini / üzerine yazılabileceğini nasıl kontrol edebilirim?


15

Kullanıcı ya gibi oluşturulan veya komut bir noktada üzerine yazılacak olan bir dosya yolu ile benim komut dosyası çağırır foo.sh file.txtveya foo.sh dir/file.txt.

Oluşturma veya üzerine yazma davranışı, dosyayı >çıktı yönlendirme operatörünün sağ tarafına koymak veya bir argüman olarak tee(aslında, bir argüman olarak geçirmek teetam olarak yaptığım şeydir) gerekliliklere çok benzer. ).

Senaryonun bağırsaklarından girmeden önce, ben dosya halinde makul kontrolü yapmak istiyorum yapabilirsiniz / üzerine yazılır oluşturulacak, ama aslında bunu oluşturmaz. Bu kontrol mükemmel olmak zorunda değildir ve evet, durumun kontrol ile dosyanın gerçekten yazıldığı nokta arasında değişebileceğini fark ediyorum - ama burada en iyi çaba tipi çözümü ile iyiyim, bu yüzden erken kurtarmalıyım dosya yolunun geçersiz olması durumunda.

Dosyanın oluşturulamamasının nedenlerine örnekler:

  • dosya gibi bir dizin bileşeni içeriyor, dir/file.txtancak dizin dirmevcut değil
  • kullanıcının belirtilen dizinde (veya dizin belirtilmemişse CWD'de yazma izinleri yok)

Evet, "ön" izinlerin denetlenmesinin UNIX Way ™ olmadığını anlıyorum, bunun yerine işlemi denemem ve daha sonra af dilemeliyim. Ancak benim özel komut dosyasında, bu kötü bir kullanıcı deneyimine yol açar ve sorumlu bileşeni değiştiremiyorum.


Argüman her zaman geçerli dizine dayanacak mı yoksa kullanıcı tam bir yol belirleyebilir mi?
jesse_b

@Jesse_b - Kullanıcı gibi mutlak bir yol belirtebilirsiniz sanırım /foo/bar/file.txt. Temelde ben için yolu geçmek teegibi tee $OUT_FILEnereye OUT_FILEkomut satırında geçirilir. Bu hem mutlak hem de göreceli yollarla "çalışmalı", değil mi?
BeeOnRope

@BeeOnRope, hayır tee -- "$OUT_FILE"en azından ihtiyacın olacak . Dosya zaten varsa veya mevcutsa, ancak normal bir dosya değilse (dizin, symlink, fifo) ne olur?
Stéphane Chazelas

@ StéphaneChazelas - iyi kullanıyorum tee "${OUT_FILE}.tmp". Dosya zaten varsa, teebu durumda istenen davranış olan üzerine yazar. Bu bir dizinse teebaşarısız olur (sanırım). symlink% 100 emin değilim?
BeeOnRope

Yanıtlar:


11

Açık test:

if touch /path/to/file; then
    : it can be created
fi

Ancak, zaten orada değilse dosyayı oluşturur. Kendimizden sonra temizleyebiliriz:

if touch /path/to/file; then
    rm /path/to/file
fi

Ancak bu, zaten istemediğiniz ve muhtemelen istemediğiniz bir dosyayı kaldıracaktır.

Bununla birlikte, bunun için bir yolumuz var:

if mkdir /path/to/file; then
    rmdir /path/to/file
fi

Bu dizindeki başka bir nesneyle aynı ada sahip bir dizininiz olamaz. Dizin oluşturabileceğiniz ancak dosya oluşturamayacağınız bir durumu düşünemiyorum. Bu testten sonra, betiğiniz bir geleneksel oluşturmak /path/to/fileve ondan ne isterse onu yapmakta özgür olacaktır .


2
Bu çok düzgün bir şekilde birçok sorunu ele alıyor! Olağandışı çözümün açıklandığı ve gerekçelendirildiği bir kod yorumu, yanıtınızın uzunluğunu aşabilir. :-)
jpaugh

Ayrıca if mkdir /var/run/lockdirbir kilitleme testi olarak kullandım . Bu atomiktir, ancak if ! [[ -f /var/run/lockfile ]]; then touch /var/run/lockfiletest ile touchsöz konusu komut dosyasının başka bir örneğinin başlayabileceği küçük bir zaman dilimine sahiptir .
DopeGhoti

8

Topladığım şeyden, kullanırken

tee -- "$OUT_FILE"

( --- ile başlayan dosya adları için işe yaramayacağını veya işe yaramayacağını unutmayın ), teedosyayı yazmak için açmayı başarabilir.

Bu budur:

  • dosya yolunun uzunluğu PATH_MAX sınırını aşmıyor
  • dosya var (symlink çözümlemesinden sonra) ve tür dizininde değil ve dosyaya yazma izniniz var.
  • dosya yoksa, dosyanın dizin adı (symlink çözümlemesinden sonra) bir dizin olarak bulunur ve dosyaya yazma ve arama izniniz vardır ve dosya adı uzunluğu, dizinin bulunduğu dosya sisteminin NAME_MAX sınırını aşmaz.
  • veya dosya, var olmayan ve bir symlink döngüsü olmayan ancak yukarıdaki ölçütleri karşılayan bir dosyayı işaret eden bir symlink'tir.

Şimdilik vfat, ntfs veya hfsplus gibi dosya adlarının içerebileceği bayt değerleri, disk kotası, işlem sınırı, selinux, apparmor veya diğer güvenlik mekanizmaları, tam dosya sistemi, hiçbir inode kalmadı, cihaz üzerinde sınırlamalar olan dosya sistemlerini yok sayacağız bir sebepten ötürü bu şekilde açılamayan dosyalar, şu anda bir işlem adres alanında eşleştirilen yürütülebilir dosyalar olan dosyaların tümü, dosyayı açma veya oluşturma yeteneğini de etkileyebilir.

İle zsh:

zmodload zsh/system
tee_would_likely_succeed() {
  local file=$1 ERRNO=0 LC_ALL=C
  if [ -d "$file" ]; then
    return 1 # directory
  elif [ -w "$file" ]; then
    return 0 # writable non-directory
  elif [ -e "$file" ]; then
    return 1 # exists, non-writable
  elif [ "$errnos[ERRNO]" != ENOENT ]; then
    return 1 # only ENOENT error can be recovered
  else
    local dir=$file:P:h base=$file:t
    [ -d "$dir" ] &&    # directory
      [ -w "$dir" ] &&  # writable
      [ -x "$dir" ] &&  # and searchable
      (($#base <= $(getconf -- NAME_MAX "$dir")))
    return
  fi
}

Olarak bashya da herhangi bir Bourne benzeri kabuk, sadece yerine

zmodload zsh/system
tee_would_likely_succeed() {
  <zsh-code>
}

ile:

tee_would_likely_succeed() {
  zsh -s -- "$@" << 'EOF'
  zmodload zsh/system
  <zsh-code>
EOF
}

Buradaki zsh-spesifik özellikler $ERRNO(son sistem çağrısının hata kodunu gösterir) ve $errnos[]ilişkili standart C makro adlarına çevrilecek dizidir. Ve $var:h(csh'den) ve $var:P(zsh 5.3 veya üstü gerekir).

bash henüz eşdeğer özelliklere sahip değil.

$file:hcan ile değiştirilmelidir dir=$(dirname -- "$file"; echo .); dir=${dir%??}veya GNU dirname: IFS= read -rd '' dir < <(dirname -z -- "$file").

Çünkü $errnos[ERRNO] == ENOENT, bir yaklaşım ls -Lddosya üzerinde çalışmak ve hata mesajının ENOENT hatasına karşılık gelip gelmediğini kontrol etmek olabilir. Bunu güvenilir ve taşınabilir bir şekilde yapmak zor.

Bir yaklaşım şöyle olabilir:

msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}

(hata mesajı varsayarak uçları ile syserror()çevrilmesi ENOENTve bu tercümenin bir içermez :yerine, o) ve [ -e "$file" ]do:

err=$(ls -Ld -- "$file" 2>&1)

Ve bir ENOENT hatası olup olmadığını kontrol edin

case $err in
  (*:"$msg_for_ENOENT") ...
esac

Bu $file:Pbölüm bash, özellikle FreeBSD'de elde edilmesi en zor olan kısımdır .

FreeBSD'de bir seçeneği ve bir seçeneği kabul eden realpathbir readlinkkomut vardır -f, ancak dosyanın çözümlenmeyen bir symlink olduğu durumlarda kullanılamazlar. perl'S ile aynı Cwd::realpath().

python's en azından bir sürümünün yüklü olduğunu ve bunlardan birine (FreeBSD'de verilmeyen bir komut) gönderme yapan bir komutun olduğunu varsayarak, os.path.realpath()benzer şekilde çalışıyor gibi görünüyor :zsh $file:Ppythonpython

dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}

Ama sonra, her şeyi de yapabilirsiniz python.

Veya tüm bu köşe davalarını ele almamaya karar verebilirsiniz.


Evet, niyetimi doğru anladın. Aslında, orijinal soru belirsizdi: Aslında dosyayı oluşturma hakkında konuştum , ama aslında onu kullanıyorum, bu teeyüzden gereksinim gerçekten dosya yoksa oluşturulabilir veya sıfır olarak kesilebilir ve üzerine yazılabilir yaparsa (veya başka bir şekilde teeele alırsa ).
BeeOnRope

Son düzenlemeniz biçimlendirmeyi sildi gibi görünüyor
D.Ben Knoble

Teşekkürler, @ bishop, @ D.BenKnoble, bkz. Düzenleme.
Stéphane Chazelas

1
@DennisWilliamson, evet, bir kabuk programları çağırmak için bir araçtır ve betiğinizde çağırdığınız her program betiğinizin çalışması için kurulmalıdır, ki bu söylemeye gerek yoktur. macOS varsayılan olarak hem bash hem de zsh yüklü olarak gelir. FreeBSD hiçbiriyle birlikte gelmez. OP'nin bunu bash'da yapmak istemesi için iyi bir nedeni olabilir, belki de zaten yazılmış bir bash betiğine eklemek istedikleri bir şeydir.
Stéphane Chazelas

1
@pipe, yine, bir kabuk bir komut yorumlayıcısıdır. Diğer dillerin tercümanlar gibi çağırmak burada yazılı olduğu birçok kabuk kodu alıntılar göreceksiniz perl, awk, sedveya python(hatta sh, bash...). Kabuk komut dosyası oluşturma, görev için en iyi komutu kullanmakla ilgilidir. İşte hatta perlbir eş değer yoktur zshbireyin $var:P(onun Cwd::realpathBSD gibi davranır realpathya readlink -fsen istediğimiz ederken GNU gibi davranmaya readlink -fya python'ın os.path.realpath())
Stéphane Chazelas

6

Dikkate almak isteyebileceğiniz seçeneklerden biri , dosyayı daha önce oluşturmak , ancak daha sonra komut dosyasında doldurmaktır. execKomutu, dosyayı bir dosya tanımlayıcısında (3, 4 vb.) Açmak için kullanabilir ve daha sonra >&3bu dosyaya içerik yazmak için dosya tanımlayıcısına ( vb.) Yeniden yönlendirme kullanabilirsiniz .

Gibi bir şey:

#!/bin/bash

# Open the file for read/write, so it doesn't get
# truncated just yet (to preserve the contents in
# case the initial checks fail.)
exec 3<>dir/file.txt || {
    echo "Error creating dir/file.txt" >&2
    exit 1
}

# Long checks here...
check_ok || {
    echo "Failed checks" >&2
    # cleanup file before bailing out
    rm -f dir/file.txt
    exit 1
}

# We're ready to write, first truncate the file.
# Use "truncate(1)" from coreutils, and pass it
# /dev/fd/3 so the file doesn't need to be reopened.
truncate -s 0 /dev/fd/3

# Now populate the file, use a redirection to write
# to the previously opened file descriptor.
populate_contents >&3

trapHatalı dosyayı temizlemek için de kullanabilirsiniz , bu yaygın bir uygulamadır.

Bu şekilde, dosyayı oluşturabileceğiniz izinler için gerçek bir kontrol alırsınız , aynı zamanda bunu başaramazsanız, bu başarısız olursa uzun kontrolleri beklemek için zaman harcamamış olursunuz.


GÜNCELLENDİ: Denetimlerin başarısız olması durumunda dosyanın tıkanmasını önlemek için, dosyayı hemen kesmeyen bash'ın fd<>fileyeniden yönlendirmesini kullanın . (Dosyadan okumayı umursamıyoruz, bu sadece bir çözümdür, bu yüzden >>kısaltmayız. resmin.)

İçeriği değiştirmeye hazır olduğumuzda, önce dosyayı kesmeliyiz (aksi takdirde, dosyada olduğundan daha az bayt yazarsak, sondaki baytlar orada kalır.) Kesmeyi kullanabiliriz (1) bu amaçla coreutils komutunu girebilir /dev/fd/3ve dosyamızın yeniden açılması gerekmediği için ( sözde dosyayı kullanarak) açık dosya tanımlayıcısını iletebiliriz . (Yine, teknik olarak daha basit bir şey : >dir/file.txtmuhtemelen işe yarayabilir, ancak dosyayı yeniden açmak zorunda kalmamak daha zarif bir çözümdür.)


Bunu düşünmüştüm, ancak sorun, dosyayı oluşturduğum ve bir süre sonra ona yazacağım nokta arasında başka bir masum hata meydana gelirse, kullanıcı muhtemelen belirtilen dosyanın üzerine yazıldığını bulmak için üzgün olacak .
BeeOnRope

1
@BeeOnRope dosyayı tıkamayan başka bir seçenek, dosyaya hiçbir şey eklemeyi denemektir : echo -n >>fileveya true >> file. Tabii ki, ext4 sadece ek dosyalara sahiptir, ancak bu yanlış pozitif ile yaşayabilirsiniz.
mosvy

1
@BeeOnRope (a) varolan dosyayı veya (b) (tam) yeni dosyayı korumanız gerekiyorsa, bu sormanızdan farklı bir sorudur. Bunun iyi bir yanıtı, yeni dosyayı my-file.txt.backupoluşturmadan önce mevcut dosyayı yeni bir isme (ör. ) Taşımaktır. Başka bir çözüm, yeni dosyayı aynı klasördeki geçici bir dosyaya yazmak ve komut dosyasının geri kalanı başarılı olduktan sonra eski dosyanın üzerine kopyalamaktır - bu son işlem başarısız olursa, kullanıcı sorunu kaybetmeden elle düzeltebilir ilerlemesi.
jpaugh

1
@BeeOnRope "Atomik değiştirme" yolunu (iyi bir yol!) Seçerseniz, genellikle geçici dosyayı son dosyayla aynı dizine yazmak istersiniz (yeniden adlandırmak için aynı dosya sisteminde olması gerekir) (2) başarılı olmak için) ve benzersiz bir ad kullanın (bu nedenle, komut dosyasının iki örneği çalışıyorsa, birbirlerinin geçici dosyalarını tıkamazlar.) Aynı dizine yazmak için geçici bir dosyayı açmak genellikle iyi bir göstergedir daha sonra yeniden adlandırabileceksiniz (son hedef varsa ve bir dizinse hariç), bu yüzden bir dereceye kadar sorunuzu da ele alacaktır.
filbranden

1
@jpaugh - Bunu yapmakta oldukça isteksizim. Bu, kaçınılmaz olarak, "Nasıl kontrol edebilirim ..." sorusuyla ilgisi olmayan çözümleri kabul edebilecek üst düzey sorunumu çözmeye çalışan cevaplara yol açacaktır. Üst düzey sorunum ne olursa olsun, bu sorunun makul bir şekilde yapmak isteyebileceğiniz bir şey olduğunu düşünüyorum. Eğer "senaryomla yaşadığım bu özel sorunu çözmek" olarak değiştirirsem, bu özel soruyu cevaplayan insanlar için haksızlık olur. Bu ayrıntıları buraya ekledim çünkü sordum, ancak birincil soruyu değiştirmek istemiyorum.
BeeOnRope

4

Bence DopeGhoti'nin çözümü daha iyi ama bu da işe yaramalı:

file=$1
if [[ "${file:0:1}" == '/' ]]; then
    dir=${file%/*}
elif [[ "$file" =~ .*/.* ]]; then
    dir="$(PWD)/${file%/*}"
else
    dir=$(PWD)
fi

if [[ -w "$dir" ]]; then
    echo "writable"
    #do stuff with writable file
else
    echo "not writable"
    #do stuff without writable file
fi

İlk if yapı, bağımsız değişkenin tam bir yol olup olmadığını denetler (ile başlar /) ve dirdeğişkeni dizin yoluna kadar son olarak ayarlar /. Aksi takdirde, argüman bir ile başlamaz, /ancak /(bir alt dizini belirterek) içeriyorsa, o dirandaki çalışma dizinine + alt dizin yoluna ayarlanır . Aksi takdirde mevcut çalışma dizinini varsayar. Daha sonra bu dizinin yazılabilir olup olmadığını kontrol eder.


4

testAşağıda belirtildiği gibi normal komutu kullanmaya ne dersiniz ?

FILE=$1

DIR=$(dirname $FILE) # $DIR now contains '.' for file names only, 'foo' for 'foo/bar'

if [ -d $DIR ] ; then
  echo "base directory $DIR for file exists"
  if [ -e $FILE ] ; then
    if [ -w $FILE ] ; then
      echo "file exists, is writeable"
    else
      echo "file exists, NOT writeable"
    fi
  elif [ -w $DIR ] ; then
    echo "directory is writeable"
  else
    echo "directory is NOT writeable"
  fi
else
  echo "can NOT create file in non-existent directory $DIR "
fi

Bu yanıtı neredeyse kopyaladım! Güzel bir giriş, ancak bahsedebilirsiniz man testve bu [ ]teste eşdeğerdir (artık ortak bilgi değil). İşte ben, gelecek davayı ele almak için aşağı cevabımda kullanmak üzere olduğum tek satırlık, gelecek nesiller için:if [ -w $1] && [ ! -d $1 ] ; then echo "do stuff"; fi
Iron Gremlin

4

Kullanıcı deneyiminin sorunuzu yönlendirdiğinden bahsettiniz. Teknik açıdan iyi cevaplara sahip olduğunuz için bir UX açısından cevap vereceğim.

Ön kontrolü yapmak yerine, sonuçları geçici bir dosyaya yazmaya ve en sonunda sonuçları kullanıcının istediği dosyaya yerleştirmeye ne dersiniz ? Sevmek:

userfile=${1:?Where would you like the file written?}
tmpfile=$(mktemp)

# ... all the complicated stuff, writing into "${tmpfile}"

# fill user's file, keeping existing permissions or creating anew
# while respecting umask
cat "${tmpfile}" > "${userfile}"
if [ 0 -eq $? ]; then
    rm "${tmpfile}"
else
    echo "Couldn't write results into ${userfile}." >&2
    echo "Results available in ${tmpfile}." >&2
    exit 1
fi

Bu yaklaşımla iyi: normal mutlu yol senaryosunda istenen işlemi üretir, test ve set atomisite sorununu yan adımlara getirir, gerekirse oluştururken hedef dosyanın izinlerini korur ve uygulanması son derece kolaydır.

Not: Kullanmış olsaydık mv, geçici dosyanın izinlerini koruyorduk - bunu istemiyoruz, bence: izinleri hedef dosyada ayarlandığı gibi tutmak istiyoruz.

Şimdi, kötü: alanın iki katı gerektirir ( cat .. >yapı), hedef dosya olması gereken zamanda yazılabilir değilse, kullanıcıyı manuel iş yapmaya zorlar ve geçici dosyayı (güvenlik olabilir) bırakır veya bakım sorunları).


Aslında, şu anda yaptığım şey bu. Sonuçların çoğunu geçici bir dosyaya yazıyorum ve sonunda son işlem adımını gerçekleştiriyorum ve sonuçları son dosyaya yazıyorum. Sorun, eğer bu son adımın başarısız olması muhtemel ise erken (senaryo başında) kurtarmak istiyorum. Komut dosyası dakikalar veya saatler boyunca katılımsız çalışabilir, bu yüzden gerçekten başarısız olmaya mahkum olduğunu bilmek istersiniz!
BeeOnRope

Tabii, ancak bunun başarısız olmasının pek çok yolu var: disk doldurabilir, yukarı akış dizini kaldırılabilir, izinler değişebilir, diğer bazı önemli şeyler için hedef dosya kullanılabilir ve kullanıcı bu dosyayı yok etmek için atadığını unuttu. operasyon. Bu konuda saf bir UX perspektifinden konuşursak, belki de doğru olan şey bir iş başvurusu gibi davranmaktır: sonunda, tamamlanmaya doğru çalıştığını bildiğinizde, kullanıcıya sonuçta ortaya çıkan içeriğin nerede olduğunu ve sunduğunu söyleyin bir önerdi onlar için komut kendileri taşımak için.
Piskopos

Teorik olarak, evet bunun başarısız olabileceği sonsuz yollar vardır. Uygulamada, bunun başarısız olduğu zamanın büyük çoğunluğu, sağlanan yolun geçerli olmamasıdır. Bazı hileli kullanıcıların aynı anda FS'yi işlemin ortasında işi kırmak için değiştirmesini engelleyemiyorum, ancak kesinlikle geçersiz veya yazılabilir bir yolun # 1 başarısızlık nedenini kontrol edebilirim.
BeeOnRope

2

TL; DR:

: >> "${userfile}"

<> "${userfile}"Ya da farklı olarak touch "${userfile}", bu, dosyanın zaman damgalarında sahte değişiklikler yapmaz ve ayrıca salt yazılır dosyalarla da çalışır.


OP'den:

Dosyanın oluşturulabileceği / üzerine yazılabileceği makul bir kontrol yapmak istiyorum, ancak aslında oluşturulamıyor.

Ve sizin yorumun bir UX perspektiften cevabım :

Bunun başarısız olduğu zamanın büyük çoğunluğu, sağlanan yolun geçerli olmamasıdır. Bazı hileli kullanıcıların FS'nin aynı anda operasyonun ortasında işi kırması için değiştirmesini makul bir şekilde önleyemiyorum, ancak kesinlikle geçersiz veya yazılabilir bir yolun # 1 başarısızlık nedenini kontrol edebilirim.

Tek güvenilir sınama open(2)dosyadır, çünkü yalnızca bu yazılabilirlikle ilgili her soruyu çözer: yol, sahiplik, dosya sistemi, ağ, güvenlik içeriği, vb. Başka herhangi bir sınama yazılabilirliğin bir kısmını ele alır, ancak diğerlerini değil. Bir test alt kümesi istiyorsanız, nihayetinde sizin için neyin önemli olduğunu seçmeniz gerekecektir.

Ama işte başka bir düşünce. Anladığım kadarıyla:

  1. içerik oluşturma süreci uzun sürelidir ve
  2. hedef dosya tutarlı bir durumda bırakılmalıdır.

Bu ön kontrolü # 1 nedeniyle yapmak istiyorsunuz ve # 2 nedeniyle mevcut bir dosyayla uğraşmak istemiyorsunuz. Öyleyse neden kabuktan dosyayı ekleme için açmasını istemiyorsunuz, ama aslında hiçbir şey eklemiyorsunuz?

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

$ for p in file_r dir_r/foo file_w dir_w/foo; do : >> $p; done
-bash: file_r: Permission denied
-bash: dir_r/foo: Permission denied

$ tree -ps
.
├── [dr-x------        4096]  dir_r
├── [drwx------        4096]  dir_w
   └── [-rw-rw-r--           0]  foo
├── [-r--------           0]  file_r
└── [-rw-------           0]  file_w

Başlık altında, yazılabilirlik sorusu tam olarak istendiği gibi çözülür:

open("dir_w/foo", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

ancak dosyanın içeriğini veya meta verilerini değiştirmeden. Şimdi, evet, bu yaklaşım:

  • dosyanın yalnızca eklenip eklenmediğini size söylemez; bu, içerik oluşturma işleminizin sonunda güncelleme konusunda bir sorun olabilir. Bunu bir dereceye kadar tespit edebilir lsattrve buna göre tepki verebilirsiniz.
  • daha önce var olmayan bir dosya oluşturur, eğer durum buysa: bunu bir seçici ile hafifletin rm.

(Diğer cevabımda) en kullanıcı dostu yaklaşımın kullanıcının taşımak zorunda olduğu geçici bir dosya oluşturmak olduğunu iddia ederken , bu girdilerini tam olarak incelemek için en az kullanıcı dostu yaklaşım olduğunu düşünüyorum .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.