Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[9fans] What happens when you do an echo -n?

118 views
Skip to first unread message

G. David Butler

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
>From: "Russ Cox" <r...@plan9.bell-labs.com>

>the underlying problem is that "read of 0 bytes == eof"
>is a problem. it made sense in unix, when message delimiters
>were not preserved. i think that writes of 0 bytes should just be
>prohibited, but i'm sure there are those who disagree with me.

I thought so too, but after a few exchanges with Forsyth, I've
come to the conclusion that it is better to preserve delimiters
(including "yes, I really mean empty string"). This puts the
onus on applications (like echo) to make sure the receiver obeys
a special read 0 convention or not send it.

An alternative is to have read return something different on
close of the remote end (e.g. < 0 and errstr is "Remote Close").
This is possible but affects all read I/O code (not that it has
stopped me before ;).

Instead, I suggest that echo, and any other program that creates
conventional output, support the read 0 is EOF convention.

David Butler

from /sys/src/cmd/echo.c:

if((i = strlen(buf)) && write(1, buf, i) < 0)
fprint(2, "echo: write error: %r\n");

for...@caldo.demon.co.uk

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
>>An alternative is to have read return something different on
>>close of the remote end (e.g. < 0 and errstr is "Remote Close").

it does something like that already: when the writer has hung up,
the system gives a reader a few chances to go quietly,
and then it becomes more aggressive.

>>Instead, I suggest that echo, and any other program that creates
>>conventional output, support the read 0 is EOF convention.

funnily enough, on Inferno yesterday afternoon i really could have used
an echo -n producing a write of 0. (it could equally well
have been Plan 9.) not only Plan 9's streams preserves
delimiters: USB does as well. when testing a USB client driver,
i needed to send a 0 length write down the
USB status pipe as the status reply to a SETUP
transaction from a Plan 9 USB host.
i wouldn't insist on using echo to do it (see below), but
the example helps to emphasise that `conventional output'
isn't necessarily as `conventional' as one might initially think
in a system where the file interface is used throughout.
Plan 9 is a good example of diagonal thinking:
i think it `lateral', you think it `weird'!

i assume the problem with echo -n arose because a script used the obvious
echo -n $something
where it happened that something=(). it seems messy -- even rather perverse -- to
require a preceding predicate on every such use of echo!
my example above is relatively esoteric and could be
done another way. (as it happens, Inferno's echo hasn't got -n
so i had to do something else anyhow, but i found the coincidence
amusing, hence this note.)

looking at the code for echo i suspect the 0-write effect
was probably not intended but is a side effect of the way
the command is written. a more subtle problem is that the use of sprint
imposes a limit on the length of each argument string.

pres...@plan9.bell-labs.com

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
For my 2 cents, I changed telnetd to not give up till it sees
3 zero length reads in a row. It is pretty stupid and doesn't
save me against the case of "echo -n; echo -n; echo -n" but
it ended a lot of bellyaching. The same code exits in many
other places output from shell scripts can get you. I would
have preferred an errno or something that told me I was really
at EOF. I guess I could change EOF in the kernel to not only
return 0 but also stick something in the error string so that
errstr() could confirm whether it was really an EOF. Maybe
next system...

Russ Cox

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
The Plan 9 and the Echo
(With my deepest apologies to Doug McIlroy)

There dwelt in the land of New Jersey, the Unix, a fair maid whom
savants traveled far to admire. Her story has already been told.

As the Unix grew older, younger maidens began to imitate her ways,
sometimes even to the point of changing their names to sound like
her's, in attempts to attract suitors, especially the insufferably rich
ones. As more maidens began to act like the Unix, all but the most
exacting of suitors left the Unix for these pale stereotypes.

When the Unix had grown old, and her myriad suitors had left her
for younger maids whose imitation of the Unix was ``good enough'', the
Unix took under her wing the Plan 9, but a small child at the time.
The Unix endeavored to teach the lessons she had learned over her
lifetime, that the Plan 9 need not make the Unix's mistakes herself.
Under the Unix's tutelage, the Plan 9 grew to be a beautiful young
maid herself. She had far fewer suitors than the Unix had once
enjoyed, since she was competing with the Unix imitators. Even so, a
small but faithful group of savants admired her, dazzled by the grace
and civility the Unix had taught her. Still others were amazed by her
agility in performing exacting tasks seldom accomplished even by the
Unix.

One sad day, the Unix passed away, leaving the Plan 9, fully grown
at this point, to fend for herself. In deference to the venerable
Unix, Nature herself now answered to the Plan 9 more eagerly than to
other mortal beings. Once again humbler folk delighted in such a pure
echo coming from the wilderness that had so frustrated their own
attempts since the Unix's younger days. Taught well by her mother,
the Plan 9 obliged with perfect echoes of whatever she was asked.

Some years later, a sensitive young lad asked the Plan 9, `Echo
nothing at all.' Wise of the Unix's earlier troubles, the Plan 9
grinned slightly, kept her mouth closed, and did nothing.

`Whatever do you mean,' the youth demanded, `not looking toward
the wilderness? For perfect echoes cannot come back unless you are
facing the wilderness. Henceforth turn toward the wilderness, even
when you are echoing nothing at all.' And the Plan 9 obliged.

`But echoing nothing at all is not a proper echo,' pleaded an
impatient swain, `so do not turn toward the wilderness when echoing
nothing at all.' At this point the Plan 9 realized history repeating
itself, and although she did not want to offend either, she decided it
was better to offend the impatient youth rather than subject all her
suitors to yet another surfeit of notation.


James A. Robinson

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
> are you smoking something?

Aw c'mon. Somebody's got to shed some humor on the list. =)


ph...@plan9.bell-labs.com

unread,
Mar 5, 1999, 3:00:00 AM3/5/99
to
are you smoking something?

Russ Cox

unread,
Mar 6, 1999, 3:00:00 AM3/6/99
to
Okay, so maybe that reference was a tad obscure.

For the confused among us, see _The Unix Programming Environment_
by Kernighan & Pike (1984), pp. 77-79, "A digression on echo".

Russ

G. David Butler

unread,
Mar 6, 1999, 3:00:00 AM3/6/99
to
There has been a lot of discussion off the list about this,
but I think it needs to be brought back here.

It is hard to compare how Plan 9 handles I/O compared to
any UN*X because Plan 9 changed the way stream I/O works
by preserving delimiters. A write of 0 bytes is like a
procedure call with no arguments and that is entirely valid
and useful. The question is how this feature relates
to the notion of end-of-file.

Many of the discussions I've been involved with propose
adding an end-of-file indicator to a sucessful return,
i.e. here are 100 octets and end-of-file or here are 0
octets and not end-of-file. The only place, it seems,
to provide this extra flag is in the errstr. No one
likes this idea for the obvious reason that the errstr
is defined to only be valid on system call failures.

As usual, I would like to change the question.

I propose the following:

Read and write behave the same on files or streams.

End-of-file is temporal.

Read and write delimiters are session dependent.

If a read of a file returns 0, it only means that there is
no data *at this time*. If I were to try again, there may
be data now. If a read of a file returns -1, there will
never be more data. e.g. the file was removed. This says
nothing about time taken before the return, i.e. the read
could block or wait some amount of time.

If a write of a file returns 0, it only means that there is
no capacity *at this time*. If I were to try again, there may
be capacity now. If a write of a file returns -1, there will
never be capacity. e.g. the file was removed. This says
nothing about the time taken before the return, i.e. the write
could block or wait some amount of time.

Streams should have the same semantics except errors occur
when the other end of the stream is closed.

To be compatable with most current applications, the
convention would be that users want to know that there
is no more data *now*, so applications should quit on the
first such indication and call it end-of-file.

David Butler
g...@dbSystems.com

Russ Cox

unread,
Mar 6, 1999, 3:00:00 AM3/6/99
to
That's not true. A read of zero means there is no
data in this particular message. It does not mean
there is no data at all, any more than a read of 10 bytes
when I asked for a kilobyte means there is only ten bytes
now. Your interpretations of the return values would
make perfect sense if message boundaries were not preserved.

We need three distinctly different return values for these three:
. end of file
. error
. zero byte read

I can think of no good way to do this that would not be
a huge break with the consistency of the current system
call model.

It would be interesting to ponder whether the binary return
value of system calls is problematic in any others. Making
exceptions for read and write seems wrong.

Maybe we should switch from C to Limbo for all our system
programming and just make the system call return values tupled.

Russ

G. David Butler

unread,
Mar 6, 1999, 3:00:00 AM3/6/99
to
>That's not true. A read of zero means there is no
>data in this particular message. It does not mean

This problem occurs because:

>>Read and write behave the same on files or streams.

So, unless the system stores write delimiters in files
you must allow:

>>Read and write delimiters are session dependent.

The number and size of the messages can change at different
times on the same data source. In addition, any number of
0 length messages may be inserted and the result is the same.

The current stream implementation does just this. Upon
hangup it introduces a false 0 length message (not just
once, but three times) before returning an error.

>We need three distinctly different return values for these three:
> . end of file
> . error
> . zero byte read

I think the choices are:

. Files and streams have different semantics.
or
. Streams may introduce 0 length messages at will.
or
. Store delimiters in files.

I was working from the second one.

David

Bengt Kleberg

unread,
Mar 9, 1999, 3:00:00 AM3/9/99
to
In article <1999030619...@ns.dbSystems.com>, 9f...@cse.psu.edu wrote:

....deleted
> I propose the following:


>
> Read and write behave the same on files or streams.

What about the write()/read() size preservation currently seen on streams?
I really would like to keep it.

> End-of-file is temporal.


>
> Read and write delimiters are session dependent.
>

> If a read of a file returns 0, it only means that there is
> no data *at this time*. If I were to try again, there may
> be data now. If a read of a file returns -1, there will
> never be more data. e.g. the file was removed. This says
> nothing about time taken before the return, i.e. the read
> could block or wait some amount of time.
> If a write of a file returns 0, it only means that there is
> no capacity *at this time*. If I were to try again, there may
> be capacity now. If a write of a file returns -1, there will
> never be capacity. e.g. the file was removed. This says
> nothing about the time taken before the return, i.e. the write
> could block or wait some amount of time.

This question will only show how ignorant I am, but how else will I learn?

Under what circumstances will an open() file (since we want to do a
read()/write() it must already be open(), right?) be removed?
I thought it stayed in the filessytem until after the last close().
If you mean removed, but still open(), then it would seem excessive to
return -1 on a write().

> Streams should have the same semantics except errors occur
> when the other end of the stream is closed.
>
> To be compatable with most current applications, the
> convention would be that users want to know that there
> is no more data *now*, so applications should quit on the
> first such indication and call it end-of-file.

I think this is a very good idea. I think it would mean lots of change (to
recognise the 0 bytes as EOF), but surely it is worth it?

0 new messages