### Install shfmt Source: https://github.com/mvdan/sh/blob/master/README.md Install the shfmt command-line tool to format shell programs. Ensure you have Go 1.25 or later installed. ```bash go install mvdan.cc/sh/v3/cmd/shfmt@latest ``` -------------------------------- ### Run Command and Get Exit Code Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Example demonstrating how to run a command and then extract its exit code using the ExitStatus function. ```Go err := r.Run(ctx, file) exitCode := interp.ExitStatus(err) ``` -------------------------------- ### Environment Variable Lookup Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Creates an environment from string pairs and retrieves the value of the HOME variable. ```go env := expand.ListEnviron(os.Environ()...) v := env.Get("HOME") if v.IsSet() { fmt.Println(v.String()) } ``` -------------------------------- ### Custom ExecHandlerFunc Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Implement ExecHandlerFunc to define custom logic for executing simple commands. Access HandlerContext for environment and I/O. ```go handler := func(ctx context.Context, args []string) error { hc := interp.HandlerCtx(ctx) fmt.Fprintf(hc.Stderr, "Running: %v\n", args) return nil // exit code 0 } ``` -------------------------------- ### Install gosh Source: https://github.com/mvdan/sh/blob/master/README.md Install the gosh proof-of-concept shell, which utilizes the interp package. ```bash go install mvdan.cc/sh/v3/cmd/gosh@latest ``` -------------------------------- ### Configure Runner with Default Handler Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Example of initializing a new interp Runner, specifying the DefaultExecHandler with a custom kill timeout. ```Go r, err := interp.New( interp.ExecHandler(interp.DefaultExecHandler(2*time.Second)), ) ``` -------------------------------- ### Custom OpenHandlerFunc Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Implement OpenHandlerFunc to control how files are opened during redirection, specifying path, flags, and permissions. ```go handler := func(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { // Custom file opening logic return nil, nil } ``` -------------------------------- ### Pattern Mode Examples Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/pattern.md Demonstrates various pattern matching modes to control glob expansion behavior. Select the appropriate mode based on your matching requirements, such as case sensitivity, extended operators, or path matching. ```Go // Match entire filename case-sensitively pattern.Regexp("*.txt", pattern.EntireString) // Match with extended globs like ?(foo) and +(bar) pattern.Regexp("+(abc|def)", pattern.ExtendedOperators) // Case-insensitive glob pattern.Regexp("*.TXT", pattern.EntireString|pattern.NoGlobCase) // Allow * to match slashes pattern.Regexp("src/**/*.go", pattern.Filenames) // Match . files pattern.Regexp(".*", pattern.GlobLeadingDot) ``` -------------------------------- ### Example Handler with Error Handling Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Demonstrates how to handle different error types within a command handler function, including context cancellation, command not found, and execution errors. ```go handler := func(ctx context.Context, args []string) error { // Check context cancellation if err := ctx.Err(); err != nil { return err // Fatal error } // Command not found if !commandExists(args[0]) { fmt.Fprintf(hc.Stderr, "command not found: %s\n", args[0]) return interp.NewExitStatus(127) // Exit status 127 } // Execute command... if execError := execCommand(args); execError != nil { return interp.NewExitStatus(1) // General error status } return nil // Success } ``` -------------------------------- ### Custom ReadDirHandlerFunc2 Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Implement ReadDirHandlerFunc2 to provide custom logic for reading directory entries during globbing. ```go handler := func(ctx context.Context, path string) ([]fs.DirEntry, error) { // Custom directory reading logic return nil, nil } ``` -------------------------------- ### Custom StatHandlerFunc Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Implement StatHandlerFunc to define custom behavior for retrieving file information, including whether to follow symbolic links. ```go handler := func(ctx context.Context, name string, followSymlinks bool) (fs.FileInfo, error) { // Custom stat logic return nil, nil } ``` -------------------------------- ### Custom CallHandlerFunc Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Implement CallHandlerFunc to intercept and potentially modify command arguments before execution. ```go handler := func(ctx context.Context, args []string) ([]string, error) { // Modify args here if needed return args, nil } ``` -------------------------------- ### Construct Binary Arithmetic Operation Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Example of how to construct a binary arithmetic operation in Go using the syntax package. This is useful for programmatically building shell command structures. ```go binArith := &syntax.BinaryArithm{ Op: syntax.Add, X: left, Y: right, } ``` -------------------------------- ### Arithmetic Operator Precedence Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Demonstrates operator precedence in arithmetic expressions. Multiplication is performed before addition, resulting in 7 instead of 9. ```go // 1 + 2 * 3 == 7, not 9 // Because * has higher precedence than + result := 1 + 2*3 // Uses BinAritOperator precedence ``` -------------------------------- ### Go Syntax for Process Substitution Operator Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Represents process substitution, allowing the output of a command to be treated as a file. This example shows input from a process. ```go procSubst := &syntax.ProcSubst{ Op: syntax.CmdIn, Stmts: []*syntax.Stmt{cmdStmt}, } // Represents: <(command) ``` -------------------------------- ### Go Syntax for Extended Glob Operator Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Represents an extended glob pattern for matching filenames. This example uses the 'zero or more' operator. ```go extGlob := &syntax.ExtGlob{ Op: syntax.GlobZeroOrMore, Pattern: &syntax.Lit{Value: "*.go"}, } // Represents: *(*.go) ``` -------------------------------- ### Parsing Error Format Example Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Illustrates the format of parsing errors, which includes filename, line number, and column information. ```shell script.sh:5:12: unexpected token ``` -------------------------------- ### Environ Interface Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Interface for environment variables, providing methods to get and iterate over variables. ```go type Environ interface { Get(name string) Variable Each(fn func(name string, vr Variable) bool) bool } ``` -------------------------------- ### Go Syntax for Parameter Name Expansion Operator Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Represents parameter name expansion, used to access variable names based on a pattern. This example uses the 'all parameter names with prefix' operator. ```go paramExp := &syntax.ParamExp{ Excl: true, Names: syntax.NamesPrefix, Param: &syntax.Lit{Value: "myvar"}, } // Represents: ${!myvar*} ``` -------------------------------- ### Handle Command Exit Status Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Example of using NewExitStatus within a handler to return a specific exit code when a condition is met. A nil return indicates an exit code of 0. ```Go func handler(ctx context.Context, args []string) error { if len(args) == 0 { return interp.NewExitStatus(1) } return nil // exit code 0 } ``` -------------------------------- ### Handle pattern.SyntaxError for Invalid Glob Patterns Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md This example shows how to detect and report a SyntaxError when a glob pattern has invalid syntax. It also demonstrates unwrapping the underlying error if one exists. ```Go re, err := pattern.Regexp("[invalid", 0) if err != nil { if se, ok := err.(pattern.SyntaxError); ok { fmt.Printf("Pattern error: %v\n", se.Error()) if wrapped := se.Unwrap(); wrapped != nil { fmt.Printf("Caused by: %v\n", wrapped) } } } ``` -------------------------------- ### Handle Negated Extended Glob Pattern by Inverting Match Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md This example provides a strategy for handling negated extended glob patterns by matching the inner pattern and inverting the result. It demonstrates extracting the inner pattern and compiling a regex for it. ```Go innerPattern := input[group.Start+1 : group.End-1] // Remove !( and ) innerRe, _ := pattern.Regexp(innerPattern, flags) compiled := regexp.MustCompile(innerRe) // To check if a string matches the negation pattern: matches := !compiled.MatchString(testString) // Invert the result ``` -------------------------------- ### Extract Exit Code from an Error using ExitStatus Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md This example shows how to use the interp.ExitStatus function to retrieve the exit code from an error returned by a script run. It handles nil errors and non-exit errors by returning 0 and 1 respectively. ```Go err := r.Run(ctx, file) exitCode := interp.ExitStatus(err) // 0-255 if exitCode != 0 { fmt.Printf("Script failed with exit code %d\n", exitCode) } ``` -------------------------------- ### Full Interpreter Configuration Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Sets up an interpreter with environment, working directory, parameters, standard I/O, and an execution handler with a timeout. ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.Dir(workDir), interp.Params("arg1", "arg2"), interp.StdIO(os.Stdin, os.Stdout, os.Stderr), interp.ExecHandler(interp.DefaultExecHandler(2*time.Second)), ) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Basic expand.Config Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Sets up the basic environment for shell expansions using the system environment variables. ```go cfg := &expand.Config{ Env: expand.ListEnviron(os.Environ()...), } ``` -------------------------------- ### Lit Type Definition Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Defines a literal string value with its start and end positions. ```go type Lit struct { ValuePos, ValueEnd Pos Value string } ``` -------------------------------- ### Interpreter Configuration with Custom File and Stat Handlers Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Sets up an interpreter with custom handlers for file opening and directory reading, and a custom stat handler for file information retrieval. ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.OpenHandler(func(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { // Custom file handling logic return os.OpenFile(path, flag, perm) }), interp.ReadDirHandler2(os.ReadDir), interp.StatHandler(func(ctx context.Context, name string, followSymlinks bool) (fs.FileInfo, error) { if followSymlinks { return os.Stat(name) } return os.Lstat(name) }), ) ``` -------------------------------- ### StatHandlerFunc Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Gets file information. This function is used by the StatHandler to customize file stat operations. ```APIDOC ## StatHandlerFunc ### Description Gets file information. This function is used by the StatHandler to customize file stat operations. ### Signature `func(ctx context.Context, name string, followSymlinks bool) (fs.FileInfo, error)` ### Parameters - `ctx` (context.Context) - Context with HandlerContext value. - `name` (string) - File path. - `followSymlinks` (bool) - Whether to follow symbolic links. ### Return Type `(fs.FileInfo, error)` - File info or error. ``` -------------------------------- ### Execute a Shell Script Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Set up an interpreter runner with environment, working directory, parameters, and standard I/O, then parse and execute a shell script. Requires importing 'context', 'log', 'os', 'time', and packages from 'mvdan.cc/sh/v3'. ```go import ( "context" "log" "os" "time" "mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/syntax" ) // Create runner r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.Dir(workDir), interp.Params(scriptArgs...), interp.StdIO(os.Stdin, os.Stdout, os.Stderr), ) if err != nil { log.Fatal(err) } // Parse script parser := syntax.NewParser() file, err := parser.Parse(os.Stdin, "script.sh") if err != nil { log.Fatal(err) } // Execute ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() err = r.Run(ctx, file) exitCode := interp.ExitStatus(err) ``` -------------------------------- ### Expand Shell Environment Variables Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md This snippet demonstrates how to set up and expand environment variables for shell code. It requires the 'expand' package and a configuration with a custom environment list. ```go env := expand.ListEnviron( "1=first_arg", "2=second_arg", "#=2", "@=first_arg second_arg", "?=0", "$=1234", "HOME=/home/user", "PWD=/home/user", "PATH=/usr/bin:/bin", "IFS= \t\n", ) cfg := &expand.Config{Env: env} result, _ := expand.Literal(cfg, word) ``` -------------------------------- ### Create a New Printer Instance Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Creates a new printer instance for formatting syntax trees back to shell code. Options can be provided for customization. ```go printer := syntax.NewPrinter(syntax.Indent(4), syntax.Minify(true)) ``` -------------------------------- ### Execute Shell Script Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md Initialize an interpreter with environment and directory settings, then run a parsed shell script file. Handles potential errors during execution. ```go r, _ := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.Dir(os.Getenv("PWD")), ) err := r.Run(ctx, file) ``` -------------------------------- ### Minimal Interpreter Configuration Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Creates a new interpreter instance with environment variables loaded from the OS. ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), ) ``` -------------------------------- ### Getting Position Information on Parse Error Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Shows how to retrieve and print detailed error messages, including position information, when parsing a script. ```go file, err := parser.Parse(reader, "script.sh") if err != nil { // Error message includes file:line:col information fmt.Printf("Parse error: %v\n", err) } ``` -------------------------------- ### interp.New Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Creates a new Runner instance with optional configuration. This is the entry point for setting up the shell interpreter. ```APIDOC ## New ### Description Creates a new Runner with optional configuration. Returns error if configuration is invalid. ### Signature ```go func New(opts ...RunnerOption) (*Runner, error) ``` ### Parameters #### Runner Options - **opts** ([]RunnerOption) - Variable number of runner options ### Return Type `(*Runner, error)` - New runner or error ### Example ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.Dir(os.Getenv("PWD")), ) if err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### Create a New Runner Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Creates a new Runner instance with optional configurations like environment, working directory, and I/O streams. Use this to set up the execution environment for shell scripts. ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.Dir(os.Getenv("PWD")), ) if err != nil { log.Fatal(err) } ``` ```go r, err := interp.New(interp.Env(expand.ListEnviron(os.Environ()...))) ``` ```go r, err := interp.New(interp.Dir("/home/user")) ``` ```go r, err := interp.New(interp.Params("arg1", "arg2")) ``` ```go r, err := interp.New(interp.StdIO(os.Stdin, os.Stdout, os.Stderr)) ``` ```go r, err := interp.New( interp.ExecHandler(interp.DefaultExecHandler(2*time.Second)), ) ``` -------------------------------- ### Word AST Node Type Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Represents a shell word, which can consist of multiple word parts. Provides methods to get its position, end position, and literal string value if applicable. ```go type Word struct { Parts []WordPart } ``` -------------------------------- ### Create a New Parser Instance Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Creates a new parser instance with optional configuration. The returned parser can be reused for multiple parses. ```go parser := syntax.NewParser(syntax.Language(syntax.LangBash)) ``` -------------------------------- ### Run Go fuzzing for syntax parsing Source: https://github.com/mvdan/sh/blob/master/README.md Execute Go's native fuzzing support for the syntax package. Navigate to the syntax directory and run the fuzz test. ```bash cd syntax go test -run=- -fuzz=ParsePrint ``` -------------------------------- ### Setting Positional Parameters for Expansion Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Configures the environment to include positional parameters like $1, $2, and the count of parameters (#) for expansion. ```go cfg := &expand.Config{ Env: expand.ListEnviron( "1=first", "2=second", "#=2", ), } ``` -------------------------------- ### Create Default Execution Handler Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md DefaultExecHandler returns a handler that locates and executes binaries found in the system's PATH. Configure the kill timeout for processes. ```Go func DefaultExecHandler(killTimeout time.Duration) ExecHandlerFunc ``` -------------------------------- ### Apply Multiple Formatting Options to Shell Code Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Configure shell code formatting with multiple options, including indentation, line breaks, and spacing. Imports for 'syntax' and 'os' are necessary. ```go printer := syntax.NewPrinter( syntax.Indent(4), syntax.BinaryNextLine(true), syntax.SwitchCaseIndent(true), syntax.SpaceRedirects(true), ) printer.Print(os.Stdout, file) ``` -------------------------------- ### Config Type Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Configuration for expansion, including environment, command substitution, and globbing options. ```go type Config struct { Env Environ CmdSubst func(io.Writer, *syntax.CmdSubst) error ProcSubst func(*syntax.ProcSubst) (string, error) ReadDir2 func(string) ([]fs.DirEntry, error) GlobStar bool DotGlob bool NoCaseGlob bool NullGlob bool NoUnset bool ExtGlob bool } ``` -------------------------------- ### Handle Windows Paths in Shell Fields Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/shell.md Demonstrates how to correctly handle Windows paths when using shell.Fields, requiring double backslashes or single quotes for proper parsing. ```go // on Windows shell.Fields("echo C:\\foo\\bar", os.Getenv) // double backslashes // OR shell.Fields("echo 'C:\\foo\\bar'", os.Getenv) // single quotes ``` -------------------------------- ### Handling Expansion Errors Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Demonstrates how to handle errors during literal expansion, including checking for specific error types like UnexpectedCommandError. ```go result, err := expand.Literal(cfg, word) if err != nil { // Check for specific error types if _, ok := err.(expand.UnexpectedCommandError); ok { // Handle missing command substitution handler } // For other errors, treat as expansion failure log.Printf("Expansion failed: %v\n", err) } ``` -------------------------------- ### Generate Single-Line Compact Shell Code Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Use this to produce a compact, single-line output for shell code. Requires 'syntax' and 'os' packages. ```go printer := syntax.NewPrinter(syntax.SingleLine(true)) printer.Print(os.Stdout, file) ``` -------------------------------- ### expand.Config with Command Substitution Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Configures shell expansions to handle command substitutions by providing a custom handler function. ```go cfg := &expand.Config{ Env: expand.ListEnviron(os.Environ()...), CmdSubst: func(w io.Writer, node *syntax.CmdSubst) error { // Handle command substitution fmt.Fprint(w, "output") return nil }, } ``` -------------------------------- ### Format Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Expands a format string with arguments, similar to shell's `printf` command. It takes an expansion configuration, a format string, and a slice of arguments, returning the formatted string, the number of arguments consumed, and an error. ```APIDOC ## Format ### Description Expands a format string with arguments, similar to shell's `printf` command. ### Method func ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```go result, argsUsed, err := expand.Format(cfg, "%s %d\n", []string{"hello", "42"}) ``` ### Response #### Success Response - **string**: Formatted string - **int**: Number of arguments consumed - **error**: Error during formatting #### Response Example N/A ``` -------------------------------- ### Create Regex for Basic Filename Matching Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Generates a regular expression for matching filenames, ensuring the entire string is considered. Use the 'pattern' package. ```go re, _ := pattern.Regexp("*.txt", pattern.EntireString) ``` -------------------------------- ### Package Structure Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md The mvdan/sh library is organized into several packages, each handling specific functionalities like parsing, expansion, interpretation, and utilities. ```Go mvdan.cc/sh/v3 ├── syntax - Parsing and formatting ├── expand - Shell expansions ├── interp - Interpreter ├── shell - High-level API ├── pattern - Pattern matching ├── fileutil - File utilities ├── cmd/ - Command-line tools │ ├── shfmt - Shell formatter │ └── gosh - Shell interpreter proof-of-concept └── moreinterp - Extended interpreter features ``` -------------------------------- ### Interpreter Configuration with Custom Call and Exec Handlers Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Configures an interpreter with a custom call handler for processing arguments and an execution handler to measure command execution time. ```go r, err := interp.New( interp.Env(expand.ListEnviron(os.Environ()...)), interp.CallHandler(func(ctx context.Context, args []string) ([]string, error) { fmt.Printf("Running: %v\n", args) return args, nil }), interp.ExecHandlers( func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc { return func(ctx context.Context, args []string) error { start := time.Now() err := next(ctx, args) fmt.Printf("Command took %v\n", time.Since(start)) return err } }, ), ) ``` -------------------------------- ### Parse and Format a Shell Script Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Use the syntax package to parse a shell script and then format it with specified indentation. Requires importing 'os', 'log', and 'mvdan.cc/sh/v3/syntax'. ```go import ( "log" "os" "mvdan.cc/sh/v3/syntax" ) // Create parser parser := syntax.NewParser(syntax.Language(syntax.LangBash)) // Parse the script file, err := parser.Parse(os.Stdin, "script.sh") if err != nil { log.Fatal(err) } // Create printer with formatting options printer := syntax.NewPrinter(syntax.Indent(2)) // Format and output printer.Print(os.Stdout, file) ``` -------------------------------- ### Expand Shell Variables Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md Configure expansion settings, including environment variables, and perform literal expansion on a word. This is useful for processing variables without field splitting. ```go cfg := &expand.Config{ Env: expand.ListEnviron(os.Environ()...), } result, _ := expand.Literal(cfg, word) ``` -------------------------------- ### Handling Negation Patterns in Globbing Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Demonstrates how to handle errors when negation patterns are not supported by the globbing library. It shows an alternative approach to achieve similar results by inverting matches. ```go // When negation patterns are not supported re, err := pattern.Regexp(pat, pattern.ExtendedOperators) if err != nil { if negErr, ok := err.(*pattern.NegExtGlobError); ok { // Handle negation by matching and inverting fmt.Println("Cannot use ! patterns, using alternative approach") } else { // Other pattern errors log.Fatal(err) } } ``` -------------------------------- ### Handling UnexpectedCommandError Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Demonstrates how to catch and process an UnexpectedCommandError, which occurs when command substitution is attempted with a nil Config.CmdSubst. ```go result, err := expand.Literal(cfg, word) if err != nil { if uce, ok := err.(expand.UnexpectedCommandError); ok { fmt.Printf("Command substitution at %s\n", uce.Node.Pos()) } } ``` -------------------------------- ### Shell Piping with Error Handling Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Illustrates a common shell command combination using pipes and logical operators for error handling and conditional execution. ```go // cmd1 | cmd2 && cmd3 || cmd4 binCmd := &syntax.BinaryCmd{ Op: syntax.OrStmt, X: &syntax.BinaryCmd{ Op: syntax.AndStmt, X: &syntax.BinaryCmd{ Op: syntax.Pipe, X: stmt1, Y: stmt2, }, Y: stmt3, }, Y: stmt4, } ``` -------------------------------- ### Handling Parsing Errors with User-Friendly Output Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Shows how to parse a script and provide user-friendly error messages, including position information, to standard error. ```go file, err := parser.Parse(reader, "script.sh") if err != nil { // Error message is user-friendly, including position info fmt.Fprintf(os.Stderr, "Parse error: %v\n", err) os.Exit(1) } ``` -------------------------------- ### Interpreter Error Handling with Timeout and Cancellation Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Demonstrates robust error handling for script execution, including checks for context cancellation, timeouts, and retrieving specific exit codes. ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) deferr cancel() err := r.Run(ctx, file) if err != nil { // Check for context cancellation if err == context.DeadlineExceeded { fmt.Println("Script timed out") } else if err == context.Canceled { fmt.Println("Script was cancelled") } else { // Get exit status or report fatal error exitCode := interp.ExitStatus(err) if exitCode != 0 { fmt.Printf("Script exited with status %d\n", exitCode) } else { fmt.Printf("Error: %v\n", err) } } } ``` -------------------------------- ### Build shfmt Docker image Source: https://github.com/mvdan/sh/blob/master/README.md Build a Docker image for shfmt. This command creates an image tagged 'my:tag' from the local Dockerfile. ```bash docker build -t my:tag -f cmd/shfmt/Dockerfile . ``` -------------------------------- ### expand.Config with Globbing Support Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Configures shell expansions to enable advanced globbing features like recursive directory matching and extended glob patterns. ```go cfg := &expand.Config{ Env: expand.ListEnviron(os.Environ()...), ReadDir2: os.ReadDir, GlobStar: true, ExtGlob: true, } ``` -------------------------------- ### Conditional Shell Parsing by Language Variant Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Sets up a parser with a specific language variant (Bash, POSIX, Zsh) before parsing a script. Use this to ensure compatibility with different shell syntaxes. ```go var langVariant syntax.LangVariant switch scriptType { case "bash": langVariant = syntax.LangBash case "posix": langVariant = syntax.LangPOSIX case "zsh": langVariant = syntax.LangZsh default: langVariant = syntax.LangBash } parser := syntax.NewParser(syntax.Language(langVariant)) file, err := parser.Parse(reader, filename) ``` -------------------------------- ### interp.StdIO Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Runner option to configure standard input, output, and error streams. ```APIDOC ## StdIO ### Description Sets standard input, output, and error streams. ### Signature ```go func StdIO(in io.Reader, out, err io.Writer) RunnerOption ``` ### Parameters #### Runner Options - **in** (io.Reader) - Standard input - **out** (io.Writer) - Standard output - **err** (io.Writer) - Standard error ### Example ```go r, err := interp.New(interp.StdIO(os.Stdin, os.Stdout, os.Stderr)) ``` ``` -------------------------------- ### Variable Substitution with Custom Environment Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Performs literal variable substitution using a custom environment configuration. Use this when you need to control the environment variables available during expansion. ```go env := &customEnviron{ vars: map[string]string{ "USER": "alice", "HOME": "/home/alice", }, } cfg := &expand.Config{ Env: env, } result, err := expand.Literal(cfg, word) ``` -------------------------------- ### Use shfmt Docker image Source: https://github.com/mvdan/sh/blob/master/README.md Run a Docker container to execute shfmt. This mounts the current directory and sets the working directory to '/mnt' for processing shell scripts. ```bash docker run --rm -u "$(id -u):$(id -g)" -v "$PWD:/mnt" -w /mnt my:tag ``` -------------------------------- ### DefaultExecHandler Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Returns the default execution handler that finds binaries in PATH and executes them. ```APIDOC ## DefaultExecHandler ### Description Returns the default execution handler that finds binaries in PATH and executes them. ### Signature ```go func DefaultExecHandler(killTimeout time.Duration) ExecHandlerFunc ``` ### Parameters #### Path Parameters - **killTimeout** (time.Duration) - Required - Timeout before killing process (negative = immediate kill) ### Return Type `ExecHandlerFunc` - Default handler ### Example ```go r, err := interp.New( interp.ExecHandler(interp.DefaultExecHandler(2*time.Second)), ) ``` ``` -------------------------------- ### Handling Pattern Errors, Including Negation Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md Illustrates how to handle errors when compiling regular expressions from patterns, with special handling for negation patterns. ```go re, err := pattern.Regexp(pat, mode) if err != nil { // Check for negation pattern (requires special handling) if negErr, ok := err.(*pattern.NegExtGlobError); ok { // Handle negation patterns with workaround fmt.Println("Note: !(pattern) is not supported, use other patterns") } else { // Syntax error in pattern fmt.Printf("Invalid pattern: %v\n", err) } } ``` -------------------------------- ### Handle Pattern Syntax Errors Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/pattern.md Demonstrates how to catch and handle `SyntaxError` when a provided shell pattern is invalid. This is crucial for robust error management in applications that accept user-defined patterns. ```Go re, err := pattern.Regexp("[invalid", 0) if err != nil { if se, ok := err.(pattern.SyntaxError); ok { fmt.Println("Syntax error:", se.Error()) } } ``` -------------------------------- ### CallHandlerFunc Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Runs on every simple command before execution. Returns modified argument list. ```APIDOC ## CallHandlerFunc ### Description Runs on every simple command before execution. Returns modified argument list. ### Signature `func(ctx context.Context, args []string) ([]string, error)` ### Parameters - `ctx` (context.Context) - Context with HandlerContext value. - `args` ([]string) - Command arguments (never empty). ### Return Type `([]string, error)` - Modified arguments or error. ``` -------------------------------- ### Recursive Globbing for Subdirectories Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/pattern.md Implements recursive globbing using '**/*.go' to match files in any subdirectory. The Filenames option is crucial for enabling this recursive behavior. ```Go // Match any Go file in any subdirectory re, err := pattern.Regexp("**/*.go", pattern.Filenames|pattern.EntireString) if err != nil { log.Fatal(err) } compiled := regexp.MustCompile(re) if compiled.MatchString("src/main.go") { fmt.Println("Go file in subdirectory") } ``` -------------------------------- ### Parse as POSIX Shell Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Creates a new parser configured to parse shell scripts according to the POSIX standard. ```go parser := syntax.NewParser(syntax.Language(syntax.LangPOSIX)) file, err := parser.Parse(reader, "script.sh") ``` -------------------------------- ### Conditional Parameter Expansion: ${var:+use this if set} Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md This snippet demonstrates the syntax for conditional parameter expansion where a replacement word is used if the variable is set. This is useful for providing default values or alternative actions when a variable has a value. ```Go paramExp := &syntax.ParamExp{ Param: varName, Exp: &syntax.Expansion{ Op: syntax.AlternateUnsetOrNull, Word: replacement, }, } ``` -------------------------------- ### NewPrinter Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Creates a new Printer instance for formatting syntax trees back into shell code. ```APIDOC ## NewPrinter ### Description Creates a new Printer for formatting syntax trees back to shell code. ### Signature ```go func NewPrinter(opts ...PrinterOption) ``` ### Parameters #### Options - **opts** ([]PrinterOption) - Variable number of printer options ### Return Type - ***Printer** - A new printer instance ### Example ```go printer := syntax.NewPrinter(syntax.Indent(4), syntax.Minify(true)) ``` ``` -------------------------------- ### Go Module Path Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md The module path for the mvdan/sh library is `mvdan.cc/sh/v3`. ```Go mvdan.cc/sh/v3 ``` -------------------------------- ### Expand Shell Variables in a Command Source: https://github.com/mvdan/sh/blob/master/_autodocs/OVERVIEW.md Parses a command line, configures environment and globbing, and then expands fields for command arguments. Use this to process shell variables before command execution. ```go package main import ( "fmt" "log" "os" "strings" "mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/syntax" ) func main() { cmdLine := "echo $HOME $USER" // Parse command line parser := syntax.NewParser() file, err := parser.Parse(strings.NewReader(cmdLine), "") if err != nil { log.Fatal(err) } // Create expansion config cfg := &expand.Config{ Env: expand.ListEnviron(os.Environ()...), ReadDir2: os.ReadDir, GlobStar: true, } // Expand fields (for command arguments) fields, err := expand.Fields(cfg, file.Stmts[0].Cmd.(*syntax.CallExpr).Args...) if err != nil { log.Fatal(err) } for _, field := range fields { fmt.Println(field) } } ``` -------------------------------- ### LookPathDir Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Finds a binary in PATH, respecting the given directory and environment. ```APIDOC ## LookPathDir ### Description Finds a binary in PATH, respecting the given directory and environment. ### Signature ```go func LookPathDir(dir string, env expand.Environ, name string) (string, error) ``` ``` -------------------------------- ### Expand Format String with Arguments Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Format expands a format string with provided arguments, similar to the shell's printf command. It returns the formatted string, the number of arguments consumed, and any error encountered. ```go result, argsUsed, err := expand.Format(cfg, "%s %d\n", []string{"hello", "42"}) ``` -------------------------------- ### NewParser Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Creates a new Parser instance with optional configurations. The parser can be reused for multiple parsing operations. ```APIDOC ## NewParser ### Description Creates a new Parser with optional configuration. The returned Parser can be reused for multiple parses. ### Signature ```go func NewParser(options ...ParserOption) *Parser ``` ### Parameters #### Options - **options** ([]ParserOption) - Variable number of parser options ### Return Type - ***Parser** - A new parser instance ### Example ```go parser := syntax.NewParser(syntax.Language(syntax.LangBash)) ``` ``` -------------------------------- ### Parse and Analyze Shell Script Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md Create a shell parser with a specific language and parse a script file. This snippet demonstrates how to iterate through the parsed statements for analysis. ```go parser := syntax.NewParser(syntax.Language(syntax.LangBash)) file, _ := parser.Parse(reader, "script.sh") for _, stmt := range file.Stmts { // Analyze statements... } ``` -------------------------------- ### Create Regex for Recursive Directory Matching Source: https://github.com/mvdan/sh/blob/master/_autodocs/configuration.md Configures a regular expression for recursive directory matching, ensuring the entire string is matched. Requires 'pattern.Filenames' and 'pattern.EntireString' flags. ```go re, _ := pattern.Regexp("**/*.go", pattern.Filenames|pattern.EntireString) ``` -------------------------------- ### Command Interface Definition Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Defines the base interface for all command types in the AST. All command types must implement this interface. ```go type Command interface { Node commandNode() } ``` -------------------------------- ### Function Brace Placement Option Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Controls whether function opening braces are placed on the next line. This option is useful for enforcing specific coding styles. ```go func FunctionNextLine(enabled bool) PrinterOption ``` -------------------------------- ### Pretty-Print a Syntax Tree Node Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Pretty-prints a syntax tree node to a writer. Supports various node types including File, Stmt, Word, Assign, Command, and WordPart. ```go var buf bytes.Buffer printer := syntax.NewPrinter() if err := printer.Print(&buf, file); err != nil { log.Fatal(err) } fmt.Print(buf.String()) ``` -------------------------------- ### Format Shell Code Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md Create a code printer with specific indentation settings and use it to format a parsed shell script file, printing the output to standard output. ```go printer := syntax.NewPrinter(syntax.Indent(2)) printer.Print(os.Stdout, file) ``` -------------------------------- ### Runner.Run Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Executes a syntax tree node within the runner's context. This function is used to execute parsed shell commands. ```APIDOC ## Run ### Description Executes a syntax tree node. Can execute File, Stmt, Word, or any Command node. Returns exit status or error. ### Signature ```go func (r *Runner) Run(ctx context.Context, node syntax.Node) error ``` ### Parameters #### Path Parameters - **ctx** (context.Context) - Required - Context for cancellation and timeout - **node** (syntax.Node) - Required - Syntax tree node to execute ### Return Type `error` - Exit status (via NewExitStatus) or error ### Example ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) deferr cancel() err := r.Run(ctx, file) ``` ``` -------------------------------- ### Configure Parser Language Variant Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Sets the shell language variant for parsing. Use this option when creating a new parser. ```go parser := syntax.NewParser(syntax.Language(syntax.LangPOSIX)) ``` -------------------------------- ### interp.Env Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Runner option to set the environment for variable lookup. ```APIDOC ## Env ### Description Sets the environment for variable lookup. Required. ### Signature ```go func Env(env expand.Environ) RunnerOption ``` ### Parameters #### Runner Options - **env** (expand.Environ) - Environment variables ### Example ```go r, err := interp.New(interp.Env(expand.ListEnviron(os.Environ()...))) ``` ``` -------------------------------- ### Command Types Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Lists the various types of commands that implement the Command interface, representing different shell constructs. ```APIDOC ## Command Types All command types implement the Command interface: - `*CallExpr` - Simple command execution (e.g., `cmd arg1 arg2`) - `*IfClause` - Conditional statements - `*WhileClause` - While/until loops - `*ForClause` - For/select loops - `*CaseClause` - Case (switch) statements - `*Block` - Grouped commands in `{ }` - `*Subshell` - Commands in `( )` - `*BinaryCmd` - Binary operators (`&&`, `||`, `|`) - `*FuncDecl` - Function declarations - `*ArithmCmd` - Arithmetic commands `(( ))` - `*TestClause` - Test clauses `[[ ]]` - `*DeclClause` - Declaration statements - `*LetClause` - Let statements - `*TimeClause` - Time command - `*CoprocClause` - Coprocess clause ``` -------------------------------- ### Configure Interactive Mode Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Enables or disables interactive shell mode for the runner. This feature is experimental. ```go interp.Interactive(true) ``` -------------------------------- ### Expand Shell String Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/shell.md Expands a shell string with parameter expansions and command substitutions. Use this to process strings that may contain variables or command outputs. ```go result, err := shell.Expand("Hello $USER", os.Getenv) if err != nil { log.Fatal(err) } fmt.Println(result) // "Hello username" ``` -------------------------------- ### Match Glob Patterns with Regex Source: https://github.com/mvdan/sh/blob/master/_autodocs/README.md Convert a glob pattern to a regular expression with specified matching modes, then compile and use the regex to check if a string matches. ```go re, _ := pattern.Regexp("*.go", pattern.EntireString) compiled := regexp.MustCompile(re) matches := compiled.MatchString("main.go") ``` -------------------------------- ### Execute a Syntax Tree Node Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Executes a given syntax tree node within the runner's context. This is the primary method for running shell commands or scripts. Ensure a context with a timeout is used for long-running operations. ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) deferr cancel() err := r.Run(ctx, file) ``` -------------------------------- ### Redirect stdout to file Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Used to redirect standard output to a file, truncating it if it exists. This is represented by the RdrOut operator. ```Go redirect := &syntax.Redirect{ Op: syntax.RdrOut, // Redirect stdout Word: outputFile, } ``` -------------------------------- ### BracesSeq Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/expand.md Expands a word with brace expansion via an iterator. This function takes a configuration and a syntax.Word as input and returns an iterator that yields expanded words and errors. ```APIDOC ## BracesSeq ### Description Expands a word with brace expansion via an iterator. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Method Signature ```go func BracesSeq(cfg *Config, word *syntax.Word) iter.Seq2[*syntax.Word, error] ``` ### Return Type `iter.Seq2[*syntax.Word, error]` - Iterator yielding expanded words and errors ``` -------------------------------- ### interp.Dir Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Runner option to set the working directory for the interpreter. ```APIDOC ## Dir ### Description Sets the working directory. Must be absolute path. ### Signature ```go func Dir(path string) RunnerOption ``` ### Parameters #### Runner Options - **path** (string) - Absolute directory path ### Example ```go r, err := interp.New(interp.Dir("/home/user")) ``` ``` -------------------------------- ### interp.CallHandler Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Runner option to set a handler that runs on every simple command before execution. ```APIDOC ## CallHandler ### Description Sets a handler that runs on every simple command before execution. ### Signature ```go func CallHandler(f CallHandlerFunc) RunnerOption ``` ### Parameters #### Runner Options - **f** (CallHandlerFunc) - Call handler ``` -------------------------------- ### Runner Type Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Represents the shell interpreter, holding its environment, directory, parameters, variables, and functions. ```go type Runner struct { Env expand.Environ Dir string Params []string Vars map[string]expand.Variable Funcs map[string]*syntax.Stmt } ``` -------------------------------- ### Handle NegExtGlobError for Unsupported Negated Extended Glob Patterns Source: https://github.com/mvdan/sh/blob/master/_autodocs/errors.md This snippet illustrates how to catch a NegExtGlobError, which is returned when a negated extended glob pattern (e.g., !(*.go)) is used. It shows how to access the locations of these patterns. ```Go re, err := pattern.Regexp("!(*.go)", pattern.ExtendedOperators) if err != nil { if negErr, ok := err.(*pattern.NegExtGlobError); ok { fmt.Println("Negation patterns found:") for _, group := range negErr.Groups { pattern := input[group.Start:group.End] fmt.Printf(" At offset %d: %s\n", group.Start, pattern) } // Handle by matching inner pattern and inverting result fmt.Println("To handle this, match the inner pattern and invert the result") } } ``` -------------------------------- ### Shell Redirecting All Output Source: https://github.com/mvdan/sh/blob/master/_autodocs/operators-reference.md Demonstrates redirecting both standard output and standard error to a file using the RdrAll operator. ```go // cmd &> file redirect := &syntax.Redirect{ Op: syntax.RdrAll, // Both stdout and stderr Word: outputFile, } ``` -------------------------------- ### ExecHandlerFunc Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/interp.md Executes simple commands. Called for all CallExpr nodes where the first argument is not a declared function or builtin. ```APIDOC ## ExecHandlerFunc ### Description Executes simple commands. Called for all CallExpr nodes where the first argument is not a declared function or builtin. ### Signature `func(ctx context.Context, args []string) error` ### Parameters - `ctx` (context.Context) - Context with HandlerContext value. - `args` ([]string) - Command arguments (never empty). ### Return Type `error` - Nil for success, NewExitStatus(code) for exit code, or other error. ### Example ```go handler := func(ctx context.Context, args []string) error { hc := interp.HandlerCtx(ctx) fmt.Fprintf(hc.Stderr, "Running: %v\n", args) return nil // exit code 0 } ``` ``` -------------------------------- ### WordPart Interface Definition Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Defines the base interface for all word part types in the AST. All word part types must implement this interface. ```go type WordPart interface { Node wordPartNode() } ``` -------------------------------- ### SingleLine Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/syntax.md Prints the entire program on one line when possible. Some newlines still appear (after comments, around here-documents). This is a Printer Option. ```APIDOC ## SingleLine ### Description Prints the entire program on one line when possible. Some newlines still appear (after comments, around here-documents). ### Signature ```go func SingleLine(enabled bool) PrinterOption ``` ``` -------------------------------- ### Handling NegExtGlobError Source: https://github.com/mvdan/sh/blob/master/_autodocs/api-reference/pattern.md Demonstrates how to catch and handle NegExtGlobError when a negation extglob pattern is encountered. This error occurs because Go's standard regexp package does not support negative lookahead. ```Go re, err := pattern.Regexp("!(*.go)", pattern.ExtendedOperators) if negErr, ok := err.(*pattern.NegExtGlobError); ok { // Handle by matching the inner pattern and inverting fmt.Println("Negation groups:", negErr.Groups) // Groups[i].Start and Groups[i].End give offsets in original pattern } ``` -------------------------------- ### LetClause Command Type Source: https://github.com/mvdan/sh/blob/master/_autodocs/types.md Represents a let statement, used for arithmetic evaluation. It contains a list of arithmetic expressions to evaluate. ```go type LetClause struct { Let Pos Exprs []ArithmExpr } ```