When true
exits, the read side of the pipe is closed, but yes
continues trying to write to the write side. This condition is called a "broken pipe", and it causes the kernel to send a SIGPIPE
signal to yes
. Since yes
does nothing special about this signal, it will be killed. If it ignored the signal, its write
call would fail with error code EPIPE
. Programs that do that have to be prepared to notice EPIPE
and stop writing, or they will go into an infinite loop.
If you do strace yes | true
1 you can see the kernel preparing for both possibilities:
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++
strace
is watching events via the debugger API, which first tells it about the system call returning with an error, and then about the signal. From yes
's perspective, though, the signal happens first. (Technically, the signal is delivered after the kernel returns control to user space, but before any more machine instructions are executed, so the write
"wrapper" function in the C library does not get a chance to set errno
and return to the application.)
1 Sadly, strace
is Linux-specific. Most modern Unixes have some command that does something similar, but it often has a different name, it probably doesn't decode syscall arguments as thoroughly, and sometimes it only works for root.
Keyword, reserved word, and builtin are all the "first word" of a Simple command. Could be placed in two groups: Keyword and Builtin. The two are mutually exclusive. A word (token) can be either a Keyword or a builtin, but not both.
Why the "first word"
From the POSIX definition of "Simple command"(emphasis mine):
A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.
2.- The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.
After that "first word" has been identified, and after is has been expanded (by an alias, for example) the final word is "the command", there could be only one command in each line. That command word could be a Built-in or a keyword.
Keyword
Yes, a keyword is a "reserved word". Load "man bash" and search for keyword. (or just execute this command: LESS=+/'keyword' man bash
.
The first hit on search say this:
keyword Shell reserved words.
It happens in the completion section, but is quite clear.
Reserved Words
In POSIX, there is this definition of "Reserved Words" and some description of what Reserved Words do.
But the bash manual has a better working definition.
Search for "RESERVED WORDS" (LESS=+/'RESERVED WORDS' man bash
) and find this:
RESERVED WORDS
Reserved words are words that have a special meaning to the shell.
The following words are recognized as reserved when unquoted and either the first word of a simple command or the third word of a case or for command:
! case do done elif else esac fi for function if
in select then until while { } time [[ ]]
Builtin
It is not defined in the bash manual, but it is quite simple:
It is a command that has been implemented inside the shell for UN-avoidable needs of the shell (cd, pwd, eval), or speed in general or to avoid conflicting interpretations of external utilities in some cases.
Time is a keyword.
why is time not a builtin but a keyword?
To allow the existence of a command as the second word.
It is similar as how an if ... then .... fi
allow the inclusion of commands (even compound commands) after the first key-word if
. Or while
or case
, etc.
Best Answer
In
bash
,time
is a reserved word, so the shell can parse it the own way and apply rules for it.Here is the code show how
bash
parse line start withtime
reserved word:You see,
time
can be followed by most othersbash
reserved words.In case of external command, the normal rule was applied,
{
was considered input of/usr/bin/time
.}
alone is invalid token, andbash
raise the error.In:
external
time
did not call the shell builtinecho
but the externalecho
command.A
strace
verifies that:Here external
time
lookup yourPATH
variable to find the command executable. That also explain in case of using a function, you got No such file or directory becasue there's no command namedmytest
in yourPATH
.