Beklenmeyen bash çıkışında oluşturulan geçici dosyaları kaldırma


89

Bir bash betiğinden geçici dosyalar oluşturuyorum. İşlemin sonunda onları siliyorum, ancak komut dosyası oldukça uzun bir süredir çalıştığı için, çalıştırma sırasında onu öldürürsem veya basitçe CTRL-C, geçici dosyalar silinmez.
Yürütme sona ermeden önce bu olayları yakalayıp dosyaları temizlememin bir yolu var mı?

Ayrıca, bu geçici dosyaların adlandırılması ve konumu için bir tür en iyi uygulama var mı?
Şu anda aşağıdakiler arasında emin değilim:

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

ve

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

Ya da belki daha iyi çözümler var mı?


Yanıtlar:


97

Çıkışta çalıştırmak için bir " tuzak " veya temizlemek için bir kontrol-c üzerinde çalıştırabilirsiniz.

trap "{ rm -f $LOCKFILE; }" EXIT

Alternatif olarak, en sevdiğim unix izimlerimden biri bir dosyayı açmak ve daha sonra onu hala açıkken silmektir. Dosya dosya sisteminde kalır ve onu okuyabilir ve yazabilirsiniz, ancak programınız kapanır kapanmaz dosya kaybolur. Yine de bunu Bash'de nasıl yapacağınızdan emin değilim.

BTW: Kendi çözümünüzü kullanmak yerine mktemp lehine vereceğim bir argüman: eğer kullanıcı programınızın çok büyük geçici dosyalar oluşturacağını tahmin ederse, TMPDIR/ var / tmp gibi daha büyük bir yere ayarlamak isteyebilir . mktemp, sizin elle sarılmış çözümünüzün (ikinci seçenek) olmadığını anlar. TMPDIR=/var/tmp gvim -d foo barÖrneğin sık sık kullanıyorum .


8
Bash ile exec 5<>$TMPFILEbağları dosya tanımlayıcı 5 okuma-yazma tmpfile, $ için, ve kullanabileceğiniz <&5, >&5ve /proc/$$/fd/5(Linux) bundan sonra. Tek sorun, Bash'in seekişlevsiz olması ...
efemient

Sağladığınız bağlantı ihtiyacım olanı en iyi açıkladığı için yanıtınızı kabul ettim. Teşekkürler
skinp

4
Birkaç not trap: tuzağa düşmenin bir yolu yok SIGKILL(tasarım gereği, yürütmeyi hemen sonlandırdığı için). Öyleyse, eğer bu gerçekleşebilirse, bir geri dönüş planınız olsun (örneğin tmpreaper). İkinci olarak, tuzaklar birikimli değildir - gerçekleştirmeniz gereken birden fazla eyleminiz varsa, hepsi trapkomutta olmalıdır. Birden temizleme eylemleri ile baş etmenin bir yolu bir işlev tanımlamaktır (ve gerekirse, programınız ilerledikçe bunu yeniden tanımlayabilirsiniz) ve referans olduğunu: trap cleanup_function EXIT.
Toby Speight

1
Kullanmam gerekiyordu trap "rm -f $LOCKFILE" EXITyoksa beklenmeyen bir dosya sonu hatası alıyordum.
Jaakko

3
Shellcheck, tek tırnakların kullanılması konusunda uyarıda bulundu, ifadenin tuzak çağrıldığında daha sonra değil, çift tırnak ile genişletileceği 'şimdi'.
LaFayette

110

Genelde tüm geçici dosyalarımı yerleştireceğim bir dizin oluşturuyorum ve hemen ardından, komut dosyası çıktığında bu dizini temizlemek için bir EXIT işleyicisi oluşturuyorum.

MYTMPDIR=$(mktemp -d)
trap "rm -rf $MYTMPDIR" EXIT

Tüm geçici dosyalarınızı altına koyarsanız $MYTMPDIR, çoğu durumda komut dosyanız çıktığında hepsi silinecektir. SIGKILL (kill -9) ile bir işlemi öldürmek işlemi hemen öldürür, bu nedenle EXIT işleyiciniz bu durumda çalışmaz.


27
+1 Kesinlikle EXIT'te bir tuzak kullanın, saçma TERM / INT / HUP / aklınıza gelen başka ne varsa değil. Gerçi, unutmayın alıntı Parametreniz genişlemeleri ve ben ediyorum da size tavsiye tek alıntı tuzak: tuzak 'rm-rf '$ TMPDIR'' EXIT
lhunath

7
Tek tırnak, çünkü daha sonra betiğinizde koşullar nedeniyle TMPDIR'ı temizlemeye ve değiştirmeye karar verirseniz, tuzağınız yine de çalışacaktır.
lhunath

1
@AaronDigulla $ () vs backticks neden önemlidir?
Ogre Mezmur


3
@AlexanderTorstling kodu, rastgele kod yürütülmesine neden olan enjeksiyonu önlemek için her zaman tek tırnaklı olmalıdır. Verileri STRING adlı bir bash koduna genişletirseniz, o zaman bu veriler artık kodun yaptığı her şeyi yapabilir, bu da beyaz boşlukta masum hataların yanı sıra tuhaf nedenlerle homedir'nizi temizlemek veya güvenlik açıkları getirmek gibi yıkıcı hatalarla sonuçlanır. Tuzağın daha sonra olduğu gibi değerlendirilecek bir dizi bash kodu aldığını unutmayın. Bu yüzden daha sonra, tuzak ateşlendiğinde, tek tırnak işaretleri kaybolacak ve sadece sözdizimsel çift tırnak işaretleri olacaktır.
lhunath

25

Komut dosyasından çıkmayı veya CTRL-C gibi sinyalleri işlemek için trap komutunu kullanmak istiyorsunuz . Ayrıntılar için Greg'in Wiki'sine bakın.

basename $0Geçici dosyalarınız için kullanmak iyi bir fikir olmasının yanı sıra, yeterli geçici dosyalar için yer sağlayan bir şablon sağlamaktır:

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT

1
TERM / INT üzerine tuzak kurmayın. EXIT'te tuzak. Alınan sinyallere göre çıkış koşulunu tahmin etmeye çalışmak aptalca ve kesinlikle bir yakalama değil.
lhunath

3
Küçük nokta: Tek ters işaret yerine $ () kullanın. Ve 0 $ civarında çift tırnak işareti koyun çünkü boşluk içerebilir.
Aaron Digulla

Bu yorumda ters işaretler iyi çalışıyor, ancak bu doğru bir nokta, kullanma alışkanlığı içinde olmak iyi $(). Çift tırnak da eklendi.
Brian Campbell

1
Tüm alt yordamınızı yalnızca TMP1 = $ (tempfile -s "XXXXXX") ile değiştirebilirsiniz
Ruslan Kabalin

4
@RuslanKabalin Tüm sistemlerin bir tempfilekomutu yoktur, bildiğim tüm makul modern sistemler ise bir mktempkomuta sahiptir.
Brian Campbell

9

Sadece seçilen cevabın, bashismyani çözümün şu anlama geldiğini unutmayın:

trap "{ rm -f $LOCKFILE }" EXIT

sadece bash ile çalışır (kabuk dashveya klasik ise Ctrl + c yakalamayacaktır sh), ancak uyumluluk istiyorsanız, tuzaklamak istediğiniz tüm sinyalleri numaralandırmanız gerekir.

Ayrıca, komut dosyası "0" sinyali için tuzaktan çıktığında (diğer adıyla EXIT) her zaman trapkomutun iki kez yürütülmesiyle sonuçlandığını unutmayın .

ÇIKIŞ sinyali varsa tüm sinyalleri tek bir hatta yığmama nedeni budur.

Daha iyi anlamak için, farklı sistemlerde değişiklik yapmadan çalışacak aşağıdaki komut dosyasına bakın:

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

Bu çözüm, kodunuzun bir kısmını son çıkıştan ( preExitişlev) hemen önce gerçek sinyalin oluşması üzerine çalıştırabileceğiniz ve gerekirse gerçek EXIT sinyalinde (çıkışın son aşaması) bazı kodları çalıştırabileceğiniz için size daha fazla kontrol sağlayacaktır.


4

$$ ile tahmin edilebilir bir dosya adı kullanmanın alternatifi, açık bir güvenlik açığıdır ve onu kullanmayı asla, asla düşünmemelisiniz. Tek kullanıcılı bilgisayarınızda basit bir kişisel komut dosyası olsa bile. Edinmemeniz gereken çok kötü bir alışkanlıktır. BugTraq "güvensiz geçici dosya" olaylarıyla doludur. Geçici dosyaların güvenlik yönü hakkında daha fazla bilgi için buraya , buraya ve buraya bakın .

Başlangıçta güvensiz TMP1 ve TMP2 görevlerinden alıntı yapmayı düşünüyordum, ancak ikinci olarak bunun muhtemelen iyi bir fikir olmayacağını düşündüm .


Yapabilseydim verirdim: Güvenlik tavsiyesi için +1 ve kötü bir fikir ve referanstan alıntı yapmadığım için başka bir +1
TMG

1

tempfile/ Tmp dosyasında güvenli bir şekilde dosya oluşturanı kullanmayı tercih ederim ve adlandırma konusunda endişelenmenize gerek yok:

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit

tempfile ne yazık ki daha güvenli olmasına rağmen çok taşınabilir değildir, bu yüzden genellikle ondan kaçınmak veya en azından onu taklit etmek daha iyidir.
lericson

1

Bu kadar çok insanın bir dosya adının boşluk içermeyeceğini varsaydığına inanamıyorum. $ TMPDIR herhangi bir zamanda "geçici dizine" atanırsa dünya çökecektir.

zTemp=$(mktemp --tmpdir "$(basename "$0")-XXX.ps")
trap "rm -f ${zTemp@Q}" EXIT

Dosya isimlerindeki boşluklar ve tek tırnak işaretleri ve satır başları gibi diğer özel karakterler, iyi bir programlama alışkanlığının bir gereği olarak kodda düşünülmelidir.


${parameter@operator}Genişletmelerin Bash 4.4'te (Eylül 2016'da yayınlandı) eklendiğine dikkat edilmelidir .
Robin A. Meade

Bash <v4.4 için eşdeğer:trap "rm -f $(printf %q "$zTemp")" EXIT
Robin A. Meade

Bu en iyi cevaptır çünkü zTempsenaryoda daha sonra değiştirilmenin değeri hakkında endişelenmenize gerek yoktur . Ayrıca, bir işleve zTempbildirilebilir local; global bir betik değişkeni olması gerekmez.
Robin A. Meade

-4

Mktemp ile oluşturulan bu tmp dosyalarını silmenize gerek yok. Yine de daha sonra silinecekler.

Daha fazla benzersiz dosya oluşturduğu için mktemp kullanın ve ardından '$$' önekini kullanın. Ve geçici dosyalar oluşturmak için daha çok platformlar arası bir yol gibi görünüyor ve sonra bunları açıkça / tmp içine koyuyor.


4
Kim ya da ne tarafından silindi?
innaM

İşlem tarafından
silindi

4
Sihir mi? Cronjob mı? Veya yeniden başlatılmış bir Solaris makinesi?
innaM

Muhtemelen onlardan biri. Geçici dosya bazı kesintilerle kaldırılmadıysa (çok sık olmayacak) bir gün tmp dosyaları kaldırılacaktır - bu yüzden geçici olarak adlandırılırlar.
Mykola Golubyev

22
/ Tmp içine konulan bir şeyin orada sonsuza kadar kalacağını varsayamazsınız, yapmamalısınız; aynı zamanda, sihirli bir şekilde yok olacağını varsaymamalısınız.
innaM
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.