Assuming env
is in your path:
env kill -p http
env
runs the executable file named by its first argument in a (possibly) modified environment; as such, it does not know about or work with shell built-in commands.
This produces some shell job control cruft, but doesn't rely on an external command:
exec kill -p bash &
exec
requires an executable to replace the current shell, so doesn't use any built-ins. The job is run in the background so that you replace the forked background shell, not your current shell.
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 with time
reserved word:
static int
time_command_acceptable ()
{
#if defined (COMMAND_TIMING)
int i;
if (posixly_correct && shell_compatibility_level > 41)
{
/* Quick check of the rest of the line to find the next token. If it
begins with a `-', Posix says to not return `time' as the token.
This was interp 267. */
i = shell_input_line_index;
while (i < shell_input_line_len && (shell_input_line[i] == ' ' || shell_input_line[i] == '\t'))
i++;
if (shell_input_line[i] == '-')
return 0;
}
switch (last_read_token)
{
case 0:
case ';':
case '\n':
case AND_AND:
case OR_OR:
case '&':
case WHILE:
case DO:
case UNTIL:
case IF:
case THEN:
case ELIF:
case ELSE:
case '{': /* } */
case '(': /* )( */
case ')': /* only valid in case statement */
case BANG: /* ! time pipeline */
case TIME: /* time time pipeline */
case TIMEOPT: /* time -p time pipeline */
case TIMEIGN: /* time -p -- ... */
return 1;
default:
return 0;
}
#else
return 0;
#endif /* COMMAND_TIMING */
}
You see, time
can be followed by most others bash
reserved words.
In case of external command, the normal rule was applied, {
was considered input of /usr/bin/time
. }
alone is invalid token, and bash
raise the error.
In:
/usr/bin/time echo hello
external time
did not call the shell builtin echo
but the external echo
command.
A strace
verifies that:
$ strace -fe execve /usr/bin/time echo 1
execve("/usr/bin/time", ["/usr/bin/time", "echo", "1"], [/* 64 vars */]) = 0
Process 25161 attached
....
[pid 25161] execve("/usr/bin/echo", ["echo", "1"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
[pid 25161] execve("/bin/echo", ["echo", "1"], [/* 64 vars */]) = 0
1
[pid 25161] +++ exited with 0 +++
....
Here external time
lookup your PATH
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 named mytest
in your PATH
.
Best Answer
The
-c
option allows programs to run commands. It’s a lot easier to fork and dothan it is to fork, create a pipe, fork again, call
execl
in each child, callwait
, check the exit status, fork again, callclose(1)
, open the file, ensure that it is open on file descriptor 1, and do anotherexecl
. I believe that this was the reason why the option was created in the first place.The
system()
library function runs a command by the above method.find … -exec
orxargs
. But you already knew that; it was part of the answer to your question, How to specify a compound command as an argument to another command?It can come in handy if you’re running an interactive shell other than bash. Conversely, if you are running bash, you can use this syntax
to run one command in another shell, as all of those shells also recognize the
-c
option. Of course you could achieve the same result withI used
ash$
, etc., to illustrate the prompts from the different shells; you probably wouldn’t actually get those.It can come in handy if you want to run one command in a “fresh” bash shell; for example,
or
The above is a misleading over-simplification. When bash is run with
-c
, it is considered a non-interactive shell, and it does not read~/.bashrc
, unless is-i
specified. So,You could use
-ci
,-i -c
or-ic
instead of-c -i
.This probably applies to some extent to the other shells mentioned in paragraph 3, so the long form (i.e., the second form, which is actually exactly the same amount of typing) might be safer, especially if you have initialization/configuration files set up for those shells.
As Wildcard explained, since you’re running a new process tree (a new shell process and, potentially, its child process(es)), changes to the environment made in the subshell cannot affect the parent shell (current directory, values of environment variables, function definitions, etc.) Therefore, it’s hard to imagine a shell builtin command that would be useful when run by
sh -c
.fg
,bg
, andjobs
cannot affect or access background jobs started by the parent shell, nor canwait
wait for them.sh -c "exec some_program"
is essentially equivalent to just runningsome_program
the normal way, directly from the interactive shell.sh -c exit
is a big waste of time.ulimit
andumask
could change the system settings for the child process, and then exit without exercising them.Just about the only builtin command that would be functional in a
sh -c
context iskill
. Of course, the commands that only produce output (echo
,printf
,pwd
andtype
) are unaffected, and, if you write a file, that will persist.ulimit
(orshopt
). And since you can put an arbitrarily complex command after-c
— up to the complexity of a full-blown shell script — you might have occasion to use any of the repertoire of builtins; e.g.,source
,read
,export
,times
,set
andunset
, etc.