Documentation ¶
Overview ¶
Package syntax implements parsing and formatting of shell programs. It supports POSIX Shell, Bash, and mksh.
Example ¶
package main import ( "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { r := strings.NewReader("{ foo; bar; }") f, err := syntax.NewParser().Parse(r, "") if err != nil { return } syntax.NewPrinter().Print(os.Stdout, f) }
Output: { foo bar }
Index ¶
- Constants
- func DebugPrint(w io.Writer, node Node) error
- func IsIncomplete(err error) bool
- func IsKeyword(word string) bool
- func Quote(s string, lang LangVariant) (string, error)
- func Simplify(n Node) bool
- func SplitBraces(word *Word) bool
- func ValidName(val string) bool
- func Walk(node Node, f func(Node) bool)
- type ArithmCmd
- type ArithmExp
- type ArithmExpr
- type ArrayElem
- type ArrayExpr
- type Assign
- type BinAritOperator
- type BinCmdOperator
- type BinTestOperator
- type BinaryArithm
- type BinaryCmd
- type BinaryTest
- type Block
- type BraceExp
- type CStyleLoop
- type CallExpr
- type CaseClause
- type CaseItem
- type CaseOperator
- type CmdSubst
- type Command
- type Comment
- type CoprocClause
- type DblQuoted
- type DeclClause
- type Expansion
- type ExtGlob
- type File
- type ForClause
- type FuncDecl
- type GlobOperator
- type IfClause
- type LangError
- type LangVariant
- type LetClause
- type Lit
- type Loop
- type Node
- type ParExpOperator
- type ParNamesOperator
- type ParamExp
- type ParenArithm
- type ParenTest
- type ParseError
- type Parser
- func (p *Parser) Arithmetic(r io.Reader) (ArithmExpr, error)
- func (p *Parser) Document(r io.Reader) (*Word, error)
- func (p *Parser) Incomplete() bool
- func (p *Parser) Interactive(r io.Reader, fn func([]*Stmt) bool) error
- func (p *Parser) Parse(r io.Reader, name string) (*File, error)
- func (p *Parser) Stmts(r io.Reader, fn func(*Stmt) bool) error
- func (p *Parser) Words(r io.Reader, fn func(*Word) bool) error
- type ParserOption
- type Pos
- type Printer
- type PrinterOption
- func BinaryNextLine(enabled bool) PrinterOption
- func FunctionNextLine(enabled bool) PrinterOption
- func Indent(spaces uint) PrinterOption
- func KeepPadding(enabled bool) PrinterOptiondeprecated
- func Minify(enabled bool) PrinterOption
- func SingleLine(enabled bool) PrinterOption
- func SpaceRedirects(enabled bool) PrinterOption
- func SwitchCaseIndent(enabled bool) PrinterOption
- type ProcOperator
- type ProcSubst
- type QuoteError
- type RedirOperator
- type Redirect
- type Replace
- type SglQuoted
- type Slice
- type Stmt
- type Subshell
- type TestClause
- type TestDecl
- type TestExpr
- type TimeClause
- type UnAritOperator
- type UnTestOperator
- type UnaryArithm
- type UnaryTest
- type WhileClause
- type Word
- type WordIter
- type WordPart
Examples ¶
Constants ¶
const ( RdrOut = RedirOperator(rdrOut) + iota // > AppOut // >> RdrIn // < RdrInOut // <> DplIn // <& DplOut // >& ClbOut // >| Hdoc // << DashHdoc // <<- WordHdoc // <<< RdrAll // &> AppAll // &>> )
const ( CmdIn = ProcOperator(cmdIn) + iota // <( CmdOut // >( )
const ( GlobZeroOrOne = GlobOperator(globQuest) + iota // ?( GlobZeroOrMore // *( GlobOneOrMore // +( GlobOne // @( GlobExcept // !( )
const ( AndStmt = BinCmdOperator(andAnd) + iota // && OrStmt // || Pipe // | PipeAll // |& )
const ( Break = CaseOperator(dblSemicolon) + iota // ;; Fallthrough // ;& Resume // ;;& ResumeKorn // ;| )
const ( NamesPrefix = ParNamesOperator(star) // * NamesPrefixWords = ParNamesOperator(at) // @ )
const ( AlternateUnset = ParExpOperator(plus) + iota // + AlternateUnsetOrNull // :+ DefaultUnset // - DefaultUnsetOrNull // :- ErrorUnset // ? ErrorUnsetOrNull // :? AssignUnset // = AssignUnsetOrNull // := RemSmallSuffix // % RemLargeSuffix // %% RemSmallPrefix // # RemLargePrefix // ## UpperFirst // ^ UpperAll // ^^ LowerFirst // , LowerAll // ,, OtherParamOps // @ )
const ( Not = UnAritOperator(exclMark) + iota // ! BitNegation // ~ Inc // ++ Dec // -- Plus = UnAritOperator(plus) // + Minus = UnAritOperator(minus) // - )
const ( Add = BinAritOperator(plus) // + Sub = BinAritOperator(minus) // - Mul = BinAritOperator(star) // * Quo = BinAritOperator(slash) // / Rem = BinAritOperator(perc) // % Pow = BinAritOperator(power) // ** Eql = BinAritOperator(equal) // == Gtr = BinAritOperator(rdrOut) // > Lss = BinAritOperator(rdrIn) // < Neq = BinAritOperator(nequal) // != Leq = BinAritOperator(lequal) // <= Geq = BinAritOperator(gequal) // >= And = BinAritOperator(and) // & Or = BinAritOperator(or) // | Xor = BinAritOperator(caret) // ^ Shr = BinAritOperator(appOut) // >> Shl = BinAritOperator(hdoc) // << AndArit = BinAritOperator(andAnd) // && OrArit = BinAritOperator(orOr) // || Comma = BinAritOperator(comma) // , TernQuest = BinAritOperator(quest) // ? TernColon = BinAritOperator(colon) // : Assgn = BinAritOperator(assgn) // = AddAssgn = BinAritOperator(addAssgn) // += SubAssgn = BinAritOperator(subAssgn) // -= MulAssgn = BinAritOperator(mulAssgn) // *= QuoAssgn = BinAritOperator(quoAssgn) // /= RemAssgn = BinAritOperator(remAssgn) // %= AndAssgn = BinAritOperator(andAssgn) // &= OrAssgn = BinAritOperator(orAssgn) // |= XorAssgn = BinAritOperator(xorAssgn) // ^= ShlAssgn = BinAritOperator(shlAssgn) // <<= ShrAssgn = BinAritOperator(shrAssgn) // >>= )
const ( TsExists = UnTestOperator(tsExists) + iota // -e TsRegFile // -f TsDirect // -d TsCharSp // -c TsBlckSp // -b TsNmPipe // -p TsSocket // -S TsSmbLink // -L TsSticky // -k TsGIDSet // -g TsUIDSet // -u TsGrpOwn // -G TsUsrOwn // -O TsModif // -N TsRead // -r TsWrite // -w TsExec // -x TsNoEmpty // -s TsFdTerm // -t TsEmpStr // -z TsNempStr // -n TsOptSet // -o TsVarSet // -v TsRefVar // -R TsNot = UnTestOperator(exclMark) // ! TsParen = UnTestOperator(leftParen) // ( )
const ( TsReMatch = BinTestOperator(tsReMatch) + iota // =~ TsNewer // -nt TsOlder // -ot TsDevIno // -ef TsEql // -eq TsNeq // -ne TsLeq // -le TsGeq // -ge TsLss // -lt TsGtr // -gt AndTest = BinTestOperator(andAnd) // && OrTest = BinTestOperator(orOr) // || TsMatchShort = BinTestOperator(assgn) // = TsMatch = BinTestOperator(equal) // == TsNoMatch = BinTestOperator(nequal) // != TsBefore = BinTestOperator(rdrIn) // < TsAfter = BinTestOperator(rdrOut) // > )
Variables ¶
This section is empty.
Functions ¶
func DebugPrint ¶
DebugPrint prints the provided syntax tree, spanning multiple lines and with indentation. Can be useful to investigate the content of a syntax tree.
Example ¶
package main import ( "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { in := strings.NewReader(`echo 'foo'`) f, err := syntax.NewParser().Parse(in, "") if err != nil { return } syntax.DebugPrint(os.Stdout, f) }
Output: *syntax.File { . Name: "" . Stmts: []*syntax.Stmt (len = 1) { . . 0: *syntax.Stmt { . . . Comments: []syntax.Comment (len = 0) {} . . . Cmd: *syntax.CallExpr { . . . . Assigns: []*syntax.Assign (len = 0) {} . . . . Args: []*syntax.Word (len = 2) { . . . . . 0: *syntax.Word { . . . . . . Parts: []syntax.WordPart (len = 1) { . . . . . . . 0: *syntax.Lit { . . . . . . . . ValuePos: 1:1 . . . . . . . . ValueEnd: 1:5 . . . . . . . . Value: "echo" . . . . . . . } . . . . . . } . . . . . } . . . . . 1: *syntax.Word { . . . . . . Parts: []syntax.WordPart (len = 1) { . . . . . . . 0: *syntax.SglQuoted { . . . . . . . . Left: 1:6 . . . . . . . . Right: 1:10 . . . . . . . . Dollar: false . . . . . . . . Value: "foo" . . . . . . . } . . . . . . } . . . . . } . . . . } . . . } . . . Position: 1:1 . . . Semicolon: 0:0 . . . Negated: false . . . Background: false . . . Coprocess: false . . . Redirs: []*syntax.Redirect (len = 0) {} . . } . } . Last: []syntax.Comment (len = 0) {} }
func IsIncomplete ¶
IsIncomplete reports whether a Parser error could have been avoided with extra input bytes. For example, if an io.EOF was encountered while there was an unclosed quote or parenthesis.
func IsKeyword ¶ added in v3.3.0
IsKeyword returns true if the given word is part of the language keywords.
func Quote ¶ added in v3.4.0
func Quote(s string, lang LangVariant) (string, error)
Quote returns a quoted version of the input string, so that the quoted version is expanded or interpreted as the original string in the given language variant.
Quoting is necessary when using arbitrary literal strings as words in a shell script or command. Without quoting, one can run into syntax errors, as well as the possibility of running unintended code.
An error is returned when a string cannot be quoted for a variant. For instance, POSIX lacks escape sequences for non-printable characters, and no language variant can represent a string containing null bytes. In such cases, the returned error type will be *QuoteError.
The quoting strategy is chosen on a best-effort basis, to minimize the amount of extra bytes necessary.
Some strings do not require any quoting and are returned unchanged. Those strings can be directly surrounded in single quotes as well.
Example ¶
package main import ( "fmt" "mvdan.cc/sh/v3/syntax" ) func main() { for _, s := range []string{ "foo", "bar $baz", `"won't"`, "~/home", "#1304", "name=value", "for", "glob-*", "invalid-\xe2'", "nonprint-\x0b\x1b", } { // We assume Bash syntax here. // For general shell syntax quoting, use syntax.LangPOSIX. quoted, err := syntax.Quote(s, syntax.LangBash) if err != nil { fmt.Printf("%q cannot be quoted: %v\n", s, err) } else { fmt.Printf("Quote(%17q): %s\n", s, quoted) } } }
Output: Quote( "foo"): foo Quote( "bar $baz"): 'bar $baz' Quote( "\"won't\""): "\"won't\"" Quote( "~/home"): '~/home' Quote( "#1304"): '#1304' Quote( "name=value"): 'name=value' Quote( "for"): 'for' Quote( "glob-*"): 'glob-*' Quote( "invalid-\xe2'"): $'invalid-\xe2\'' Quote("nonprint-\v\x1b"): $'nonprint-\v\x1b'
func Simplify ¶
Simplify modifies a node to remove redundant pieces of syntax, and returns whether any changes were made.
The changes currently applied are:
Remove clearly useless parentheses $(( (expr) )) Remove dollars from vars in exprs (($var)) Remove duplicate subshells $( (stmts) ) Remove redundant quotes [[ "$var" == str ]] Merge negations with unary operators [[ ! -n $var ]] Use single quotes to shorten literals "\$foo"
func SplitBraces ¶
SplitBraces parses brace expansions within a word's literal parts. If any valid brace expansions are found, they are replaced with BraceExp nodes, and the function returns true. Otherwise, the word is left untouched and the function returns false.
For example, a literal word "foo{bar,baz}" will result in a word containing the literal "foo", and a brace expansion with the elements "bar" and "baz".
It does not return an error; malformed brace expansions are simply skipped. For example, the literal word "a{b" is left unchanged.
func Walk ¶
Walk traverses a syntax tree in depth-first order: It starts by calling f(node); node must not be nil. If f returns true, Walk invokes f recursively for each of the non-nil children of node, followed by f(nil).
Example ¶
package main import ( "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { in := strings.NewReader(`echo $foo "and $bar"`) f, err := syntax.NewParser().Parse(in, "") if err != nil { return } syntax.Walk(f, func(node syntax.Node) bool { switch node := node.(type) { case *syntax.ParamExp: node.Param.Value = strings.ToUpper(node.Param.Value) } return true }) syntax.NewPrinter().Print(os.Stdout, f) }
Output: echo $FOO "and $BAR"
Types ¶
type ArithmCmd ¶
type ArithmCmd struct {
Left, Right Pos
Unsigned bool // mksh's ((# expr))
X ArithmExpr
}
ArithmCmd represents an arithmetic command.
This node will only appear in LangBash and LangMirBSDKorn.
type ArithmExp ¶
type ArithmExp struct {
Left, Right Pos
Bracket bool // deprecated $[expr] form
Unsigned bool // mksh's $((# expr))
X ArithmExpr
}
ArithmExp represents an arithmetic expansion.
type ArithmExpr ¶
type ArithmExpr interface { Node // contains filtered or unexported methods }
ArithmExpr represents all nodes that form arithmetic expressions.
These are *BinaryArithm, *UnaryArithm, *ParenArithm, and *Word.
type ArrayElem ¶
type ArrayElem struct { Index ArithmExpr Value *Word Comments []Comment }
ArrayElem represents a Bash array element.
Index can be nil; for example, declare -a x=(value). Value can be nil; for example, declare -A x=([index]=). Finally, neither can be nil; for example, declare -A x=([index]=value)
type ArrayExpr ¶
ArrayExpr represents a Bash array expression.
This node will only appear with LangBash.
type Assign ¶
type Assign struct { Append bool // += Naked bool // without '=' Name *Lit // must be a valid name Index ArithmExpr // [i], ["k"] Value *Word // =val Array *ArrayExpr // =(arr) }
Assign represents an assignment to a variable.
Here and elsewhere, Index can mean either an index expression into an indexed array, or a string key into an associative array.
If Index is non-nil, the value will be a word and not an array as nested arrays are not allowed.
If Naked is true and Name is nil, the assignment is part of a DeclClause and the argument (in the Value field) will be evaluated at run-time. This includes parameter expansions, which may expand to assignments or options.
type BinAritOperator ¶
type BinAritOperator token
func (BinAritOperator) String ¶
func (o BinAritOperator) String() string
type BinCmdOperator ¶
type BinCmdOperator token
func (BinCmdOperator) String ¶
func (o BinCmdOperator) String() string
type BinTestOperator ¶
type BinTestOperator token
func (BinTestOperator) String ¶
func (o BinTestOperator) String() string
type BinaryArithm ¶
type BinaryArithm struct { OpPos Pos Op BinAritOperator X, Y ArithmExpr }
BinaryArithm represents a binary arithmetic expression.
If Op is any assign operator, X will be a word with a single *Lit whose value is a valid name.
Ternary operators like "a ? b : c" are fit into this structure. Thus, if Op==TernQuest, Y will be a *BinaryArithm with Op==TernColon. Op can only be TernColon in that scenario.
func (*BinaryArithm) End ¶
func (b *BinaryArithm) End() Pos
func (*BinaryArithm) Pos ¶
func (b *BinaryArithm) Pos() Pos
type BinaryCmd ¶
type BinaryCmd struct { OpPos Pos Op BinCmdOperator X, Y *Stmt }
BinaryCmd represents a binary expression between two statements.
type BinaryTest ¶
type BinaryTest struct { OpPos Pos Op BinTestOperator X, Y TestExpr }
BinaryTest represents a binary test expression.
func (*BinaryTest) End ¶
func (b *BinaryTest) End() Pos
func (*BinaryTest) Pos ¶
func (b *BinaryTest) Pos() Pos
type Block ¶
Block represents a series of commands that should be executed in a nested scope. It is essentially a list of statements within curly braces.
type BraceExp ¶
BraceExp represents a Bash brace expression, such as "{a,f}" or "{1..10}".
This node will only appear as a result of SplitBraces.
type CStyleLoop ¶
type CStyleLoop struct {
Lparen, Rparen Pos
// Init, Cond, Post can each be nil, if the for loop construct omits it.
Init, Cond, Post ArithmExpr
}
CStyleLoop represents the behavior of a for clause similar to the C language.
This node will only appear with LangBash.
func (*CStyleLoop) End ¶
func (c *CStyleLoop) End() Pos
func (*CStyleLoop) Pos ¶
func (c *CStyleLoop) Pos() Pos
type CallExpr ¶
CallExpr represents a command execution or function call, otherwise known as a "simple command".
If Args is empty, Assigns apply to the shell environment. Otherwise, they are variables that cannot be arrays and which only apply to the call.
type CaseClause ¶
type CaseClause struct {
Case, In, Esac Pos
Braces bool // deprecated mksh form with braces instead of in/esac
Word *Word
Items []*CaseItem
Last []Comment
}
CaseClause represents a case (switch) clause.
func (*CaseClause) End ¶
func (c *CaseClause) End() Pos
func (*CaseClause) Pos ¶
func (c *CaseClause) Pos() Pos
type CaseItem ¶
type CaseItem struct { Op CaseOperator OpPos Pos // unset if it was finished by "esac" Comments []Comment Patterns []*Word Stmts []*Stmt Last []Comment }
CaseItem represents a pattern list (case) within a CaseClause.
type CaseOperator ¶
type CaseOperator token
func (CaseOperator) String ¶
func (o CaseOperator) String() string
type CmdSubst ¶
type CmdSubst struct {
Left, Right Pos
Stmts []*Stmt
Last []Comment
Backquotes bool // deprecated `foo`
TempFile bool // mksh's ${ foo;}
ReplyVar bool // mksh's ${|foo;}
}
CmdSubst represents a command substitution.
type Command ¶
type Command interface { Node // contains filtered or unexported methods }
Command represents all nodes that are simple or compound commands, including function declarations.
These are *CallExpr, *IfClause, *WhileClause, *ForClause, *CaseClause, *Block, *Subshell, *BinaryCmd, *FuncDecl, *ArithmCmd, *TestClause, *DeclClause, *LetClause, *TimeClause, and *CoprocClause.
Example ¶
package main import ( "fmt" "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { r := strings.NewReader("echo foo; if x; then y; fi; foo | bar") f, err := syntax.NewParser().Parse(r, "") if err != nil { return } printer := syntax.NewPrinter() for i, stmt := range f.Stmts { fmt.Printf("Cmd %d: %-20T - ", i, stmt.Cmd) printer.Print(os.Stdout, stmt.Cmd) fmt.Println() } }
Output: Cmd 0: *syntax.CallExpr - echo foo Cmd 1: *syntax.IfClause - if x; then y; fi Cmd 2: *syntax.BinaryCmd - foo | bar
type CoprocClause ¶
CoprocClause represents a Bash coproc clause.
This node will only appear with LangBash.
func (*CoprocClause) End ¶
func (c *CoprocClause) End() Pos
func (*CoprocClause) Pos ¶
func (c *CoprocClause) Pos() Pos
type DeclClause ¶
type DeclClause struct { // Variant is one of "declare", "local", "export", "readonly", // "typeset", or "nameref". Variant *Lit Args []*Assign }
DeclClause represents a Bash declare clause.
Args can contain a mix of regular and naked assignments. The naked assignments can represent either options or variable names.
This node will only appear with LangBash.
func (*DeclClause) End ¶
func (d *DeclClause) End() Pos
func (*DeclClause) Pos ¶
func (d *DeclClause) Pos() Pos
type Expansion ¶
type Expansion struct { Op ParExpOperator Word *Word }
Expansion represents string manipulation in a ParamExp other than those covered by Replace.
type ExtGlob ¶
type ExtGlob struct { OpPos Pos Op GlobOperator Pattern *Lit }
ExtGlob represents a Bash extended globbing expression. Note that these are parsed independently of whether shopt has been called or not.
This node will only appear in LangBash and LangMirBSDKorn.
type ForClause ¶
type ForClause struct {
ForPos, DoPos, DonePos Pos
Select bool
Braces bool // deprecated form with { } instead of do/done
Loop Loop
Do []*Stmt
DoLast []Comment
}
ForClause represents a for or a select clause. The latter is only present in Bash.
type FuncDecl ¶
type FuncDecl struct { Position Pos RsrvWord bool // non-posix "function f" style Parens bool // with () parentheses, only meaningful with RsrvWord=true Name *Lit Body *Stmt }
FuncDecl represents the declaration of a function.
type GlobOperator ¶
type GlobOperator token
func (GlobOperator) String ¶
func (o GlobOperator) String() string
type IfClause ¶
type IfClause struct { Position Pos // position of the starting "if", "elif", or "else" token ThenPos Pos // position of "then", empty if this is an "else" FiPos Pos // position of "fi", shared with .Else if non-nil Cond []*Stmt CondLast []Comment Then []*Stmt ThenLast []Comment Else *IfClause // if non-nil, an "elif" or an "else" Last []Comment // comments on the first "elif", "else", or "fi" }
IfClause represents an if statement.
type LangError ¶
type LangError struct { Filename string Pos Pos Feature string Langs []LangVariant }
LangError is returned when the parser encounters code that is only valid in other shell language variants. The error includes what feature is not present in the current language variant, and what languages support it.
type LangVariant ¶
type LangVariant int
LangVariant describes a shell language variant to use when tokenizing and parsing shell code. The zero value is LangBash.
const ( // LangBash corresponds to the GNU Bash language, as described in its // manual at https://www.gnu.org/software/bash/manual/bash.html. // // We currently follow Bash version 5.2. // // Its string representation is "bash". LangBash LangVariant = iota // LangPOSIX corresponds to the POSIX Shell language, as described at // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html. // // Its string representation is "posix" or "sh". LangPOSIX // LangMirBSDKorn corresponds to the MirBSD Korn Shell, also known as // mksh, as described at http://www.mirbsd.org/htman/i386/man1/mksh.htm. // Note that it shares some features with Bash, due to the the shared // ancestry that is ksh. // // We currently follow mksh version 59. // // Its string representation is "mksh". LangMirBSDKorn // LangBats corresponds to the Bash Automated Testing System language, // as described at https://github.com/bats-core/bats-core. Note that // it's just a small extension of the Bash language. // // Its string representation is "bats". LangBats // LangAuto corresponds to automatic language detection, // commonly used by end-user applications like shfmt, // which can guess a file's language variant given its filename or shebang. // // At this time, [Variant] does not support LangAuto. LangAuto )
func (*LangVariant) Set ¶ added in v3.2.0
func (l *LangVariant) Set(s string) error
func (LangVariant) String ¶
func (l LangVariant) String() string
type LetClause ¶
type LetClause struct { Let Pos Exprs []ArithmExpr }
LetClause represents a Bash let clause.
This node will only appear in LangBash and LangMirBSDKorn.
type Lit ¶
Lit represents a string literal.
Note that a parsed string literal may not appear as-is in the original source code, as it is possible to split literals by escaping newlines. The splitting is lost, but the end position is not.
type Loop ¶
type Loop interface { Node // contains filtered or unexported methods }
Loop holds either *WordIter or *CStyleLoop.
type Node ¶
type Node interface { // Pos returns the position of the first character of the node. Comments // are ignored, except if the node is a *File. Pos() Pos // End returns the position of the character immediately after the node. // If the character is a newline, the line number won't cross into the // next line. Comments are ignored, except if the node is a *File. End() Pos }
Node represents a syntax tree node.
type ParExpOperator ¶
type ParExpOperator token
func (ParExpOperator) String ¶
func (o ParExpOperator) String() string
type ParNamesOperator ¶
type ParNamesOperator token
func (ParNamesOperator) String ¶
func (o ParNamesOperator) String() string
type ParamExp ¶
type ParamExp struct {
Dollar, Rbrace Pos
Short bool // $a instead of ${a}
Excl bool // ${!a}
Length bool // ${#a}
Width bool // ${%a}
Param *Lit
Index ArithmExpr // ${a[i]}, ${a["k"]}
Slice *Slice // ${a:x:y}
Repl *Replace // ${a/x/y}
Names ParNamesOperator // ${!prefix*} or ${!prefix@}
Exp *Expansion // ${a:-b}, ${a#b}, etc
}
ParamExp represents a parameter expansion.
type ParenArithm ¶
type ParenArithm struct {
Lparen, Rparen Pos
X ArithmExpr
}
ParenArithm represents an arithmetic expression within parentheses.
func (*ParenArithm) End ¶
func (p *ParenArithm) End() Pos
func (*ParenArithm) Pos ¶
func (p *ParenArithm) Pos() Pos
type ParseError ¶
ParseError represents an error found when parsing a source file, from which the parser cannot recover.
func (ParseError) Error ¶
func (e ParseError) Error() string
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
Parser holds the internal state of the parsing mechanism of a program.
func NewParser ¶
func NewParser(options ...ParserOption) *Parser
NewParser allocates a new Parser and applies any number of options.
Example (Options) ¶
package main import ( "fmt" "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { src := "for ((i = 0; i < 5; i++)); do echo $i >f; done" // LangBash is the default r := strings.NewReader(src) f, err := syntax.NewParser().Parse(r, "") fmt.Println(err) // Parser errors with LangPOSIX r = strings.NewReader(src) _, err = syntax.NewParser(syntax.Variant(syntax.LangPOSIX)).Parse(r, "") fmt.Println(err) syntax.NewPrinter().Print(os.Stdout, f) syntax.NewPrinter(syntax.SpaceRedirects(true)).Print(os.Stdout, f) }
Output: <nil> 1:5: c-style fors are a bash feature for ((i = 0; i < 5; i++)); do echo $i >f; done for ((i = 0; i < 5; i++)); do echo $i > f; done
func (*Parser) Arithmetic ¶
func (p *Parser) Arithmetic(r io.Reader) (ArithmExpr, error)
Arithmetic parses a single arithmetic expression. That is, as if the input were within the $(( and )) tokens.
func (*Parser) Document ¶
Document parses a single here-document word. That is, it parses the input as if they were lines following a <<EOF redirection.
In practice, this is the same as parsing the input as if it were within double quotes, but without having to escape all double quote characters. Similarly, the here-document word parsed here cannot be ended by any delimiter other than reaching the end of the input.
func (*Parser) Incomplete ¶
Incomplete reports whether the parser is waiting to read more bytes because it needs to finish properly parsing a statement.
It is only safe to call while the parser is blocked on a read. For an example use case, see Parser.Interactive.
func (*Parser) Interactive ¶
Interactive implements what is necessary to parse statements in an interactive shell. The parser will call the given function under two circumstances outlined below.
If a line containing any number of statements is parsed, the function will be called with said statements.
If a line ending in an incomplete statement is parsed, the function will be called with any fully parsed statements, and Parser.Incomplete will return true.
One can imagine a simple interactive shell implementation as follows:
fmt.Fprintf(os.Stdout, "$ ") parser.Interactive(os.Stdin, func(stmts []*syntax.Stmt) bool { if parser.Incomplete() { fmt.Fprintf(os.Stdout, "> ") return true } run(stmts) fmt.Fprintf(os.Stdout, "$ ") return true }
If the callback function returns false, parsing is stopped and the function is not called again.
func (*Parser) Parse ¶
Parse reads and parses a shell program with an optional name. It returns the parsed program if no issues were encountered. Otherwise, an error is returned. Reads from r are buffered.
Parse can be called more than once, but not concurrently. That is, a Parser can be reused once it is done working.
func (*Parser) Stmts ¶
Stmts reads and parses statements one at a time, calling a function each time one is parsed. If the function returns false, parsing is stopped and the function is not called again.
func (*Parser) Words ¶
Words reads and parses words one at a time, calling a function each time one is parsed. If the function returns false, parsing is stopped and the function is not called again.
Newlines are skipped, meaning that multi-line input will work fine. If the parser encounters a token that isn't a word, such as a semicolon, an error will be returned.
Note that the lexer doesn't currently tokenize spaces, so it may need to read a non-space byte such as a newline or a letter before finishing the parsing of a word. This will be fixed in the future.
type ParserOption ¶
type ParserOption func(*Parser)
ParserOption is a function which can be passed to NewParser to alter its behavior. To apply option to existing Parser call it directly, for example KeepComments(true)(parser).
func KeepComments ¶
func KeepComments(enabled bool) ParserOption
KeepComments makes the parser parse comments and attach them to nodes, as opposed to discarding them.
func StopAt ¶
func StopAt(word string) ParserOption
StopAt configures the lexer to stop at an arbitrary word, treating it as if it were the end of the input. It can contain any characters except whitespace, and cannot be over four bytes in size.
This can be useful to embed shell code within another language, as one can use a special word to mark the delimiters between the two.
As a word, it will only apply when following whitespace or a separating token. For example, StopAt("$$") will act on the inputs "foo $$" and "foo;$$", but not on "foo '$$'".
The match is done by prefix, so the example above will also act on "foo $$bar".
func Variant ¶
func Variant(l LangVariant) ParserOption
Variant changes the shell language variant that the parser will accept.
The passed language variant must be one of the constant values defined in this package.
type Pos ¶
type Pos struct {
// contains filtered or unexported fields
}
Pos is a position within a shell source file.
func NewPos ¶ added in v3.5.0
NewPos creates a position with the given offset, line, and column.
Note that Pos uses a limited number of bits to store these numbers. If line or column overflow their allocated space, they are replaced with 0.
func (Pos) After ¶
After reports whether the position p is after p2. It is a more expressive version of p.Offset() > p2.Offset().
func (Pos) Col ¶
Col returns the column number of the position, starting at 1. It counts in bytes.
Col is protected against overflows; if an input line has too many columns, extra columns will have a column number of 0, rendered as "?" by Pos.String.
func (Pos) IsValid ¶
IsValid reports whether the position contains useful position information. Some positions returned via [Parse] may be invalid: for example, [Stmt.Semicolon] will only be valid if a statement contained a closing token such as ';'.
func (Pos) Line ¶
Line returns the line number of the position, starting at 1.
Line is protected against overflows; if an input has too many lines, extra lines will have a line number of 0, rendered as "?" by Pos.String.
type Printer ¶
type Printer struct {
// contains filtered or unexported fields
}
Printer holds the internal state of the printing mechanism of a program.
func NewPrinter ¶
func NewPrinter(opts ...PrinterOption) *Printer
NewPrinter allocates a new Printer and applies any number of options.
type PrinterOption ¶
type PrinterOption func(*Printer)
PrinterOption is a function which can be passed to NewPrinter to alter its behavior. To apply option to existing Printer call it directly, for example KeepPadding(true)(printer).
func BinaryNextLine ¶
func BinaryNextLine(enabled bool) PrinterOption
BinaryNextLine will make binary operators appear on the next line when a binary command, such as a pipe, spans multiple lines. A backslash will be used.
func FunctionNextLine ¶ added in v3.1.0
func FunctionNextLine(enabled bool) PrinterOption
FunctionNextLine will place a function's opening braces on the next line.
func Indent ¶
func Indent(spaces uint) PrinterOption
Indent sets the number of spaces used for indentation. If set to 0, tabs will be used instead.
func KeepPadding
deprecated
func KeepPadding(enabled bool) PrinterOption
KeepPadding will keep most nodes and tokens in the same column that they were in the original source. This allows the user to decide how to align and pad their code with spaces.
Note that this feature is best-effort and will only keep the alignment stable, so it may need some human help the first time it is run.
Deprecated: this formatting option is flawed and buggy, and often does not result in what the user wants when the code gets complex enough. The next major version, v4, will remove this feature entirely. See: https://github.com/mvdan/sh/issues/658
func Minify ¶
func Minify(enabled bool) PrinterOption
Minify will print programs in a way to save the most bytes possible. For example, indentation and comments are skipped, and extra whitespace is avoided when possible.
func SingleLine ¶ added in v3.3.0
func SingleLine(enabled bool) PrinterOption
SingleLine will attempt to print programs in one line. For example, lists of commands or nested blocks do not use newlines in this mode. Note that some newlines must still appear, such as those following comments or around here-documents.
Print's trailing newline when given a *File is not affected by this option.
func SpaceRedirects ¶
func SpaceRedirects(enabled bool) PrinterOption
SpaceRedirects will put a space after most redirection operators. The exceptions are '>&', '<&', '>(', and '<('.
func SwitchCaseIndent ¶
func SwitchCaseIndent(enabled bool) PrinterOption
SwitchCaseIndent will make switch cases be indented. As such, switch case bodies will be two levels deeper than the switch itself.
type ProcOperator ¶
type ProcOperator token
func (ProcOperator) String ¶
func (o ProcOperator) String() string
type ProcSubst ¶
type ProcSubst struct {
OpPos, Rparen Pos
Op ProcOperator
Stmts []*Stmt
Last []Comment
}
ProcSubst represents a Bash process substitution.
This node will only appear with LangBash.
type QuoteError ¶ added in v3.4.0
func (QuoteError) Error ¶ added in v3.4.0
func (e QuoteError) Error() string
type RedirOperator ¶
type RedirOperator token
func (RedirOperator) String ¶
func (o RedirOperator) String() string
type Redirect ¶
type Redirect struct { OpPos Pos Op RedirOperator N *Lit // fd>, or {varname}> in Bash Word *Word // >word Hdoc *Word // here-document body }
Redirect represents an input/output redirection.
type Slice ¶
type Slice struct {
Offset, Length ArithmExpr
}
Slice represents a character slicing expression inside a ParamExp.
This node will only appear in LangBash and LangMirBSDKorn.
type Stmt ¶
type Stmt struct { Comments []Comment Cmd Command Position Pos Semicolon Pos // position of ';', '&', or '|&', if any Negated bool // ! stmt Background bool // stmt & Coprocess bool // mksh's |& Redirs []*Redirect // stmt >a <b }
Stmt represents a statement, also known as a "complete command". It is compromised of a command and other components that may come before or after it.
type Subshell ¶
Subshell represents a series of commands that should be executed in a nested shell environment.
type TestClause ¶
TestClause represents a Bash extended test clause.
This node will only appear in LangBash and LangMirBSDKorn.
func (*TestClause) End ¶
func (t *TestClause) End() Pos
func (*TestClause) Pos ¶
func (t *TestClause) Pos() Pos
type TestExpr ¶
type TestExpr interface { Node // contains filtered or unexported methods }
TestExpr represents all nodes that form test expressions.
These are *BinaryTest, *UnaryTest, *ParenTest, and *Word.
type TimeClause ¶
TimeClause represents a Bash time clause. PosixFormat corresponds to the -p flag.
This node will only appear in LangBash and LangMirBSDKorn.
func (*TimeClause) End ¶
func (c *TimeClause) End() Pos
func (*TimeClause) Pos ¶
func (c *TimeClause) Pos() Pos
type UnAritOperator ¶
type UnAritOperator token
func (UnAritOperator) String ¶
func (o UnAritOperator) String() string
type UnTestOperator ¶
type UnTestOperator token
func (UnTestOperator) String ¶
func (o UnTestOperator) String() string
type UnaryArithm ¶
type UnaryArithm struct { OpPos Pos Op UnAritOperator Post bool X ArithmExpr }
UnaryArithm represents an unary arithmetic expression. The unary operator may come before or after the sub-expression.
If Op is Inc or Dec, X will be a word with a single *Lit whose value is a valid name.
func (*UnaryArithm) End ¶
func (u *UnaryArithm) End() Pos
func (*UnaryArithm) Pos ¶
func (u *UnaryArithm) Pos() Pos
type UnaryTest ¶
type UnaryTest struct { OpPos Pos Op UnTestOperator X TestExpr }
UnaryTest represents a unary test expression. The unary operator may come before or after the sub-expression.
type WhileClause ¶
type WhileClause struct {
WhilePos, DoPos, DonePos Pos
Until bool
Cond []*Stmt
CondLast []Comment
Do []*Stmt
DoLast []Comment
}
WhileClause represents a while or an until clause.
func (*WhileClause) End ¶
func (w *WhileClause) End() Pos
func (*WhileClause) Pos ¶
func (w *WhileClause) Pos() Pos
type Word ¶
type Word struct {
Parts []WordPart
}
Word represents a shell word, containing one or more word parts contiguous to each other. The word is delimited by word boundaries, such as spaces, newlines, semicolons, or parentheses.
Example ¶
package main import ( "fmt" "os" "strings" "mvdan.cc/sh/v3/syntax" ) func main() { r := strings.NewReader("echo foo${bar}'baz'") f, err := syntax.NewParser().Parse(r, "") if err != nil { return } printer := syntax.NewPrinter() args := f.Stmts[0].Cmd.(*syntax.CallExpr).Args for i, word := range args { fmt.Printf("Word number %d:\n", i) for _, part := range word.Parts { fmt.Printf("%-20T - ", part) printer.Print(os.Stdout, part) fmt.Println() } fmt.Println() } }
Output: Word number 0: *syntax.Lit - echo Word number 1: *syntax.Lit - foo *syntax.ParamExp - ${bar} *syntax.SglQuoted - 'baz'
func (*Word) Lit ¶
Lit returns the word as a literal value, if the word consists of *Lit nodes only. An empty string is returned otherwise. Words with multiple literals, which can appear in some edge cases, are handled properly.
For example, the word "foo" will return "foo", but the word "foo${bar}" will return "".