On Mon, 16 Jan 2017 13:35:07 -0800 (PST)
Deepak Jain <
deep...@gmail.com> wrote:
> util.ExecuteCommandWithOuput(exec.Command("cp", "-r", "./*.json",
> artifact. dir))
>
> func ExecuteCommandWithOuput(cmd *exec.Cmd) {
> output, err := cmd.Output()
> if err != nil {
> log.Print("Error executing ", cmd.Args, err)
> }
> fmt.Print(string(output))
> }
>
> Output
>
> 2017/01/16 13:26:35 Error executing [cp -r ./*.json myartifact] exit
> status 1
>
> Questions
> 1. How do i get details of complete error message on failure of cp
> command ? I did have err != nill and Print err
The os/exec.Command() function constructs an value of type os/exec.Cmd
which is a struct type describing the details of an external process to
execute. Processes on Unix-like OSes (and Windows) have notions of the
standard streams [2]. The "stderr" is where (well-written) programs
write their error messages when they encounter a problem preventing
them from completing their intended task. Values of the os/exec.Cmd
have three fields, Stdin, Stdout and Stderr, which control the standard
streams of the process to be executed, and as it happens by default
these fields are initialized to nil which means the spawned process
will have its standard streams connected to the so-called "null device"
(/dev/null on Unices and NUL on Windows), and so its error output will
go to nowhere.
There are different approaches at handling error output of the external
processes.
1. You can connect its Stderr to the Stderr of your running process
which is available as os.Stderr, like this:
cmd := exec.Command(`cp ...`)
cmd.Stderr = os.Stderr
and then whatever that `cp` process outputs will end up on the
standard error stream of your host process.
2. You can "collect" that output and do something with it.
One approach is to exploit the fact pointers to values of
type bytes.Buffer implement the io.Writer interface, so you can do
var errmsg bytes.Buffer
cmd := exec.Command(`cp ...`)
cmd.Stderr = &errmsg
err := cmd.Run()
if err != nil || errmsg.Len() > 0 {
log.Fatalf("Failed to copy: %s\n", errmsg.String())
}
3. The CombinedOutput() method of the os/exec.Cmd type provides a way
to intelligently collect the outputs--to both the standard output
stream and the standard error stream--of the process being executed
and present them to you as a combined chunk of bytes.
Such collection is done in an intelligent way -- by keeping only
a manageable "header" and "trailer" parts of the output to deal with
cases where a process spews enormous amounts of data, and you don't
intend to process it all, and would be okay with just decorating
those collected bits with some error message prefix and so on.
> 2. Does exec.Command not support copy of files and recursive copy of
> directories ?
exec.Command() has nothing to do with copying anything.
As its name suggests, it's a tool to execute external processes,
and these processes can do anything. The `cp` program is for
copying/linking filesystem entities but you could execute FireFox or
Apache just as easily.
> 3. Any suggestions how i implement copy of files and recursive copy
> of directories ?
[...]
Answered over there at SO [1].
Here's my answer copied from there:
---------------->8----------------
The problem
To explain: the so-called "wildcards" are expanded by the shell in
which you typically execute command-line commands. That is, when you
invoke `cp -r ./*.json dir/`, the shell kicks in, expands *.json by
itself—producing a list of names of the files matching that pattern and
located in the current directory and pass the cp command a list of such
names.
So if you have, say, 10 matching files, the actuall call to cp will end
up looking like
cp -r file1.json file2.json ... dir/
When you pass call `cp ...` directly—without the shell kicking in and
expanding that *.json "fileglob" for you, the cp command receives the
name of a file "*.json" verbatim and attempts to open it. Since the
file named exactly "*.json" supposedly does not exist, `cp` fails and
exits.
The solutions
The first (admittedly lame) solution is to pass a call to cp "through"
a shell. That is, turn your call to cp into a shell script and pass it
to a shell.
The simplest way to do this is to use something like
exec.Command(`/bin/sh -c 'cp -r ./*.json manifest'`)
This will call a shell `/bin/sh` and pass it the script to execute via
its -c command-line option.
Another solution is to roll copying yourself using the standard Go
library: the functions of the path/filepath package provide support for
both expanding fileglobs as the shell would do it and iterating over
the entries of a given directory. Using either of these approaches you
can build the list of files to copy and/or iterate over them.
Then you can use the function os.OpenFile() to open the source and
destination files, and io.Copy() to copy the contents between them.
---------------->8----------------
Please, if you ask your question using several venues, make sure to
cross-reference between them.
1.
http://stackoverflow.com/a/41690953/720999
2.
https://en.wikipedia.org/wiki/Standard_streams