シェルスクリプトで一時ファイルを作成する方法


155

スクリプトの実行中に、/tmpディレクトリに一時ファイルを作成したい。

そのスクリプトの実行後、そのスクリプトによってクリーンアップされます。

シェルスクリプトでそれを行う方法は?

回答:


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

ファイルのファイル記述子を開いて削除することにより、スクリプトの終了時に(キルやクラッシュを含む)ファイルが削除されることを確認できます。/proc/$PID/fd/$FDファイル記述子が開いている限り、ファイルは利用可能です(スクリプト用。実際には他のプロセス用ではありませんが、回避策です)。閉じられると(プロセスの終了時にカーネルが自動的に行います)、ファイルシステムはファイルを削除します。

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

4
良い答え、クラッシュ+1の場合のファイル記述子を使用したエレガントなソリューション
カオス

2
/proc-それを持たないシステムを除く。
デニスウィリアムソン

4
何をしexec 3> "$tmpfile"ますか?tmpfileがスタンドアロンスクリプトである場合にのみ有用ではありませんか?
アレクセイマグラ

5
作成したFDからどのように読み取りますか?
eckes

3
「cat <3または同様のものを使用できます。」実際には、3 @ dragon788という名前のファイルから読み取ります。また、cat <&3与えBad file descriptorます。修正または削除していただければ幸いです。誤報はあまり役に立ちません。
ダニエルファレル

65

mktemp一時ファイルまたはディレクトリを作成するために使用します。

temp_file=$(mktemp)

またはディレクトリの場合:

temp_dir=$(mktemp -d)

スクリプトの最後で、一時ファイル/ディレクトリを削除する必要があります。

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

mktempは、/tmpディレクトリまたは--tmpdir引数で指定されたディレクトリにファイルを作成します。


20
trap "rm -f $temp_file" 0 2 3 15ファイルを作成した直後に使用して、スクリプトが終了または停止しctrl-Cてもファイルが削除されるようにすることができます。
ウルテル

1
@wurtelがEXIT唯一のフックである場合はどうなりtrapますか?
ハウケレイジング

4
@HaukeLaging次に、Ctrl + Cでスクリプトが停止された場合、トラップは起動しません。注意すべきことは、TRAPはあなたの助けにはならないということですkill -9 $somepid。その特定のキルシグナルは、他に何も起こらずにinsta-deathです。
dragon788

5
@ dragon788あなたはそれを試しましたか?あなたがすべき。bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke Laging

トラップEXITで十分です。
クサラナナンダ

15

mktempがあるシステムを使用している場合は、他の回答として使用する必要があります。

POSIXツールチェストの場合:

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

EXIT唯一のフックである場合はどうなりtrapますか?
ハウケレイジング

@HaukeLaging:tmpfileスクリプトが終了する前に削除されますが、スクリプトが他のシグナルを受信したときは削除されません。
クオンルム

ここでは何も起こりません(GNU bash、バージョン4.2.53)。
ハウケレイジング

@HaukeLaging:どういう意味That's not what happensですか?
cuonglm

3
mktemp異なる構文を使用してHP / UXで作成されました。Todd C. Millerは、90年代半ばにOpenBSD用に別のものを作成し(FreeBSDとNetBSDによってコピー)、後にスタンドアロンユーティリティ(www.mktemp.org)としても利用できるようにしました。これはmktemp、2007年に(ほとんど互換性のある)ユーティリティがGNU coreutilsに追加されるまでLinuxで一般的に使用されmktempていたものです。実際に言うことができないのはGNUユーティリティです。
ステファンシャゼル

14

一部のシェルには機能が組み込まれています。

zsh

zsh=(...)形式のプロセス置換は、一時ファイルを使用します。たとえば=(echo test)、を含む一時ファイルのパスに展開します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

コマンドが終了すると、そのファイルは自動的に削除されます。

Linuxのbash / zsh。

here-filesまたはhere-stringsはbashzsh削除された一時ファイルとして実装されます。

あなたがそうするなら:

exec 3<<< test

ファイル記述子3は、を含む削除済み一時ファイルに接続されていますtest\n

次の方法でコンテンツを取得できます。

cat <&3

Linuxの場合は、次の方法でもそのファイルを読み書きできます。 /dev/fd/3

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

(他のシェルはパイプを使用する/dev/nullか、here docが空の場合に使用する場合があります)。

POSIX

mktempPOSIXユーティリティはありません。ただし、POSIXはmkstemp(template)C APIを指定し、m4標準ユーティリティはそのAPIをmkstemp()同じ名前のm4関数で公開します。

mkstemp()関数が呼び出されたときに存在しないことが保証されたランダムな部分を含むファイル名を提供します。レース0の方法でアクセス許可0600のファイルを作成します。

だから、あなたはすることができます:

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

ただし、終了時にクリーンアップを処理する必要があることに注意してください。ただし、ファイルの書き込みと読み取りを一定回数だけ行う必要がある場合は、here-doc / here-のように作成した直後にファイルを開いて削除できます。上記の文字列アプローチ:

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

ファイルを1回読み取り用に開き、2回の読み取りの間に巻き戻すことができますが、その巻き戻し(lseek())を実行できるPOSIXユーティリティはないため、POSIXスクリプト(zshsysseekbuiltin)and ksh93<#((...))operator)canしかしそれを行う)。


1
Bashを使用したプロセス置換もあります<()
-WinnieNicklaus

3
@WinnieNicklaus、はい、しかしそれは一時ファイルを使用しないため、ここでは無関係です。プロセス置換はkshによって導入され、bashおよびzshによってコピーされ、zshは3番目の形式でそれを拡張しました=(...)
ステファンシャゼル

7

以下に、Hauke Lagingの行の回答を少し改善しました。

#!/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
コンテンツは1回しか利用できないことに注意してください。つまり、2回目にcat <&$ FD_Rを実行すると、出力は生成されません。unix.stackexchange.com/questions/166482/…を参照してください。プログラムがクラッシュした場合にファイルを自動的に削除する方法はありますか?
smihael

0

通常、一時ファイルを使用するワークフローは、テスト中のbashスクリプトが原因です。teeそれが機能していることを確認し、プロセスの次の反復のために出力を保存できるように、それを増やしたいと思います。というファイルを作成しましたtmp

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

のようにそれを使用することができます

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

ランダム値の前にフォーマットされた日時が好きな理由は、簡単に作成したtmpファイルを見つけることができるため、次に名前を付けることを考える必要がありません(そして私のダンスクリプトを取得することに集中する必要はありません動作するように)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.