Read exit status from previous commands in a pipe - go

I am trying to detect if any command in a pipe sequence has failed before the execution of my application.
I am approaching a solution by reading $PIPESTATUS (bash) or $pipestatus (zsh) environment variables, which should contain an array with the exit codes of each command.
Sadly, I am not being able to retrieve these values using os.Getenv. This is a quick proof of concept:
package main
import (
"os"
"fmt"
)
func main() {
fmt.Printf("$PIPESTATUS is /%v/\n", os.Getenv("PIPESTATUS"))
fmt.Printf("$pipestatus is /%v/\n", os.Getenv("pipestatus"))
}
Which results in the following output:
$ true | false | go run read_pipestatus.go
$PIPESTATUS is //
$pipestatus is //
How can I make this approach work or alternatives ones?

Related

Won't print anything on terminal

I would like to know why when I execute the command go run example.go the won't print anything on terminal.
The code below works.
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
Will print Hello.
But when I would like to use the function fmt.Printf when I run the command to execute, appear very quickly the response but is deleted on terminal.
package main
import "fmt"
func main() {
var i int = 2
fmt.Printf("%v %T", i, i) // fmt.Print does not work to
}
You use fmt.Printf with a format that does not end with a newline, so your system dutifully prints out the output without a terminating newline.
Presumably your shell then overwrites the output by sending the cursor to the beginning of the line and printing something. To prevent this from happening, either have your program end its output with a newline, or update your shell's prompt to avoid printing over existing output.
(Side note: it's just Go, not Go Lang. This goes give some issues with searching, common among short-named languages like C and C++.)
When Printf is used, you need to put a \n at the end.
Your program will produce undefined: I.
By replacing I with i it should work https://play.golang.org/p/GxFh-SYePR3 and return 2 int.

return value of bash shell

I am trying to learn linux bash scripting. I have a script and I want to get the return value of this script and store it in a variable.
Any help is welcome.
Thank you in advance.
#!/bin/bash
HOST_NAME=$1
{ echo "105" ; sleep 5; } | telnet $HOST_NAME 9761;
To avoid confusion don't think/talk of it as a return value, think of it as what it is - an exit status.
In most programming languages you can capture the return value of a function by capturing whatever that function returns in a variable, e.g. with a C-like language :
int foo() {
printf("35\n");
return 7;
}
void main() {
int var;
var=foo();
}
the variable var in main() after calling foo() will hold the value 7 and 35 will have been printed to stdout. In shell however with similar-looking code:
foo() {
printf "35\n"
return 7
}
main() {
local var
var=$(foo)
}
var will have the value 35 and the unmentioned builtin variable $? which always holds the exit status of the last command run will have the value 7. If you wanted to duplicate the C behavior where 35 goes to stdout and var contains 7 then that'd be:
foo() {
printf "35\n"
return 7
}
main() {
local var
foo
var=$?
}
The fact that shell functions use the keyword return to report their exit status is confusing at first if you're used to other Algol-based languages like C but if they used exit then it'd terminate the whole process so they had to use something and it quickly becomes obvious what it really means.
So when taking about shell scripts and functions use the words "output" and "exit status", not "return" which some people in some contexts will assume means either of those 2 things, and that'll avoid all confusion.
Btw to avoid making things even more complicated I said above that $? is a variable but it's really the value of the "special parameter" ?. If you really want to understand the difference right now then see https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameters for a discussion of shell parameters which includes "special parameters" like ? and #, "positional parameters" like 1 and 2, and "variables" like HOME and var as used in my script above.
The $? shell variable stores the return value, however with the Linux telnet client this may not be as useful as you think. The client will return 1 if the remote host closes the connection (or any remote or network error occurs) and 0 if the local client side closes the connection successfully. The problem being that many services are written so that they send data and then close the TCP connection themselves without waiting for the client:
$ telnet time-b.timefreq.bldrdoc.gov 13
Trying 132.163.96.2...
Connected to time-b-b.nist.gov.
Escape character is '^]'.
58600 19-04-27 13:56:16 50 0 0 736.0 UTC(NIST) *
Connection closed by foreign host.
$ echo $?
1
Even if the client sends a command to the server to quit over the TCP stream, this still results in the remote side closing the connection, with the same result:
$ telnet mail.tardis.ed.ac.uk 25
Trying 193.62.81.50...
Connected to isolus.tardis.ed.ac.uk.
Escape character is '^]'.
220 isolus.tardis.ed.ac.uk ESMTP Postfix (Debian/GNU)
QUIT
221 2.0.0 Bye
Connection closed by foreign host.
$ echo $?
1
So, you're going to get a 1 no matter what really. If you want the return value of a remote script, this is easier with ssh like this:
$ ssh ssh.tardis.ed.ac.uk "exit 5"
THE TARDIS PROJECT | pubpubpubpubpubpubpubpubpub | Authorised access only
$ echo $?
5
As far as I know the only time telnet would return zero (i.e. success) is if you escape and quite the client, like this:
$ telnet www.google.com 80
Trying 216.58.210.36...
Connected to www.google.com.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
$ echo $?
0
Hope this helps.
It depends on what you mean by return value.
Processes (on UNIX-like systems) can return to a shell a single unsigned byte as an exit status, so that gives a value in the range 0-255. By convention zero means success and any other value indicates a failure.
(In lower-level languages, like C, you can get more than just this exit status, but that's not visible in bash).
The exit status of the last command run is stored in variable ?, so you can get its value from $?, however since many programs only return either 0 (it worked) or 1 (it didn't work), that's not much use.
Bash conditionals, like if and while test for success (exit code of 0) or failure (exit code of non-zero):
if some-command
then
echo "It worked"
else
echo "It didn't work"
fi
However ....
If you mean you want to get the output from the script, that's a different matter. You can capture it using:
var=$(some-command)
But wait, that only captures normal output, routed to a stream called stdout (file descriptor 1), it does not capture error messages which most programs write to a stream called stderr (file descriptor 2). To capture errors as well you need to redirect file descriptor 2 to file descriptor 1:
var=$(some-command 2>&1)
The output text is now in variable var.
The ? variable always stores the exit code of the previous command.
You can retrieve the value with $?.
Some context: http://tldp.org/LDP/abs/html/exit-status.html

Pipe string into STDIN of command in Golang

I want to replicate the following subprocess.run function call in Golang. What would be the correct way to do it?
subprocess.run(['kinit', username], input=password.encode())
So far, I've figured out how to use exec.Command to run external commands, but what's confusing me is passing strings as input to STDIN of that command. Python's subprocess.run has a handy input parameter that takes care of this, how can I achieve similar results in Golang?
I figured out how to do it.
package main
import "os/exec"
import "strings"
func main() {
cmd := exec.Command("kinit", username)
cmd.Stdin = strings.NewReader(password)
err := cmd.Run()
}
The Stdin attribute of the Command object is the STDIN pipe, we can set it to a strings.NewReader object containing the input string to achieve the same effect as of the Python snipped mentioned in the question.

Using cgo, why does C output not 'survive' piping when golang's does?

I'm experimenting with cgo to use C code from golang, but in my little hello-world test, I've ran into something I can't understand or find more information about.
I'm starting with a simple test similar to examples I've found
package main
import (
"fmt"
"unsafe"
)
/*
#import <stdio.h>
#import <stdlib.h>
*/
import "C"
func main() {
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
fmt.Printf("Printed from golang fmt\n")
}
This simple example just echoes strings to stdout from both golang (using fmt.Printf) and raw C (using C.puts) via the basic cgo binding.
When I run this directly in my terminal, I see both lines:
$ ./main
Printed from C.puts
Printed from golang fmt
When I run this but redirect output in any way – pipe to less, shell redirection to a file, etc – I only see golang's output:
./main | cat
Printed from golang fmt
What happens to the C.puts content when piping / redirecting?
Secondary questions: Is this a cgo quirk, or a c standard library quirk I'm not aware of? Is this behaviour documented? How would I go about debugging this on my own (e.g. is there a good/plausible way for me to 'inspect' what FD1 really is in each block?)
Update: If it's relevant, I'm using go version go1.6.2 darwin/amd64.
This is C behavior you're seeing.
Go does not buffer stdout, while in C it is usually buffered. When the C library detects stdout is a tty, it may use line buffering, so the additional \n inserted by puts will cause the output to be displayed.
You need to flush stdout to ensure you get all the output:
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf("Printed from golang fmt\n")
See also
Why does printf not flush after the call unless a newline is in the format string?
Is stdout line buffered, unbuffered or indeterminate by default?
The C library buffering is per line, so the first line can be left in the buffer before it is properly flushed (done at exit time in C programs). You can either try to flush stdout, or try adding a trailing \n in the first string. Does it work if you add the \n?

UNIX: is possible to bypass exit codes of unix?

I´m writing some Shell code and, for some logic of programming, I need to do some returns with negative numbers. This is:
if condition ; then
return -1
else
return -2
fi
Nevertheless, I get errors when using negative numbers, maybe because: Unix exit statuses are restricted to values 0-255, the range of an unsigned 8-bit integer. (from http://en.wikipedia.org/wiki/Exit_status#Unix)
Is there a way to bypass this? (I know that I could use another return numbers)
Thank you.
Sorry, but the Unix standard for shell scripting is exit 0 for success and exit non-zero for non-success.
The best you can do is capture return values and use them as you want, i.e.
myfunc () {
printf -- "$1" "\n"
if (( ${1:-0} == 0 )) ; then
return 0
else
return 1
fi
}
var=$(myfunc -2)
print var=${var}
#output
var=-2
Not what your overlords want to hear, but refer them to the Posix Standards.
Also FYI, $() is called command substitution. You will see people also implement command sub with paired back ticks 'cmd', but use the $( cmd ), unless you are using original borne shell coding on Sun/AIX or other heritage vendor platforms OR you are required to create code that is completely backwards (with the emphasis on backwards!) compatible.
$() is nice because you can nest them as much as you need, i.e.
$( cmd1 $( cmd2 $( cmd..n ) ) )
According the New Kornshell Programming Language (ISBN-10: 0131827006, 1995!) backticks are deprecated.
Note that either type of command substitution is creating a sub-shell to run the command, and then 'substitute in' the results into your command line.
I hope this helps ;-)

Resources