Skip to content

Commit

Permalink
Merge pull request #104 from xushiwei/log
Browse files Browse the repository at this point in the history
gsh: exec
  • Loading branch information
xushiwei committed Feb 18, 2024
2 parents 71d0801 + 9d6338e commit e1d042b
Show file tree
Hide file tree
Showing 3 changed files with 379 additions and 3 deletions.
70 changes: 67 additions & 3 deletions gsh/classfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ package gsh

import (
"bytes"
"errors"
"io"
"os"
"os/exec"
"strings"
)

const (
Expand All @@ -42,16 +44,78 @@ func (p *App) initApp() {
p.ferr = os.Stderr
}

// Gop_Exec executes a shell command.
func (p *App) Gop_Exec(name string, args ...string) error {
func (p *App) execWith(env []string, name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stdin = p.fin
cmd.Stdout = p.fout
cmd.Stderr = p.ferr
p.err = cmd.Run()
cmd.Env = env
p.err = Sys.Run(cmd)
return p.err
}

// Gop_Exec executes a shell command.
func (p *App) Gop_Exec(name string, args ...string) error {
return p.execWith(nil, name, args...)
}

// Exec executes a shell command with specified environs.
func (p *App) Exec__0(env map[string]string, name string, args ...string) error {
var cmdEnv []string
if env != nil {
cmdEnv = Setenv__0(Sys.Environ(), env)
}
return p.execWith(cmdEnv, name, args...)
}

// Exec executes a shell command.
func (p *App) Exec__1(name string, args ...string) error {
return p.execWith(nil, name, args...)
}

// Exec executes a shell command line with $env variables support.
// - exec "GOP_GOCMD=tinygo gop run ."
// - exec "ls -l $HOME"
func (p *App) Exec__2(cmdline string) error {
var iCmd = -1
var items = strings.Fields(cmdline)
var env []string
var initEnv = func() {
env = Sys.Environ()
if iCmd > 0 {
env = Setenv__2(env, items[:iCmd])
}
}
var mapping = func(name string) string {
if env == nil {
initEnv()
}
return Getenv(env, name)
}
for i, e := range items {
pos := strings.IndexAny(e, "=$")
if pos >= 0 && e[pos] == '=' {
if strings.IndexByte(e[pos+1:], '$') >= 0 {
items[i] = Sys.ExpandEnv(e)
}
continue
}
if iCmd < 0 {
iCmd = i
}
if pos >= 0 {
items[i] = os.Expand(e, mapping)
}
}
if iCmd < 0 {
return errors.New("exec: no command")
}
if env == nil && iCmd > 0 {
initEnv()
}
return p.execWith(env, items[iCmd], items[iCmd+1:]...)
}

// LastErr returns error of last command execution.
func (p *App) LastErr() error {
return p.err
Expand Down
111 changes: 111 additions & 0 deletions gsh/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
Copyright 2024 Qiniu Limited (qiniu.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gsh

import (
"os"
"os/exec"
"strings"
)

// -----------------------------------------------------------

// Setenv overwrites environments with specified env.
func Setenv__0(ret []string, env map[string]string) []string {
for k, v := range env {
nameEq := k + "="
ret = setenv(ret, nameEq+v, len(nameEq))
}
return ret
}

// Setenv overwrites environments with specified (name, val) pair.
func Setenv__1(ret []string, name, val string) []string {
nameEq := name + "="
return setenv(ret, nameEq+val, len(nameEq))
}

func setenv(ret []string, pair string, idxVal int) []string {
nameEq := pair[:idxVal]
for i, e := range ret {
if strings.HasPrefix(e, nameEq) {
ret[i] = pair
return ret
}
}
return append(ret, pair)
}

// Setenv overwrites environments with specified "name=val" pairs.
func Setenv__2(ret []string, env []string) []string {
for _, pair := range env {
pos := strings.IndexByte(pair, '=')
if pos > 0 {
ret = setenv(ret, pair, pos+1)
}
}
return ret
}

// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
// To distinguish between an empty value and an unset value, use LookupEnv.
func Getenv(env []string, name string) string {
nameEq := name + "="
for _, e := range env {
if strings.HasPrefix(e, nameEq) {
return e[len(nameEq):]
}
}
return ""
}

// -----------------------------------------------------------

type OS interface {
// Environ returns a copy of strings representing the environment,
// in the form "key=value".
Environ() []string

// ExpandEnv replaces ${var} or $var in the string according to the values
// of the current environment variables. References to undefined
// variables are replaced by the empty string.
ExpandEnv(s string) string

// Run starts the specified command and waits for it to complete.
Run(c *exec.Cmd) error
}

// -----------------------------------------------------------

type defaultOS struct{}

func (p defaultOS) Environ() []string {
return os.Environ()
}

func (p defaultOS) ExpandEnv(s string) string {
return os.ExpandEnv(s)
}

func (p defaultOS) Run(c *exec.Cmd) error {
return c.Run()
}

var Sys OS = defaultOS{}

// -----------------------------------------------------------
Loading

0 comments on commit e1d042b

Please sign in to comment.