Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2d95834

Browse files
mahadzaryab1bitfield
andauthored
add WithEnv for setting command environment (#208)
Co-authored-by: John Arundel <john@bitfieldconsulting.com>
1 parent 0edd895 commit 2d95834

File tree

3 files changed

+93
-15
lines changed

3 files changed

+93
-15
lines changed

‎README.md‎

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -269,18 +269,31 @@ These are functions that create a pipe with a given contents:
269269

270270
| Source | Contents |
271271
| -------- | ------------- |
272-
| [`Args`](https://pkg.go.dev/github.com/bitfield/script#Args) | command-line arguments
273-
| [`Do`](https://pkg.go.dev/github.com/bitfield/script#Do) | HTTP response
274-
| [`Echo`](https://pkg.go.dev/github.com/bitfield/script#Echo) | a string
275-
| [`Exec`](https://pkg.go.dev/github.com/bitfield/script#Exec) | command output
276-
| [`File`](https://pkg.go.dev/github.com/bitfield/script#File) | file contents
277-
| [`FindFiles`](https://pkg.go.dev/github.com/bitfield/script#FindFiles) | recursive file listing
278-
| [`Get`](https://pkg.go.dev/github.com/bitfield/script#Get) | HTTP response
279-
| [`IfExists`](https://pkg.go.dev/github.com/bitfield/script#IfExists) | do something only if some file exists
280-
| [`ListFiles`](https://pkg.go.dev/github.com/bitfield/script#ListFiles) | file listing (including wildcards)
281-
| [`Post`](https://pkg.go.dev/github.com/bitfield/script#Post) | HTTP response
282-
| [`Slice`](https://pkg.go.dev/github.com/bitfield/script#Slice) | slice elements, one per line
283-
| [`Stdin`](https://pkg.go.dev/github.com/bitfield/script#Stdin) | standard input
272+
| [`Args`](https://pkg.go.dev/github.com/bitfield/script#Args) | command-line arguments |
273+
| [`Do`](https://pkg.go.dev/github.com/bitfield/script#Do) | HTTP response |
274+
| [`Echo`](https://pkg.go.dev/github.com/bitfield/script#Echo) | a string |
275+
| [`Exec`](https://pkg.go.dev/github.com/bitfield/script#Exec) | command output |
276+
| [`File`](https://pkg.go.dev/github.com/bitfield/script#File) | file contents |
277+
| [`FindFiles`](https://pkg.go.dev/github.com/bitfield/script#FindFiles) | recursive file listing |
278+
| [`Get`](https://pkg.go.dev/github.com/bitfield/script#Get) | HTTP response |
279+
| [`IfExists`](https://pkg.go.dev/github.com/bitfield/script#IfExists) | do something only if some file exists |
280+
| [`ListFiles`](https://pkg.go.dev/github.com/bitfield/script#ListFiles) | file listing (including wildcards) |
281+
| [`Post`](https://pkg.go.dev/github.com/bitfield/script#Post) | HTTP response |
282+
| [`Slice`](https://pkg.go.dev/github.com/bitfield/script#Slice) | slice elements, one per line |
283+
| [`Stdin`](https://pkg.go.dev/github.com/bitfield/script#Stdin) | standard input |
284+
285+
## Modifiers
286+
287+
These are methods on a pipe that change its configuration:
288+
289+
| Source | Modifies |
290+
| -------- | ------------- |
291+
| [`WithEnv`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithEnv) | environment for commands |
292+
| [`WithError`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithError) | pipe error status |
293+
| [`WithHTTPClient`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithHTTPClient) | client for HTTP requests |
294+
| [`WithReader`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithReader) | pipe source |
295+
| [`WithStderr`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStderr) | standard error output stream for command |
296+
| [`WithStdout`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStdout) | standard output stream for pipe |
284297

285298
## Filters
286299

@@ -340,7 +353,8 @@ Sinks are methods that return some data from a pipe, ending the pipeline and ext
340353

341354
| Version | New |
342355
| ----------- | ------- |
343-
| _next_ | [`DecodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.DecodeBase64) / [`EncodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.EncodeBase64) |
356+
| _next_ | [`WithEnv`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithEnv) |
357+
| | [`DecodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.DecodeBase64) / [`EncodeBase64`](https://pkg.go.dev/github.com/bitfield/script#Pipe.EncodeBase64) |
344358
| v0.22.0 | [`Tee`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Tee), [`WithStderr`](https://pkg.go.dev/github.com/bitfield/script#Pipe.WithStderr) |
345359
| v0.21.0 | HTTP support: [`Do`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Do), [`Get`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Get), [`Post`](https://pkg.go.dev/github.com/bitfield/script#Pipe.Post) |
346360
| v0.20.0 | [`JQ`](https://pkg.go.dev/github.com/bitfield/script#Pipe.JQ) |

‎script.go‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ type Pipe struct {
3232
stdout io.Writer
3333
httpClient *http.Client
3434

35-
// because pipe stages are concurrent, protect 'err' and 'stderr'
3635
mu *sync.Mutex
3736
err error
3837
stderr io.Writer
38+
env []string
3939
}
4040

4141
// Args creates a pipe containing the program's command-line arguments from
@@ -168,6 +168,7 @@ func NewPipe() *Pipe {
168168
mu: new(sync.Mutex),
169169
stdout: os.Stdout,
170170
httpClient: http.DefaultClient,
171+
env: nil,
171172
}
172173
}
173174

@@ -374,6 +375,12 @@ func (p *Pipe) EncodeBase64() *Pipe {
374375
})
375376
}
376377

378+
func (p *Pipe) environment() []string {
379+
p.mu.Lock()
380+
defer p.mu.Unlock()
381+
return p.env
382+
}
383+
377384
// Error returns any error present on the pipe, or nil otherwise.
378385
// Error is not a sink and does not wait until the pipe reaches
379386
// completion. To wait for completion before returning the error,
@@ -392,6 +399,11 @@ func (p *Pipe) Error() error {
392399
// error output). The effect of this is to filter the contents of the pipe
393400
// through the external command.
394401
//
402+
// # Environment
403+
//
404+
// The command inherits the current process's environment, optionally modified
405+
// by [Pipe.WithEnv].
406+
//
395407
// # Error handling
396408
//
397409
// If the command had a non-zero exit status, the pipe's error status will also
@@ -419,6 +431,10 @@ func (p *Pipe) Exec(cmdLine string) *Pipe {
419431
if pipeStderr != nil {
420432
cmd.Stderr = pipeStderr
421433
}
434+
pipeEnv := p.environment()
435+
if pipeEnv != nil {
436+
cmd.Env = pipeEnv
437+
}
422438
err = cmd.Start()
423439
if err != nil {
424440
fmt.Fprintln(cmd.Stderr, err)
@@ -430,7 +446,8 @@ func (p *Pipe) Exec(cmdLine string) *Pipe {
430446

431447
// ExecForEach renders cmdLine as a Go template for each line of input, running
432448
// the resulting command, and produces the combined output of all these
433-
// commands in sequence. See [Pipe.Exec] for error handling details.
449+
// commands in sequence. See [Pipe.Exec] for details on error handling and
450+
// environment variables.
434451
//
435452
// This is mostly useful for substituting data into commands using Go template
436453
// syntax. For example:
@@ -460,6 +477,9 @@ func (p *Pipe) ExecForEach(cmdLine string) *Pipe {
460477
if pipeStderr != nil {
461478
cmd.Stderr = pipeStderr
462479
}
480+
if p.env != nil {
481+
cmd.Env = p.env
482+
}
463483
err = cmd.Start()
464484
if err != nil {
465485
fmt.Fprintln(cmd.Stderr, err)
@@ -903,6 +923,16 @@ func (p *Pipe) Wait() error {
903923
return p.Error()
904924
}
905925

926+
// WithEnv sets the environment for subsequent [Pipe.Exec] and [Pipe.ExecForEach]
927+
// commands to the string slice env, using the same format as [os/exec.Cmd.Env].
928+
// An empty slice unsets all existing environment variables.
929+
func (p *Pipe) WithEnv(env []string) *Pipe {
930+
p.mu.Lock()
931+
defer p.mu.Unlock()
932+
p.env = env
933+
return p
934+
}
935+
906936
// WithError sets the error err on the pipe.
907937
func (p *Pipe) WithError(err error) *Pipe {
908938
p.SetError(err)

‎script_test.go‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,40 @@ func TestWithStdout_SetsSpecifiedWriterAsStdout(t *testing.T) {
17681768
}
17691769
}
17701770

1771+
func TestWithEnv_UnsetsAllEnvVarsGivenEmptySlice(t *testing.T) {
1772+
t.Parallel()
1773+
p := script.NewPipe().WithEnv([]string{"ENV1=test1"}).Exec("sh -c 'echo ENV1=$ENV1'")
1774+
want := "ENV1=test1\n"
1775+
got, err := p.String()
1776+
if err != nil {
1777+
t.Fatal(err)
1778+
}
1779+
if got != want {
1780+
t.Fatalf("want %q, got %q", want, got)
1781+
}
1782+
got, err = p.Echo("").WithEnv([]string{}).Exec("sh -c 'echo ENV1=$ENV1'").String()
1783+
if err != nil {
1784+
t.Fatal(err)
1785+
}
1786+
want = "ENV1=\n"
1787+
if got != want {
1788+
t.Errorf("want %q, got %q", want, got)
1789+
}
1790+
}
1791+
1792+
func TestWithEnv_SetsGivenVariablesForSubsequentExec(t *testing.T) {
1793+
t.Parallel()
1794+
env := []string{"ENV1=test1", "ENV2=test2"}
1795+
got, err := script.NewPipe().WithEnv(env).Exec("sh -c 'echo ENV1=$ENV1 ENV2=$ENV2'").String()
1796+
if err != nil {
1797+
t.Fatal(err)
1798+
}
1799+
want := "ENV1=test1 ENV2=test2\n"
1800+
if got != want {
1801+
t.Errorf("want %q, got %q", want, got)
1802+
}
1803+
}
1804+
17711805
func TestErrorReturnsErrorSetByPreviousPipeStage(t *testing.T) {
17721806
t.Parallel()
17731807
p := script.File("testdata/nonexistent.txt")

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /