Help us shape the future of Go! Take the 2026 Go Developer Survey

shellparse

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package shellparse reads a shell command line roughly as a shell would: quotes (including $'...' escapes and $"..."), a leading backslash, unquoted $IFS as a word break, operators, subshells, $( ) and backtick substitutions, and the strings run by sh -c, eval and ssh. It does not expand other variables, globs or braces. It has no dependencies. The blocking bash check (tools/builtin) and the advisory destructive-bash rule and watchdog (rules) both read commands with it, and share its rm policy (DestructiveRm), so the rule sees whatever the check refuses.

Index

Constants

View Source
const MaxDepth = 4

MaxDepth bounds recursion into nested shells (sh -c, eval, ssh, substitutions).

Variables

View Source
var ShellValueOptions = map[string]bool{"-o": true, "+o": true, "-O": true, "+O": true, "--rcfile": true, "--init-file": true}

ShellValueOptions are a shell's options that take the next word as their value.

View Source
var Shells = map[string]bool{"sh": true, "bash": true, "zsh": true, "dash": true, "ksh": true, "fish": true}

Shells are the commands whose -c argument is itself a command line.

View Source
var Wrappers = map[string]bool{"sudo": true, "command": true, "exec": true, "nohup": true, "env": true, "time": true, "nice": true, "doas": true, "builtin": true}

Wrappers run their arguments as a command.

Functions

func Command

func Command(words []string) (name string, args []string)

Command skips assignments (FOO=1, also after env) and wrappers (sudo, env, nohup, ... and their flags) to the command word, and returns its name (CommandName) and arguments. name is "" when there is no command word.

func CommandName

func CommandName(w string) string

CommandName is a command word as the shell resolves it: a leading backslash removed and the path dropped (/bin/rm -> rm). Quotes are already gone (Segments removes them).

func IsRedirect

func IsRedirect(w string) bool

IsRedirect: >, >>, 2>, &>, 2>&1, >file, <file ...

func RedirectTakesNext

func RedirectTakesNext(w string) bool

RedirectTakesNext: a bare operator ("> file") names its target in the next word; "2>&1" and ">file" do not.

func RmFlags

func RmFlags(args []string) (recursive, force bool, targets []string)

RmFlags reads rm's arguments: recursive (-r, -R, --recursive or a GNU prefix of it such as --rec), force (-f, --force or a prefix such as --for) and the paths it removes. Redirections are not paths.

func RmRefused

func RmRefused(words []string) bool

RmRefused reports whether a simple command (a word list) holds an rm the bash tool refuses: any word that resolves to rm, with the words after it as its arguments (rm -r rm / removes /). It reads every rm's arguments in one backward pass, so a line of many rm words stays linear.

func Segments

func Segments(s string) (segs [][]string, nested []string)

Segments splits a command line into simple commands (word lists, quotes removed) at ; & && || | newlines and parentheses, and returns the bodies of $( ) and backtick substitutions, wherever they appear (unquoted or in double quotes), as nested commands.

func Steps

func Steps() int64

Steps reports the running total of that work. Tests compare it across input sizes to check that reading a line stays linear; the count is deterministic, where wall-clock ratios are noisy on shared CI runners.

Types

type Result

type Result int

Result is what Walk found.

const (
	// None: fn returned false for every command.
	None Result = iota
	// Found: fn returned true for some command.
	Found
	// TooDeep: the line nests command strings past MaxDepth, so not every
	// command was visited. A caller that blocks should refuse.
	TooDeep
)

func DestructiveRm

func DestructiveRm(command string) Result

DestructiveRm reports whether any command in the line, nested ones included, is a recursive rm of a system or home path (Found), or whether the line nests command strings too deep to check (TooDeep). Every word that resolves to rm starts a check of the words after it, so wrappers (xargs, timeout 5, nice -n 5) do not hide it. The bash tool refuses what it reports; the advisory destructive-bash rule and the watchdog (rules.Destructive) see at least the same.

Out of scope (documented, not checked): relative targets that climb out with .., paths that come from a variable, a substitution or a glob or brace expansion (X=/; rm -rf $X), find -delete, deletes from other programs (python shutil.rmtree), chmod/chown -R, commands fed to a shell on stdin, and paths that reach rm through stdin (find / | xargs rm -rf) or a script.

func Walk

func Walk(cmd string, fn func(words []string) bool) Result

Walk calls fn with every simple command in cmd (a word list, quotes removed), including the commands nested in $( ), backticks, the command string of sh/bash/... -c (wherever -c sits among the shell's options), script -c, env -S, eval arguments and an ssh remote command, each also as its own command line. It stops at the first command fn returns true for.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL