Hello world that handles errors

9

0

Write a program or function with the following functionality:

  • The program/function first attempts to write the string Hello, world! to the standard output stream. (No other forms of output are acceptable for this challenge, as the focus is very much on the I/O rather than the trivial behaviour of the program itself.) Depending on whether it succeeded:
    • If it succeeded in outputting Hello, world!, the program/function exits without any further behaviour.
    • If it failed to produce the correct output due to an error, the program/function attempts to write the string Error writing "Hello, world!" to the standard error stream. (For the purposes of this challenge, you don't need error handling for the error handling itself.)

Clarifications

  • Your program/function will be run with no input (unless it's written in a language which absolutely requires input to work, in which case it will be run with the simplest possible input).

  • When producing output, you may also produce a single trailing newline if you wish, but doing so is not mandatory.

  • The definition of "error writing to standard output" that your program implements must treat at least the following cases as errors:

    • Standard output being nonexistent (i.e. stdout is a closed filehandle, no file descriptor 1 exists, or however those cases translate to the language and OS you're using);
    • Standard output referring to a file on a disk that has no free space left;
    • Standard output connecting to another program, which has already closed its end of the connection.

    and must treat at least the following cases as success (i.e. not an error):

    • Standard output connects to a terminal, and Hello, world! is displayed onscreen.
    • Standard output connects to a file, and Hello, world! is written into the file.

    You can choose the details of what counts as an output error, so long as it's consistent with the above rules.

  • Your program/function should not crash upon encountering any of the error situations listed above. It's up to you what exit code you use.

  • Your program/function should not describe the nature of the encountered error on the standard error stream; it should just print the string specified above. Extraneous output on standard error (e.g. compiler warnings) is only legal if it's produced unconditionally, regardless of whether an error is encountered or not.

  • Your program only needs to work on one operating system (although it must be one on which the errors listed above make sense; I've tried to keep them general enough to work on most multitasking consumer operating systems, but weirder operating systems may well be excluded from this challenge). If your program is nonportable, list the assumptions it needs to run in the title of your submission.

  • This task may not be possible in every language (not every language allows a program to handle output errors in a custom way). You'll have to pick a language where it is possible.

  • Make sure that your program/function works! Don't just trust the documentation of library functions to do what they say they do. The error handling of simple output functions often turns out to be broken in practice, even if the functions claim to handle errors in theory.

Test cases

Here's a way to simulate each of the error conditions above using bash on Linux (you don't have to use Linux, but it's likely the easiest system to test this on):

your_program_here >&-           # nonexistent stdout
your_program_here > /dev/full   # out of disk space
mkfifo test  # note: change "test" to a filename that isn't in use
true < test &
your_program_here > test        # connecting to a program that doesn't want input
rm test      # clean up the FIFO we used earlier

The first two testcases are deterministic. The last isn't (it relies on a race condition); for testing purposes, I recommend adding a delay between the start of your program and the actual output to standard output, in order to ensure that the race condition is resolved in the way that exposes the error.

Victory condition

This is a challenge, so shorter is better. As (nearly) always, we're measuring the length of the program in bytes.

user62131

Posted 2017-04-12T04:19:00.260

Reputation:

1Do you know if there is a way to test this on Windows? I can test the first criterion, but not the part about the disk being full... – Stewie Griffin – 2017-04-12T06:04:19.403

To reduce the race condition, could you use sleep 1 < test; (sleep 2; your_program_here) > test? – Neil – 2017-04-12T07:43:39.640

Related – JayCe – 2018-09-06T17:12:47.583

Answers

6

Bash, 71 60 bytes

h=Hello,\ world!
(echo $h)2>&-||echo Error writing \"$h\">&2

Try it online!

How it works

After saving Hello, world! to the variable h, we do the following.

First, (echo $h)2>&- attempts to print Hello, world! to STDOUT. 2>&- is required to prevent displaying the error message echo: write error: Bad file descriptor in case the write fails. Since writing to a named pipe that doesn't accept input would kill the Bash program with signal 13 (SIGPIPE), we execute the command in a subshell ((...)), so only the subshell will get killed.

Finally, if printing to STDOUT failed, the subshell will exit with a non-zero status code (141 for SIGPIPE, 1 for a generic error), so echo Error writing \"$h\">&2 prints the desired message to STDERR.

Dennis

Posted 2017-04-12T04:19:00.260

Reputation: 196 637

2

Python 2, 65 bytes

h='Hello, world!'
try:print h
except:exit('Error writing "%s"'%h)

Two bytes could be saved by printing single quotes.

Try it online!

Dennis

Posted 2017-04-12T04:19:00.260

Reputation: 196 637

1

Zsh, 55 bytes

h=Hello,\ world!
2>&-<<<$h||<<<'Error writing "'$h\">&2

Unlike its cousin Bash, Zsh refuses to die because of a broken pipe.

Try it online!

Dennis

Posted 2017-04-12T04:19:00.260

Reputation: 196 637

1

Javascript, 79 76 bytes

try{(l=console).log(a="Hello, world!")}catch(e){l.error('Error writing '+a)}

Matthew Roh

Posted 2017-04-12T04:19:00.260

Reputation: 5 043

Note that the string you should output is 'Hello, world!', which is one byte longer than what you use. Also, I suppose assigning a inside the call to console.log would be shorter (1B) and removing the semicolon after l.log(a) saves another byte. – Luke – 2017-04-12T15:05:56.587

@Luke Thanks, That was quite a big mistake! – Matthew Roh – 2017-04-12T15:08:46.730

1try{(l=console).log(a="Hello, world!")}catch(e){l.error('Error writing '+a)} for 76 bytes. First, console is assigned to l, then "Hello, world!' is assigned to a, and then it's executed. – Luke – 2017-04-12T15:10:02.240

1

PowerShell, 80 Bytes

try{echo($h="Hello, World!") -ea 4}catch{$host.ui|% *rL* "Error writing ""$h"""}

explained:

try{
    #Attempt to 'echo' (write output) the string, and assign it to $h
    #Make sure the 'error action' is set to '4' to make it a terminating error.
    echo($h="Hello, World!") -ea 4
} catch { 
    #Use the "WriteErrorLine" function in $host.ui to stderr
    $host.ui|% *rL* "Error writing ""$h"""
}

haven't managed to actually try this when it errors, but it definitely ~should~ work.

colsw

Posted 2017-04-12T04:19:00.260

Reputation: 3 195

The error message should be written to STDERR. Writing it to STDOUT isn't possible if an error occurs during the first write attempt. – Dennis – 2017-04-12T16:27:49.913

@Dennis thanks for that, updated there, didn't read the question fully. – colsw – 2017-04-12T21:37:42.640

Afaict PowerShell only catches fatal exceptions, so you'd need Write-Host -ErrorAction Stop or something like that. Also, the throw produces additional debugging ingormation apart of the line it should print, which by the way should have a lowercase W and double quotes around the HW string. – Dennis – 2017-04-12T22:19:29.080

@Dennis the additional debug info has crushed me, answer updated there now. – colsw – 2017-04-13T15:44:55.037

1

C (gcc), 87 86 bytes

f(){signal(13,1);write(1-puts("Hello, world!"),"Error writing \"Hello, world!\"",29);}

Try it online!

Ungolfed

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void f(void)
{
    signal(SIGPIPE, SIG_IGN); // Works (and is required) on TIO. YMMV
    int fd = (puts("Hello, world!")) < 0 ? 2 : -13;
    write(fd, "Error writing \"Hello, world!\"", 29);
}

Dennis

Posted 2017-04-12T04:19:00.260

Reputation: 196 637

how do you know what return puts? here says just it is >=0 for to be all ok... – RosLuP – 2017-04-12T18:51:45.137

puts returns the number of bytes that have been written or -1 in case of an error, so it either returns 14 (Hello World plus newline) or -1. (That may be platform-specific, but this is how it behaves with glibc.) – Dennis – 2017-04-12T18:53:24.843

K&R2 says it return EOF [-1 praticly] if error; or a value not negative if all ok – RosLuP – 2017-04-12T19:08:25.567

1On PPCG, languages are defined by their implementation, and gcc/glibc behave like I said they do. – Dennis – 2017-04-12T19:15:07.483

i prefer the old book – RosLuP – 2017-04-12T19:16:32.830

0

Perl 5, 51 bytes

requires -M5.01, which is free

say$h="Hello, world!"or die"Error writing \"$h\"$/"

Tested in Strawberry Perl 5.24.0 by running the program as-is (printed to standard output) and by running

print f $h="Hello, world!"or die"Error writing \"$h\"$/"

(printed to standard error). I don't know how to test for other errors using Strawberry, but they should be handled the same….

msh210

Posted 2017-04-12T04:19:00.260

Reputation: 3 094

I don't know what to make of that page you linked to; can you explain, please? Also, note that the script needs to work on one OS only. And I'll add the comma; thanks. – msh210 – 2017-04-12T06:27:14.173

Output should display Hello, world! after === 1 === and nothing after the others. Debug should display nothing after === 1 === and Error writing "Hello, world!" after the others. I'm aware that your program doesn't have to work on TIO, but print f... shows the intended error messages while the original program does not. – Dennis – 2017-04-12T06:31:15.683

"Output" and "Debug" both display nothing as far as I see. I also don't know what the "Header" and "Footer" sections are supposed to be. And I'm wholly unfamiliar with TIO, but note that Strawberry Perl runs on MS Windows. – msh210 – 2017-04-12T06:37:00.923

Hat-tip to Dennis for the idea of storing the string in a variable (though I probably would've thought of it had I not seen it there).

– msh210 – 2017-04-12T06:38:49.370

Header and Footer run the program with the four conditions described in the challenge (normal output, closed file descriptor, full disk, and broken pipe). At least on Linux, the intended error message isn't printed, but Unable to flush stdout: Bad file descriptor and Unable to flush stdout: No space left on device are, while the last one is killed because of a broken pipe before producing any output. While I have no idea how to close file descriptors or create broken pipes on Windows, a full disk should be a bit cumbersome but ultimately straightforward. – Dennis – 2017-04-12T06:44:27.137

0

REXX, 111 106 bytes

signal on notready
a='Hello, world!'
_=lineout(,a)
exit
notready:_=lineout('stderr','Error writing "'a'"')

The program relies on there being a stream called 'stderr'. This will probably not be the case on IBM systems.

idrougge

Posted 2017-04-12T04:19:00.260

Reputation: 641

0

C, 77 bytes

f(a){a="Error writing \"Hello, world!\"";write(1,a+15,13)-13&&write(2,a,29);}

for call

main(){f(1); return 0;}

RosLuP

Posted 2017-04-12T04:19:00.260

Reputation: 3 036

On which platform did you test this? It doesn't work on Linux if a broken pipe is encountered. – Dennis – 2017-04-12T19:31:49.907

0

R, 91 bytes

s="Hello, world!"
tryCatch(cat(s),error=function(e)cat('Error writing "','"',file=2,sep=s))

Try it online!

I tried erroring it by running it with cat(s,file=12) instead of cat(s), and it prints the correct text to stderr. This is an invalid connection error otherwise.

JayCe

Posted 2017-04-12T04:19:00.260

Reputation: 2 655

Any idea how to test for other output errors? – JayCe – 2018-07-13T19:17:07.003