mktemp — Create a unique temp file or directory without TOCTOU races across all 5 shells
Equivalents in every shell
mktempCreates a file under `$TMPDIR` (or `/tmp`) with a random name like `tmp.AbCdEf1234`. The file is created with `0600` permissions (only owner can read/write) — safe for secrets. Add `-d` for a directory instead.
mktempSame external `mktemp` binary. Zsh `=(cmd)` process substitution writes to a temp file automatically and cleans up on shell exit — useful in pipelines without needing explicit `mktemp` + `rm` bookkeeping.
mktempSame external binary. Fish ships no native temp-file helper — use `mktemp` and clean up via `function fish_exit; rm -f $tmpfile; end` or `trap`-like patterns.
New-TemporaryFileReturns a `FileInfo` for a `tmp<HEX>.tmp` file under `$env:TEMP`. Permissions are NTFS default (Users group readable on shared machines!). For a directory: `$d = New-Item -ItemType Directory -Path (Join-Path $env:TEMP ([Guid]::NewGuid()))`. Note `[System.IO.Path]::GetTempFileName()` exists but exposes a predictable-suffix security issue (TOCTOU window).
set TMPFILE=%TEMP%\tmp_%RANDOM%_%TIME:~6,2%%TIME:~9,2%.tmp & type nul > "%TMPFILE%"No native `mktemp`. `%RANDOM%` is a 5-digit pseudorandom int (~32K range) — race-prone if two scripts run in the same second. For a hardened version: call `powershell -Command "(New-TemporaryFile).FullName"` and capture stdout.
Worked examples
Create a temp directory and clean up on script exit
tmpdir=$(mktemp -d) && trap "rm -rf \"$tmpdir\"" EXITset tmpdir (mktemp -d); function on_exit --on-event fish_exit; rm -rf $tmpdir; end$tmpdir = New-Item -ItemType Directory (Join-Path $env:TEMP ([Guid]::NewGuid())); try { ... } finally { Remove-Item $tmpdir -Recurse -Force }set TMPDIR=%TEMP%\%RANDOM%-%RANDOM% & mkdir "%TMPDIR%" & ... & rmdir /s /q "%TMPDIR%"Custom template (e.g. project-specific prefix)
mktemp /tmp/myapp.XXXXXXXXmktemp /tmp/myapp.XXXXXXXX$f = Join-Path $env:TEMP "myapp.$([Guid]::NewGuid().ToString("N").Substring(0,8))"; New-Item -ItemType File -Path $fset TMPFILE=%TEMP%\myapp_%RANDOM%%RANDOM%.tmp & type nul > "%TMPFILE%"Temp file in a specific directory (not `$TMPDIR`)
mktemp -p /var/cache/myappNew-Item -ItemType File -Path (Join-Path "/var/cache/myapp" ([Guid]::NewGuid().ToString("N").Substring(0,8) + ".tmp"))Gotchas
- BusyBox `mktemp` (Alpine images, embedded Linux) drops several GNU-coreutils flags: no `-p` (use `--tmpdir`), no `--suffix`, weaker template enforcement. Dockerfiles built on `alpine` will silently behave differently from `debian-slim` — pin the image OR test on both.
- macOS BSD `mktemp` REQUIRES a template (`mktemp /tmp/foo.XXXXXX`) on older versions — bare `mktemp` errors. GNU `mktemp` accepts bare invocation. Cross-OS scripts should always pass a template explicitly: `mktemp /tmp/$(basename "$0").XXXXXX`.
- pwsh `[System.IO.Path]::GetTempFileName()` returns a file but with a PREDICTABLE suffix pattern (`tmp????.tmp` with 4 hex chars — 64K namespace). On multi-user Windows machines or shared CI runners, that's a TOCTOU race window. Prefer `New-TemporaryFile` (uses `Path.GetRandomFileName` → 8 random base32 chars, 40 bits of entropy).
- cmd `%RANDOM%` produces values 0–32767 — collision likely under any concurrent workload. `%RANDOM%%RANDOM%` (two concatenated) gets to ~1 billion possible names but is still pseudo-random based on system time. For real security, shell out to pwsh `[Guid]::NewGuid()` or to `powershell New-TemporaryFile`.
- `mktemp` files are created mode `0600` on POSIX — readable only by owner. NTFS-created temp files (pwsh `New-TemporaryFile`) inherit the parent directory ACL: on a shared workstation, that often means the BUILTIN\Users group can read them. For pwsh temp files holding secrets, explicitly set ACL: `icacls $f /inheritance:r /grant:r "$env:USERNAME:F"`.