How to Review a Shell Script Before You Run It With Examples
Review a shell script before you run it: download without executing, read it, spot dangerous patterns, run ShellCheck, verify the source and sandbox it.

"Run this one-liner to install" is common practice and it is remote code execution with your privileges. Reviewing the script first takes a few minutes and catches most carelessness and some malice. It is the practical counterpart to the arguments in the curl-pipe-bash risk. The review has a routine, and the examples assume Bash 5.2, curl 8 and ShellCheck 0.10.
Fetch it, do not pipe it
curl -fsSLo /tmp/install.sh https://example.test/install.sh
wc -l /tmp/install.sh
file /tmp/install.sh
less /tmp/install.sh214 /tmp/install.sh
/tmp/install.sh: POSIX shell script, ASCII text executable
Piping into a shell executes whatever the server sends at that moment, including partial output if the connection drops mid-script, and a server can serve different content to curl than to a browser. Saving the file lets you read exactly what will run and keep a copy. Check the size and type: a short installer that is a megabyte of binary data is not what it claims. Set the permissions yourself with chmod +x only after reading.
What to look for
grep -nE 'eval|base64|\bsudo\b|rm -rf|curl .*\| *(ba)?sh|wget .*\| *(ba)?sh|chmod [0-7]*7[0-7]*|/etc/|crontab|authorized_keys|nc -e|/dev/tcp' /tmp/install.sh
grep -nE 'https?://' /tmp/install.sh | sort -u41:sudo install -m 0755 "$tmp/tool" /usr/local/bin/tool
88: curl -fsSL "$base/checksums.txt" -o "$tmp/checksums.txt"
Not every hit is bad; a legitimate installer uses sudo to install to a system path. What matters is whether you understand each. Red flags: eval or base64 -d | sh on data from the network, downloads from hosts other than the vendor's, edits to cron, SSH keys or shell startup files that are not documented, and rm -rf with variables that could be empty (rm -rf "$DIR/" with DIR unset deletes the root). Obfuscated strings, long hex or base64 blobs, and minified code are reasons to stop. The patterns behind these are in avoiding eval and preventing command injection.
Lint and syntax check
bash -n /tmp/install.sh && echo "syntax OK"
shellcheck -S warning /tmp/install.sh | head -20bash -n reads without running, catching syntax errors. ShellCheck finds unquoted variables, risky patterns and portability bugs; it helps you judge quality, because a sloppy installer is more likely to break your system by accident. It does not detect malicious intent, since malware can pass lint. Warnings that involve rm and unquoted variables deserve a closer look.
Verify the source
sha256sum /tmp/install.sh
curl -fsSL https://example.test/install.sh.sha256
gpg --verify install.sh.asc install.shIf the project publishes a checksum or signature, compare it, ideally fetched over a different channel (the project's repository or release page rather than the same web server). A checksum served next to the file only detects corruption; a signature from a key you already trust detects tampering. See verifying downloads. Read the script's own network calls: it may fetch a second-stage file you have not reviewed, and the same questions apply to that.
Try it in a sandbox first
docker run --rm -it --network none -v /tmp/install.sh:/install.sh:ro debian:13-slim bash -x /install.sh
docker run --rm -it -v /tmp/install.sh:/install.sh:ro debian:13-slim sh -c 'apt-get update -qq && apt-get install -y -qq curl ca-certificates && bash /install.sh'A throwaway container shows what the script does without risk to your machine: add -x for a trace and inspect which files it creates. Cut the network at first to see what it tries to reach. For stronger isolation use a virtual machine. Containers share the host kernel, so they are protection against mistakes, not against a determined attacker. Never put secrets or your home directory into the sandbox.
Why this habit matters
An installer runs with your privileges and often with root's. Whatever it does is done as you: reading your keys, editing your shell profile, adding a service. A malicious or careless script needs no exploit, because you invited it in. The convenience of a one-line install hides that fact, and the incidents that make news, a compromised download server or a typo-squatted domain, are the tail of a risk that includes ordinary mistakes. A script that deletes the wrong directory when a variable is empty harms someone just as thoroughly as one written to do it.
Reading is not infallible, and it is not the only layer. It works best combined with a trust decision about the source and a limit on the damage. Ask who publishes the script, whether the URL is under the vendor's own domain, whether the project has a history and an issue tracker, and whether the same software is available from your distribution's repository, where someone else has already reviewed packaging. If it is, use that route.
Reading a script efficiently
Read in layers. First the header: the shebang, the strict mode settings, the variables that define URLs and versions. Next the functions' names, which sketch the structure: detect the platform, download, verify, install, clean up. Then the downloads, because they decide what code enters your machine: where from, over what protocol, and whether a checksum is verified before use. Finally the privileged steps, every sudo, every write outside the working directory and every change to shell configuration, each of which should have an obvious reason.
Look for what is missing as well as what is present. A good installer checks that downloads succeeded, cleans up its temporary directory, tells you what it changed and offers an uninstall path. An installer that modifies ~/.bashrc silently, leaves files in /tmp and cannot be undone is a sign of low care, which is relevant even when the intent is honest.
Understanding the second stage
Many installers are small bootstrappers that download a larger payload and run it. Reading the bootstrap is the easy part. Find the URL of the payload and read or at least inspect that too: its size, its type and the checksum the bootstrap verifies, if any. A bootstrap that downloads a binary over HTTPS from the vendor's domain and verifies a signed checksum is in good shape. One that downloads a script and pipes it to a shell moves the whole question one level down, and you should repeat the review there.
Limiting damage when you proceed
When you decide to run it, narrow the blast radius. Use a fresh user or a container for the first run, use a throwaway virtual machine for software that wants deep system access, and snapshot the system before an install that touches system paths. Run without sudo first and see what fails; many tools install fine into a home directory. Afterwards, check what changed with a file listing diff, or with find / -newer /tmp/marker after creating a marker file, and keep the list in case you want to remove the tool later.
Where this does not apply
Reading 200 lines is practical; reading a 5,000-line installer with embedded binaries is not, and you should trust the vendor and the signature chain instead or find a packaged version through your distribution. A review is a snapshot: a script that is clean now can change tomorrow, so pin to a version and checksum in automation. For code you run daily, prefer packages from your distribution's repository. See the ShellCheck wiki and the Bash invocation options.


