Home / Alt manpages / ischroot(1)

  • ischroot(1)
  • User command
  • linux

Make Shell Scripts Chroot-Aware with ischroot

A build script that behaves differently inside a chroot needs a reliable way to ask, and ischroot answers through its exit status alone. You will finish with a small, reliable check plus a clear response for the case where detection is not possible at all. The examples use ischroot from Debianutils 5.17, installed here as package version 5.17build1.

Allow about ten minutes. You need a shell and the Debianutils package. The checks are ordinary, read-only commands and do not need sudo. This guide detects the current process environment; it does not create, enter, leave or repair a chroot.

1. Check the installed command

Confirm the command on your PATH is the one you expect, then read its version:

$ command -v ischroot
/usr/bin/ischroot
$ dpkg-query -W -f='${Package} ${Version}\n' debianutils
debianutils 5.17build1
$ ischroot --version
Debian ischroot, version 5.17

Package revisions vary by distribution, but the contract stays simple: exit status 0 means the process is in a chroot, 1 means it is not, and 2 means detection was not possible. The command normally prints nothing for a detection result, so its exit status is the only useful output.

Checkpoint

Do not test this with ischroot && echo yes alone. That only distinguishes status 0 from every non-zero status, which hides the difference between "not in a chroot" and "could not detect".

2. Read the result without losing status 2

Capture the status immediately after running the command. The assignment must not come first, because $? changes after every command:

$ ischroot
$ status=$?
$ case "$status" in
>   0) echo 'inside a chroot' ;;
>   1) echo 'not inside a chroot' ;;
>   2) echo 'chroot status could not be detected' ;;
>   *) echo "unexpected ischroot status: $status" >&2; exit 1 ;;
> esac
not inside a chroot

The prompt characters in this block show a multi-line shell command; do not type the leading > characters. On a different host, the final line may instead say inside a chroot, or the command may return status 2.

For a script, turn the check into a function so callers can decide what an uncertain result means:

is_chroot() {
    ischroot
    case "$?" in
        0) return 0 ;;
        1) return 1 ;;
        2) return 2 ;;
        *) return 2 ;;
    esac
}

if is_chroot; then
    echo 'use the chroot-safe path'
else
    status=$?
    if [ "$status" -eq 2 ]; then
        echo 'detection is uncertain' >&2
        exit 1
    fi
    echo 'use the host path'
fi

That structure treats uncertainty as a deliberate policy decision. If your operation is harmless either way, you may choose to continue on status 2. If it touches host files, package databases or service controls, stopping is the safer default.

3. Choose a fallback only when detection fails

The long options --default-false and --default-true change only the response to status 2. They do not force the command to report that the current process is, or is not, in a chroot when detection actually succeeds.

$ ischroot --default-false
$ printf 'status: %s\n' "$?"
status: 1
$ ischroot --default-true
$ printf 'status: %s\n' "$?"
status: 1

In this ordinary host environment detection succeeds, so both options still return 1. That is expected: the option names only become relevant when the command cannot determine the answer. Use one when a caller cannot handle a third result:

if ischroot --default-false; then
    echo 'inside a chroot'
else
    echo 'treat as outside a chroot'
fi

This is useful for a compatibility branch, but it deliberately throws away the distinction between "not in a chroot" and "detection failed". Prefer the three-way check when that distinction affects safety.

4. Keep detection separate from changes

ischroot does not enter a chroot and does not alter mounts, processes or files. That makes it a reasonable guard before a command that behaves differently in a build environment, but the guard itself is not a security boundary: a script must still validate paths, privileges and inputs before it changes anything.

For example, this prints a message and stops when the answer is uncertain, without running either branch:

status=0
ischroot
status=$?
case "$status" in
    0) echo 'chroot branch selected' ;;
    1) echo 'host branch selected' ;;
    2) echo 'cannot safely select a branch' >&2; exit 1 ;;
    *) echo 'unexpected detector failure' >&2; exit 1 ;;
esac

There is nothing to undo here, because these examples make no state changes. If you later add a package install, file removal or service restart after the guard, treat that as a separate operation: preview its target, use the least privilege needed and give it its own recovery path.

5. Diagnose the common mistakes

  • "ischroot: command not found". The program is not available through PATH. Check with command -v ischroot and install or repair Debianutils through your normal package-management process. Do not replace the detector with a guessed file test.
  • Every non-zero result treated as "outside". That silently converts status 2 into status 1. It may be acceptable for a low-risk informational message, but it is a poor default before host-sensitive work. Record the original status before printing diagnostics or calling another command.
  • Unexpected output appears. Run ischroot --help or ischroot --version separately: those options intentionally print to standard output and exit successfully. A normal detection result communicates through the status code, not a line of text.

Done means

  • Verified the installed Debianutils and ischroot versions.
  • Distinguished status 0, status 1 and status 2 in your check.
  • Captured $? immediately after the detector runs.
  • Used a default option only when collapsing uncertainty was an intentional policy.
  • Kept any later privileged or destructive action outside the detection step.