Shell betiğinde geçici bir dosya nasıl oluşturulur?


155

Bir betiği çalıştırırken, /tmpdizinde geçici bir dosya oluşturmak istiyorum .

Bu betiğin çalıştırılmasından sonra, o komut dosyası tarafından temizlenir.

Kabuk komut dosyasında nasıl yapılır?

Yanıtlar:


198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

Bir dosya tanımlayıcısını dosyaya açıp silerek, komut dosyaları çıktığında (öldürmeler ve çökmeler dahil) bir dosyanın silindiğinden emin olabilirsiniz. /proc/$PID/fd/$FDDosya tanıtıcısı açık olduğu sürece, dosya kullanılabilir durumda kalır (komut dosyası için; gerçekten diğer işlemler için değil , bir çalışma ortamıdır). Kapatıldığında (işlem başladığında çekirdeğin otomatik olarak yaptığı) dosya sistemi dosyayı siler.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3

4
İyi cevap, kaza durumunda dosya tanımlayıcılı zarif çözüm +1
kaos

2
/proc- sahip olmayan sistemler hariç.
Dennis Williamson,

4
ne yapar exec 3> "$tmpfile"? Bu yalnızca tmpfile tek başına bir komut dosyasıysa yararlı olmaz mı?
Alexej Magura

5
Oluşturulan FD'den nasıl okursunuz?
17'de

3
"Kedi <3 veya benzer bir şey kullanabilirsiniz." Aslında bu 3 @ dragon788 adlı bir dosyadan okur. Ayrıca, cat <&3verecek Bad file descriptor. Düzeltirseniz veya çıkarırsanız sevinirim; yanlış bilgilendirme pek yardımcı olmuyor.
Daniel Farrell

65

mktempGeçici bir dosya veya dizin oluşturmak için kullanın :

temp_file=$(mktemp)

Veya bir şekerleme için:

temp_dir=$(mktemp -d)

Betiğin sonunda geçici dosyayı / dir silmeniz gerekir:

rm ${temp_file}
rm -R ${temp_dir}

mktemp, /tmpdizinde veya --tmpdirargümanla verilen dizgede dosya oluşturur .


20
trap "rm -f $temp_file" 0 2 3 15Dosyayı oluşturduktan hemen sonra kullanabilirsiniz , böylece komut dosyası çıktıktan veya ctrl-Cdosya durdurulduktan sonra hala kaldırılır.
wurtel

1
@wurtel EXITTek kanca ne için olur trap?
Hauke,

4
@HaukeLaging Sonra komut dosyası Ctrl + C ile durdurulursa tuzak patlamaz. Unutulmaması gereken bir şey, eğer TRAP'ın size yardımcı olmadığıdır kill -9 $somepid. Bu özel öldürme sinyali, başka hiçbir şey olmadan, insta-ölümdür.
dragon788,

5
@ dragon788 Bunu denediniz mi? Malısın. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke,

Bindirme EXITyeterli.
Kusalananda

15

Eğer mktemp olan bir sistemde iseniz , diğer cevaplar olarak kullanmalısınız.

POSIX araç testi ile:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"

EXITTek kanca olsaydı ne olur trap?
Hauke,

@HaukeLaging: tmpfilekomut dosyası çıkmadan önce hala kaldırılıyor, ancak komut dosyası başka sinyaller aldığında da kaldırılmıyor.
cuonglm

Burada olan bu değil (GNU bash, Sürüm 4.2.53).
Hauke,

@HaukeLaging: Ne demek istiyorsun That's not what happens?
cuonglm,

3
mktempHP / UX'te farklı bir sözdizimine sahip kaynak. Todd C. Miller, 90'ların ortalarında OpenBSD için farklı bir tane yarattı (FreeBSD ve NetBSD tarafından kopyalandı) ve daha sonra bağımsız bir yardımcı program olarak da kullanılabilir hale getirdi (www.mktemp.org). Genellikle Linux'ta (çoğunlukla uyumlu) bir mktempyardımcı program 2007'de GNU coreutil'lerine ekleninceye kadar kullanılandır. Sadece birisinin gerçekten söyleyemediği mktempbir GNU yardımcı programı olduğunu söylemek mümkün değildir .
Stéphane Chazelas

14

Bazı mermilerin yerleşik özelliği vardır.

zsh

zsh'nin =(...)işlem değiştirme şekli geçici bir dosya kullanıyor. Örneğin =(echo test), içeren geçici bir dosyanın yoluna genişler test\n.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

Komut tamamlandıktan sonra bu dosya otomatik olarak kaldırılır.

Linux'ta bash / zsh.

İşte dosyaları veya burada-dizeleri içinde bashve zshsilinen geçici dosyalar olarak uygulanmaktadır.

Yani yaparsan:

exec 3<<< test

Dosya tanımlayıcısı 3, içeren silinmiş bir geçici dosyaya bağlanır test\n.

İçeriğini şunlarla alabilirsiniz:

cat <&3

Linux'ta ise, bu dosyayı da okuyabilir veya yazabilirsiniz. /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(diğer bazı mermiler borular kullanmaktadır veya /dev/nullburada doc boşsa kullanabilirler).

POSIX

mktempPOSIX yardımcı programı yok . Ancak POSIX bir mkstemp(template)C API belirtir ve m4standart yardımcı program bu API'yi mkstemp()m4 işleviyle aynı adla gösterir.

mkstemp()Size işlev çağrıldığında var olmama garantisi verilen rastgele kısmı olan bir dosya adı verir. 0600 izinli dosyayı yarışsız bir şekilde oluşturur.

Yani yapabilirsin:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Bununla birlikte, çıkış sırasında temizliği yapmanız gerektiğine dikkat edin, ancak dosyayı yalnızca birkaç kez yazıp okumanız gerekirse, dosyayı burada açabilir ve burada-doc / here- için oluşturduktan hemen sonra silebilirsiniz. Yukarıdaki dize yaklaşımı:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

Dosyayı bir kez okumak için açabilir ve iki okuma arasında geri sarabilirsiniz, ancak bu geri sarma işlemini yapabilen bir POSIX yardımcı programı yoktur lseek(), bu nedenle bir POSIX komut dosyasında ( zsh( sysseekyerleşik) ve ksh93( <#((...))işleç) yapabilirsiniz. olsa yap).


1
Bash ayrıca kullanarak işlem ikame var<()
WinnieNicklaus

3
@WinnieNicklaus, evet, ama bu geçici dosyalar kullanmıyor, bu yüzden burada önemsiz. Süreç ikamesi bash ve zsh tarafından kopyalanmış, ksh tarafından tanıtılan ve zsh bir 3 forma ile uzatıldı: =(...).
Stéphane Chazelas

7

İşte Hauke ​​Laging'in çizgisinde biraz daha gelişmiş bir cevap:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R

2
İçeriğin yalnızca bir kez kullanılabilir olduğu not edilmelidir. Yani eğer ikinci kez kedi <& $ FD_R yaparsam, hiç çıktı alınmaz. Unix.stackexchange.com/questions/166482/… adresine bakın . Program çöktüğünde, ancak dosyayı birden çok kez erişilebilir duruma getirdiğinde dosyayı otomatik olarak silmenin bir yolu var mı?
smihael

0

Genellikle geçici dosyalar içeren iş akışım, test ettiğim bash betiğinden kaynaklanıyor. Bunu yapmak istiyorum, teeböylece çalıştığını görebiliyorum ve işlemimin bir sonraki yinelemesi için çıktıyı korudum. Adlı bir dosya oluşturdumtmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

böylece onu kullanabilirim

$ some_command --with --lots --of --stuff | tee $(tmp)

Datetime'nın rastgele değerlerden önce biçimlendirilmesinden hoşlanmamın nedeni, kolayca yaptığım tmp dosyasını bulmama izin veriyor ve bir dahaki sefere neye isim vereceğimi düşünmek zorunda değilim (ve sadece dang betiğimi almaya odaklanıyorum) çalışmak).

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.