diff --git a/README.md b/README.md index 4e5845a..6739c75 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ ## Example ### 在远程执行ssh命令 +提供3个方法: Run() Exec() Output() +1. Run() : 程序执行后,不再受执行者控制. 适用于启动服务端进程. +2. Exec() : 在控制台同步实时输出程序的执行结果. +3. Output() : 会等待程序执行完成后,输出执行结果,在需要对执行的结果进行操作时使用. ```go package main import ( @@ -32,7 +36,7 @@ func main() { } defer c.Close() - output, err := c.Exec("uptime") + output, err := c.Output("uptime") if err != nil { panic(err) } diff --git a/client.go b/client.go index bb26658..e63b1c5 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,7 @@ const DefaultTimeout = 30 * time.Second type Client struct { *Config SSHClient *ssh.Client + SSHSession *ssh.Session SFTPClient *sftp.Client } @@ -65,7 +66,13 @@ func New(cnf *Config) (client *Client, err error) { return client, errors.New("Failed to conn sftp: " + err.Error()) } - return &Client{SSHClient: sshClient, SFTPClient: sftpClient}, nil + session, err := sshClient.NewSession() + if err != nil { + return nil, err + } + // defer session.Close() + + return &Client{SSHClient: sshClient, SFTPClient: sftpClient, SSHSession: session}, nil } // NewClient 根据配置 @@ -149,4 +156,5 @@ func NewWithPrivateKey(Host, Port, User, Passphrase string) (client *Client, err func (c *Client) Close() { c.SFTPClient.Close() c.SSHClient.Close() + c.SSHSession.Close() } diff --git a/example/bash/Makefile b/example/bash/Makefile new file mode 100644 index 0000000..b150f08 --- /dev/null +++ b/example/bash/Makefile @@ -0,0 +1,124 @@ +PROJECT = transfer +VERSION = $(shell git describe) +BRANCH = $(shell git name-rev --name-only HEAD) +VERSION ="$(echo $TRAVIS_TAG)" +ARCNAME = $(PROJECT)-$(VERSION)-$(GOOS)-$(GOARCH) +ARTIFACTS_DIR=artifact + + +# ZIP="zip -9 -r" +# TAR="tar cvf" +# GZIP="gzip -9" +# GZIP_7Z="7za a -tgzip -mx9" +# ZIP_7Z="7za a -tzip -mx9" +ifndef ZIP + ifneq ($(shell which zip 2>/dev/null),) + ZIP := zip -9 + endif + ifneq ($(shell which 7z 2>/dev/null),) + ZIP := 7z a -tzip -mx=9 + endif + ifneq ($(shell which 7za 2>/dev/null),) + ZIP := 7za a -tzip -mx=9 + endif + ifndef ZIP + $(warning "No zip / 7z / 7za in ($(PATH))") + ZIP := : zip_not_found + endif +endif + +.PHONY: all release release-all +all: release-all + + +.PHONY: windows-dependencies +windows-dependencies: + @ go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo + +.PHONY: embed-assets +embed-assets: + @ GO111MODULE=off go get -u github.com/jteeuwen/go-bindata/... + @# go-bindata ./logos/microBadger_headert.png ./webpage.html + + + +release: + env CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(FLAGS) -ldflags='-s -w -X github.com/rinetd/transfer/version.Version=$(VERSION)' -o build/$(GOOS)-$(GOARCH)/$(PROJECT)$(EXT) + - tar czf build/$(ARCNAME).tar.gz -C build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT) + - tar czf build/$(ARCNAME).tar.gz -C build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT) +ifeq ($(OS),Windows_NT) + cd dist; \ + rm -f $(ARCNAME)-*-Win.zip; \ + $(ZIP) $(ARCNAME)-$(VERSTR)-Win.zip build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT); + $(ZIP) dist/$(ARCNAME)-$(VERSTR)-Win.zip -r0 images/*.png; +endif + +release-all: + # -@$(MAKE) release GOOS=darwin GOARCH=amd64 + # -@$(MAKE) release GOOS=linux GOARCH=386 + # -@$(MAKE) release GOOS=linux GOARCH=amd64 + -@$(MAKE) release GOOS=windows GOARCH=386 EXT=.exe + # -@$(MAKE) release GOOS=windows GOARCH=amd64 EXT=.exe + +.PHONY: windows +windows: windows-dependencies embed-assets + goversioninfo -icon=rc/icon.ico -manifest=rc/manifest.exe.manifest rc/versioninfo.json + @- rm binaries/*.exe + CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -x -ldflags="-s -w " -o binaries/deploy`date +%m%d`.exe + @# - rm binaries/deploy_windows_64bit.exe + @# - CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -H=windowsgui -linkmode internal" -o binaries/deploy_windows_64bit.exe + rm resource.syso + +platform: + # @$(MAKE) releaseGOOS=js GOARCH=wasm + # @$(MAKE) release GOOS=windows GOARCH=386 FLAGS='-ldflags="-H=windowsgui"' EXE=.exe + # @$(MAKE) release GOOS=windows GOARCH=amd64 FLAGS='-ldflags="-H=windowsgui"' EXE=.exe + # @$(MAKE) release GOOS=linux GOARCH=arm + # @$(MAKE) release GOOS=linux GOARCH=arm64 + # @$(MAKE) release GOOS=linux GOARCH=mips + # @$(MAKE) release GOOS=linux GOARCH=mips64 + # @$(MAKE) release GOOS=linux GOARCH=mips64le + # @$(MAKE) release GOOS=linux GOARCH=mipsle + # @$(MAKE) release GOOS=linux GOARCH=ppc64 + # @$(MAKE) release GOOS=linux GOARCH=ppc64le + # @$(MAKE) release GOOS=linux GOARCH=s390x + # @$(MAKE) release GOOS=android GOARCH=386 + # @$(MAKE) release GOOS=android GOARCH=amd64 + # @$(MAKE) release GOOS=android GOARCH=arm + # @$(MAKE) release GOOS=android GOARCH=arm64 + # @$(MAKE) release GOOS=darwin GOARCH=386 + # @$(MAKE) release GOOS=darwin GOARCH=arm + # @$(MAKE) release GOOS=darwin GOARCH=arm64 + # @$(MAKE) release GOOS=dragonfly GOARCH=amd64 + # @$(MAKE) release GOOS=freebsd GOARCH=386 + # @$(MAKE) release GOOS=freebsd GOARCH=amd64 + # @$(MAKE) release GOOS=freebsd GOARCH=arm + # @$(MAKE) release GOOS=nacl GOARCH=386 + # @$(MAKE) release GOOS=nacl GOARCH=amd64p32 + # @$(MAKE) release GOOS=nacl GOARCH=arm + # @$(MAKE) release GOOS=netbsd GOARCH=386 + # @$(MAKE) release GOOS=netbsd GOARCH=amd64 + # @$(MAKE) release GOOS=netbsd GOARCH=arm + # @$(MAKE) release GOOS=openbsd GOARCH=386 + # @$(MAKE) release GOOS=openbsd GOARCH=amd64 + # @$(MAKE) release GOOS=openbsd GOARCH=arm + # @$(MAKE) release GOOS=plan9 GOARCH=386 + # @$(MAKE) release GOOS=plan9 GOARCH=amd64 + # @$(MAKE) release GOOS=plan9 GOARCH=arm + # @$(MAKE) release GOOS=solaris GOARCH=amd64 + + +.PHONY: build +build: + go get ./... + +.PHONY: test +test: + go get -t ./... + go test -v ... + +build-image: + docker build -t rientd/transfer . + +clean: + rm -rf build dist diff --git a/example/get/main.go b/example/download/main.go similarity index 100% rename from example/get/main.go rename to example/download/main.go diff --git a/example/getdata/Makefile b/example/getdata/Makefile new file mode 100644 index 0000000..396bac3 --- /dev/null +++ b/example/getdata/Makefile @@ -0,0 +1,52 @@ +COMPILE_TIME = $(shell date +"%Y-%M-%d %H:%M:%S") +BUILD=`date +%FT%T%z` + +# Setup the -ldflags option for go build here, interpolate the variable values +LDFLAGS_f1=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f1" +LDFLAGS_f2=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD} -X main.Entry=f2" + + +name:=getdata +# make ver=release +ifeq ($(ver), debug) + CXXFLAGS = -c -g -Ddebug +else + CXXFLAGS = -c -O3 +endif + +.PHONY: all +all: windows + +.PHONY: windows-dependencies +windows-dependencies: + go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo + +.PHONY: embed-assets +embed-assets: + @# go get github.com/jteeuwen/go-bindata/... + @# go-bindata ./logos/$(name)_headert.png ./webpage.html + +.PHONY: linux +linux: *.go embed-assets + GOOS=linux GOARCH=amd64 go build -o output/$(name)_linux_64bit + GOOS=linux GOARCH=386 go build -o output/$(name)_linux_32bit + strip output/$(name)_linux_* + +.PHONY: windows +windows: *.go windows-dependencies embed-assets + goversioninfo -icon=rc/icon.ico -manifest=rc/manifest.exe.manifest rc/versioninfo.json + @- rm output/*.exe + CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w " -o output/$(name)_`date +%m-%d`.exe + @# - rm output/$(name)_windows_64bit.exe + @# - CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -H=windowsgui -linkmode internal" -o output/deploy_windows_64bit.exe + rm resource.syso + +.PHONY: osx +osx: *.go embed-assets + GOOS=darwin GOARCH=amd64 go build -o output/$(name)_osx_64bit + GOOS=darwin GOARCH=386 go build -o output/$(name)_osx_32bit + + +.PHONY: clean +clean: + rm -rf output/* \ No newline at end of file diff --git a/example/getdata/main.go b/example/getdata/main.go new file mode 100644 index 0000000..917f937 --- /dev/null +++ b/example/getdata/main.go @@ -0,0 +1,105 @@ +package getdata + +import ( + "fmt" + "log" + "os" + "path" + "time" + + "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" +) + +func connect(user, password, host string, port int) (*sftp.Client, error) { + var ( + auth []ssh.AuthMethod + addr string + clientConfig *ssh.ClientConfig + sshClient *ssh.Client + sftpClient *sftp.Client + err error + ) + // get auth method + auth = make([]ssh.AuthMethod, 0) + auth = append(auth, ssh.Password(password)) + + clientConfig = &ssh.ClientConfig{ + User: user, + Auth: auth, + Timeout: 30 * time.Second, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // connet to ssh + addr = fmt.Sprintf("%s:%d", host, port) + + if sshClient, err = ssh.Dial("tcp", addr, clientConfig); err != nil { + return nil, err + } + + // create sftp client + if sftpClient, err = sftp.NewClient(sshClient); err != nil { + return nil, err + } + + return sftpClient, nil +} + +var FORMAT = "2006-01-02" +var ( + err error + sftpClient *sftp.Client +) +var dbnames = []string{"tower", "mengyin", "pingyi", "shizhi", "tancheng", "yinan", "yishui", "feixian", "gaoxinqu", "hedong", "jingkaiqu", "junan", "luozhuang", "lanling", "lanshan", "lingang", "linshu"} + +func main() { + + // 这里换成实际的 SSH 连接的 用户名,密码,主机名或IP,SSH端口 + // sftpClient, err = connect("root", "sdlylshl871016", "111.235.181.127", 443) + sftpClient, err = connect("root", "HR2018!!", "15.14.12.150", 22) + if err != nil { + log.Println(err) + } + defer sftpClient.Close() + // 用来测试的远程文件路径 和 本地文件夹 + // fmt.Println(shizhi) + // var localDir = "." + date_dir := "db_" + time.Now().Format(FORMAT) + os.Mkdir(date_dir, 0755) + var lzkpbi = "/docker/backup/" + time.Now().Format(FORMAT) + "_lzkp_bi_inner.zip" + Down(lzkpbi, date_dir) + for _, n := range dbnames { + p := "/docker/backup/" + time.Now().Format(FORMAT) + "_" + n + "_inner.zip" + // fmt.Println(p) + + Down(p, date_dir) + } + // fmt.Scanln() +} + +func Down(src, dst string) { + fmt.Println(src, "数据正在复制中,请耐心等待...") + srcFile, err := sftpClient.Open(src) + if err != nil { + log.Println(err) + return + } + defer srcFile.Close() + + var localFileName = path.Base(src) + dstFile, err := os.Create(path.Join(dst, localFileName)) + if err != nil { + log.Println(err) + return + } + defer dstFile.Close() + + if _, err = srcFile.WriteTo(dstFile); err != nil { + log.Println(err) + return + } + + fmt.Println(src, "数据复制完成!") + +} diff --git a/example/getdata/rc/icon.ico b/example/getdata/rc/icon.ico new file mode 100644 index 0000000..94e9663 Binary files /dev/null and b/example/getdata/rc/icon.ico differ diff --git a/example/getdata/rc/manifest.exe.manifest b/example/getdata/rc/manifest.exe.manifest new file mode 100644 index 0000000..df4430f --- /dev/null +++ b/example/getdata/rc/manifest.exe.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/getdata/rc/versioninfo.json b/example/getdata/rc/versioninfo.json new file mode 100644 index 0000000..77423aa --- /dev/null +++ b/example/getdata/rc/versioninfo.json @@ -0,0 +1,43 @@ +{ + "_FixedFileInfo": { + "FileVersion": { + "Major": 18, + "Minor": 7, + "Patch": 16, + "Build": 0 + }, + "ProductVersion": { + "Major": 2018, + "Minor": 7, + "Patch": 16, + "Build": 0 + }, + "FileFlagsMask": "3f", + "FileFlags ": "00", + "FileOS": "040004", + "FileType": "01", + "FileSubType": "00" + }, + "StringFileInfo": { + "FileDescription": "履职考评系统升级", + "FileVersion": "2.1.0", + "ProductName": "履职考评系统", + "ProductVersion": "2018.7.1", + "LegalCopyright": "Copyright@pytool", + "OriginalFilename": "update.exe", + "Comments": " ", + "CompanyName": "Company", + "InternalName": "", + "LegalTrademarks": "", + "PrivateBuild": "", + "SpecialBuild": "" + }, + "VarFileInfo": { + "Translation": { + "LangID": "0409", + "CharsetID": "04B0" + } + }, + "IconPath": "deploy.ico", + "ManifestPath": "" +} \ No newline at end of file diff --git a/example/runetl/Makefile b/example/runetl/Makefile new file mode 100644 index 0000000..bc56960 --- /dev/null +++ b/example/runetl/Makefile @@ -0,0 +1,126 @@ +PROJECT = 数据同步 +VERSION = $(shell git describe) +BRANCH = $(shell git name-rev --name-only HEAD) +VERSION ="$(echo $TRAVIS_TAG)" +ARCNAME = $(PROJECT)-$(VERSION)-$(GOOS)-$(GOARCH) +ARTIFACTS_DIR=artifact + + +# ZIP="zip -9 -r" +# TAR="tar cvf" +# GZIP="gzip -9" +# GZIP_7Z="7za a -tgzip -mx9" +# ZIP_7Z="7za a -tzip -mx9" +ifndef ZIP + ifneq ($(shell which zip 2>/dev/null),) + ZIP := zip -9 + endif + ifneq ($(shell which 7z 2>/dev/null),) + ZIP := 7z a -tzip -mx=9 + endif + ifneq ($(shell which 7za 2>/dev/null),) + ZIP := 7za a -tzip -mx=9 + endif + ifndef ZIP + $(warning "No zip / 7z / 7za in ($(PATH))") + ZIP := : zip_not_found + endif +endif + +.PHONY: all release release-all +all: release-all + + +.PHONY: windows-dependencies +windows-dependencies: + @ go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo + +.PHONY: embed-assets +embed-assets: + @ GO111MODULE=off go get -u github.com/jteeuwen/go-bindata/... + @# go-bindata ./logos/microBadger_headert.png ./webpage.html + + + +release: + goversioninfo -icon=rc/icon.ico -manifest=rc/manifest.exe.manifest rc/versioninfo.json + env CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build $(FLAGS) -ldflags='-s -w -X github.com/rinetd/transfer/version.Version=$(VERSION)' -o build/$(GOOS)-$(GOARCH)/$(PROJECT)$(EXT) + rm resource.syso + - tar czf build/$(ARCNAME).tar.gz -C build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT) + - tar czf build/$(ARCNAME).tar.gz -C build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT) +ifeq ($(OS),Windows_NT) + cd dist; \ + rm -f $(ARCNAME)-*-Win.zip; \ + $(ZIP) $(ARCNAME)-$(VERSTR)-Win.zip build/$(GOOS)-$(GOARCH)/ $(PROJECT)$(EXT); + $(ZIP) dist/$(ARCNAME)-$(VERSTR)-Win.zip -r0 images/*.png; +endif + +release-all: + # -@$(MAKE) release GOOS=darwin GOARCH=amd64 + # -@$(MAKE) release GOOS=linux GOARCH=386 + # -@$(MAKE) release GOOS=linux GOARCH=amd64 + -@$(MAKE) release GOOS=windows GOARCH=386 EXT=.exe + # -@$(MAKE) release GOOS=windows GOARCH=amd64 EXT=.exe + +.PHONY: windows +windows: windows-dependencies embed-assets + goversioninfo -icon=rc/icon.ico -manifest=rc/manifest.exe.manifest rc/versioninfo.json + @- rm binaries/*.exe + CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -x -ldflags="-s -w " -o binaries/deploy`date +%m%d`.exe + @# - rm binaries/deploy_windows_64bit.exe + @# - CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w -H=windowsgui -linkmode internal" -o binaries/deploy_windows_64bit.exe + rm resource.syso + +platform: + # @$(MAKE) releaseGOOS=js GOARCH=wasm + # @$(MAKE) release GOOS=windows GOARCH=386 FLAGS='-ldflags="-H=windowsgui"' EXE=.exe + # @$(MAKE) release GOOS=windows GOARCH=amd64 FLAGS='-ldflags="-H=windowsgui"' EXE=.exe + # @$(MAKE) release GOOS=linux GOARCH=arm + # @$(MAKE) release GOOS=linux GOARCH=arm64 + # @$(MAKE) release GOOS=linux GOARCH=mips + # @$(MAKE) release GOOS=linux GOARCH=mips64 + # @$(MAKE) release GOOS=linux GOARCH=mips64le + # @$(MAKE) release GOOS=linux GOARCH=mipsle + # @$(MAKE) release GOOS=linux GOARCH=ppc64 + # @$(MAKE) release GOOS=linux GOARCH=ppc64le + # @$(MAKE) release GOOS=linux GOARCH=s390x + # @$(MAKE) release GOOS=android GOARCH=386 + # @$(MAKE) release GOOS=android GOARCH=amd64 + # @$(MAKE) release GOOS=android GOARCH=arm + # @$(MAKE) release GOOS=android GOARCH=arm64 + # @$(MAKE) release GOOS=darwin GOARCH=386 + # @$(MAKE) release GOOS=darwin GOARCH=arm + # @$(MAKE) release GOOS=darwin GOARCH=arm64 + # @$(MAKE) release GOOS=dragonfly GOARCH=amd64 + # @$(MAKE) release GOOS=freebsd GOARCH=386 + # @$(MAKE) release GOOS=freebsd GOARCH=amd64 + # @$(MAKE) release GOOS=freebsd GOARCH=arm + # @$(MAKE) release GOOS=nacl GOARCH=386 + # @$(MAKE) release GOOS=nacl GOARCH=amd64p32 + # @$(MAKE) release GOOS=nacl GOARCH=arm + # @$(MAKE) release GOOS=netbsd GOARCH=386 + # @$(MAKE) release GOOS=netbsd GOARCH=amd64 + # @$(MAKE) release GOOS=netbsd GOARCH=arm + # @$(MAKE) release GOOS=openbsd GOARCH=386 + # @$(MAKE) release GOOS=openbsd GOARCH=amd64 + # @$(MAKE) release GOOS=openbsd GOARCH=arm + # @$(MAKE) release GOOS=plan9 GOARCH=386 + # @$(MAKE) release GOOS=plan9 GOARCH=amd64 + # @$(MAKE) release GOOS=plan9 GOARCH=arm + # @$(MAKE) release GOOS=solaris GOARCH=amd64 + + +.PHONY: build +build: + go get ./... + +.PHONY: test +test: + go get -t ./... + go test -v ... + +build-image: + docker build -t rientd/transfer . + +clean: + rm -rf build dist diff --git a/example/runetl/main.go b/example/runetl/main.go new file mode 100644 index 0000000..37fb721 --- /dev/null +++ b/example/runetl/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + + "github.com/pytool/ssh" +) + +func main() { + config := ssh.Default.WithHost("192.168.5.157").WithPassword("HR2018!!") + // config.Host = "15.14.12.153" + client, err := ssh.New(config) + // client, err := ssh.NewClient("localhost", "22", "root", "ubuntu") + if err != nil { + // panic(err) + fmt.Println("连接失败,按Enter键退出!") + fmt.Scanln() + } + defer client.Close() + + err = client.Exec("sh /root/shetl/etl.sh") + if err != nil { + fmt.Println(err) + // panic(err) + fmt.Println("执行失败,按Enter键退出!") + fmt.Scanln() + } + + // fmt.Printf("Uptime: %s\n", output) + +} diff --git a/example/runetl/rc/icon.ico b/example/runetl/rc/icon.ico new file mode 100644 index 0000000..3ba71ab Binary files /dev/null and b/example/runetl/rc/icon.ico differ diff --git a/example/runetl/rc/manifest.exe.manifest b/example/runetl/rc/manifest.exe.manifest new file mode 100644 index 0000000..c7b0c6d --- /dev/null +++ b/example/runetl/rc/manifest.exe.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/runetl/rc/versioninfo.json b/example/runetl/rc/versioninfo.json new file mode 100644 index 0000000..93b3b9a --- /dev/null +++ b/example/runetl/rc/versioninfo.json @@ -0,0 +1,37 @@ +{ + "FixedFileInfo": { + "FileVersion": { + "Major": 1, + "Minor": 2, + "Patch": 0, + "Build": 1 + }, + "FileFlagsMask": "3f", + "FileFlags ": "00", + "FileOS": "040004", + "FileType": "01", + "FileSubType": "00" + }, + "StringFileInfo": { + "Comments": "执行etl", + "CompanyName": "pytool", + "FileVersion": "", + "FileDescription": "执行数据同步etl", + "ProductName": "履职考评数据同步", + "ProductVersion": "2018.12.11", + "LegalCopyright": "Copyright@pytool", + "OriginalFilename": "etl.exe", + "InternalName": "", + "LegalTrademarks": "", + "PrivateBuild": "", + "SpecialBuild": "" + }, + "VarFileInfo": { + "Translation": { + "LangID": "0409", + "CharsetID": "04B0" + } + }, + "IconPath": "icon.ico", + "ManifestPath": "" +} \ No newline at end of file diff --git a/cmd/main.go b/example/update/main.go similarity index 100% rename from cmd/main.go rename to example/update/main.go diff --git a/example/put/main.go b/example/upload/main.go similarity index 100% rename from example/put/main.go rename to example/upload/main.go diff --git a/sftp.go b/sftp.go index 80bd23a..ebb477f 100644 --- a/sftp.go +++ b/sftp.go @@ -61,13 +61,13 @@ func (c *Client) downloadFile(remoteFile, local string) error { rsum := c.Md5File(remoteFile) ioutil.WriteFile(localFile+".md5", []byte(rsum), 755) if FileExist(localFile) { - // 1. 检测远程是否存在 if rsum != "" { lsum, _ := Md5File(localFile) if lsum == rsum { - log.Println("sftp: 文件与本地一致,跳过上传!", localFile) + log.Println("sftp: 文件与本地一致,跳过下载!", localFile) return nil } + log.Println("sftp: 正在下载 ", localFile) } } } @@ -196,6 +196,7 @@ func (c *Client) UploadFile(localFile, remote string) error { log.Println("sftp: 文件与本地一致,跳过上传!", localFile) return nil } + log.Println("sftp: 正在上传 ", localFile) } } @@ -437,7 +438,7 @@ func (c *Client) Md5File(path string) string { if c.IsNotExist(path) { return "" } - b, err := c.Run("md5sum " + path) + b, err := c.Output("md5sum " + path) if err != nil { return "" } diff --git a/ssh.go b/ssh.go index 78d5644..f6770a2 100644 --- a/ssh.go +++ b/ssh.go @@ -1,30 +1,84 @@ package ssh import ( + "bufio" + "bytes" + "errors" "fmt" + "io" "path/filepath" ) -// Run Execute cmd on the remote host and return stderr and stdout -func (c *Client) Run(cmd string) ([]byte, error) { +// Run Execute cmd on the remote host for daemon service +func (c *Client) Run(cmd string) { session, err := c.SSHClient.NewSession() if err != nil { - return nil, err + return } defer session.Close() - return session.CombinedOutput(cmd) + session.Start(cmd) + if err != nil { + fmt.Printf("exec command:%v error:%v\n", cmd, err) + } + fmt.Printf("Waiting for command:%v to finish...\n", cmd) + //阻塞等待fork出的子进程执行的结果,和cmd.Start()配合使用[不等待回收资源,会导致fork出执行shell命令的子进程变为僵尸进程] + err = session.Wait() + if err != nil { + fmt.Printf(":Command finished with error: %v\n", err) + } + return } -//Exec Execute cmd on the remote host and return stderr and stdout -func (c *Client) Exec(cmd string) ([]byte, error) { +//Exec Execute cmd on the remote host and bind stderr and stdout +func (c *Client) Exec(cmd string) error { + session, err := c.SSHClient.NewSession() + if err != nil { + return err + } + defer session.Close() + // session.Run(cmd) + // return session.CombinedOutput(cmd) + stdout, err := session.StdoutPipe() + // stderr, err = session.StderrPipe() + if err != nil { + fmt.Println(err) + return err + } + + var b bytes.Buffer + session.Stderr = &b + session.Start(cmd) + //创建一个流来读取管道内内容,这里逻辑是通过一行一行的读取的 + reader := bufio.NewReader(stdout) + + //实时循环读取输出流中的一行内容 + for { + line, err2 := reader.ReadString('\n') + if err2 != nil || io.EOF == err2 { + break + } + print(line) + } + + //阻塞直到该命令执行完成,该命令必须是被Start方法开始执行的 + session.Wait() + if b.Len() > 0 { + return errors.New(b.String()) + } + return nil +} + +// Output Execute cmd on the remote host and return stderr and stdout +func (c *Client) Output(cmd string) ([]byte, error) { session, err := c.SSHClient.NewSession() if err != nil { return nil, err } defer session.Close() - - return session.CombinedOutput(cmd) + // session.Run(cmd) + // return session.CombinedOutput(cmd) + return session.Output(cmd) } // RunScript Executes a shell script file on the remote machine.