19

I use Bash as my interactive shell and I was wondering if there was an easy way to get Bash to run a system command instead of a shell builtin command in the case where they both share the same name.

For example, use the system kill (from util-linux) to print the process id (pid) of the named process(es) instead of sending a signal:

$ /bin/kill -p httpd
2617
...

Without specifying the full path of the system command, the Bash builtin is used instead of the system command. The kill builtin doesn’t have the -p option so the command fails:

$ kill -p httpd
bash: kill: p: invalid signal specification

I tried the answers listed in Make bash use external `time` command rather than shell built-in but most of them only work because time is actually a shell keyword – not a shell builtin.

Other than temporarily disabling the Bash builtin with enable -n kill, the best solution I’ve seen so far is to use:

$(which kill) -p httpd

Are there other easier (involve less typing) ways to execute an external command instead of a shell builtin?

Note that kill is just an example and I’d like a generalised solution similar to the way that prefixing with the command builtin prevents functions which have the same name as an external command from being run. In most cases, I usually prefer to use the builtin version as it saves forking a new process and some times the builtin has features that the external command doesn’t.

Anthony Geoghegan
  • 12,605
  • 7
  • 59
  • 62
  • Enclosing `which kill` in backticks (can't put them in comments) is *slightly* shorter. – abligh Nov 12 '15 at 14:15
  • @abligh Good point. I've spent years training myself to use the "new"er syntax for command substitution.:) – Anthony Geoghegan Nov 12 '15 at 14:37
  • @abligh: FYI, you can put \`backticks\` into comments by preceding them with backslashes (\\).  But there are reasons to stick with `$(…)` — see [this](http://unix.stackexchange.com/q/5778), [this](http://unix.stackexchange.com/q/147838), and [this](http://unix.stackexchange.com/q/104119). – G-Man Says 'Reinstate Monica' Apr 19 '16 at 20:56

6 Answers6

23

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.

chepner
  • 7,341
  • 1
  • 26
  • 27
  • 2
    `env` is clearly the right answer IMHO. – abligh Nov 12 '15 at 14:38
  • @abligh: You're right in comment in my deleted answer. `command -p cmd` calling external command in `zsh`, not `bash`. – cuonglm Nov 12 '15 at 15:39
  • 8
    `(exec kill -p http)` causes the job to replace a sub-shell instead of your current shell and you don't have to deal with the job control cruft. – Michael Hoffman Nov 12 '15 at 23:44
  • 1
    @AnthonyGeoghegan Indeed, but `env` also doesn't know about shell builtins, since it's not a shell. You'd get the same effect with `nice` or `xargs` or any other program like that. – user253751 Nov 13 '15 at 00:05
1

The simplest way to do what you want might be to put the line

alias kill="/bin/kill"

into your ~/.bashrc file. After that, each new login/invocation of bash will interpret "kill" as /bin/kill.

cuonglm
  • 150,973
  • 38
  • 327
  • 406
Benjamin Staton
  • 501
  • 2
  • 9
  • I had already thought of an alias and that was one of the solutions listed in the question I linked to but I’m hoping for a more generalised solution (similar to the `command` solution) rather than create a separate alias for each “shadowed” command. I’ve now edited my question to make this clearer and more explicit. In most cases, I usually prefer to use the builtin version as processes can be specified by job IDs. Thanks, anyway. – Anthony Geoghegan Nov 12 '15 at 13:26
1

If you know a solution that requires some typing and you want a solution that requries less typing, build it:

runFile() { local cmd="$1"; shift; cmd="$(which "$cmd")" && "$cmd" "$@"; }

Abbreviating stuff that normally takes some effort is what computers excel at.

Petr Skocik
  • 28,176
  • 14
  • 81
  • 141
  • 1
    That's a good general point so I'll upvote your answer. Over the past few years, I've built up a collection of aliases, functions and scripts for the systems that I regularly use. However, I prefer to use non-customised solutions where possible as I sometimes have to work on fresh installs or servers that I didn't set up. Thanks. – Anthony Geoghegan Nov 12 '15 at 20:34
1

In this very specific case, the command pgrep is an exact match for the need.

In a general sense, a function works. From "file command":

fcmd(){ local a=$1; shift; $(which "$a") "$@"; }

call as

fcmd kill -p httpd

But if you need less typing, there is no shorter way than a good alias.

From the concept "list pid" (lp):

alias lp='/bin/kill -p'

then, just type:

lp httpd
0

(In zsh) You can prefix any command name with an = sign to get the system version instead of a builtin. This is also a handy way to dodge any aliases that mess up a specific scenario.

$ =kill -p httpd
Caleb
  • 69,278
  • 18
  • 196
  • 226
  • 1
    I tried that with Bash version 4.3.30 and it didn't work. I've never seen such syntax before; can you add a link to where this feature is documented? I'd normally prefix an alias with a backslash to run the non-aliased version of a command. – Anthony Geoghegan Nov 12 '15 at 20:28
  • @Anthony maybe this is a `zsh` only thing. I use it regularly but I'll check on `bash` from a computer tomorrow. – Caleb Nov 12 '15 at 20:31
  • I suspected it might be some shell that I'm not familiar with (I’m only familiar with bash, dash and csh). There was another answer (since deleted) that only worked for `zsh`. While I tagged this question with `bash` and used it in the title, you should keep this answer as a zsh user would still find it useful. – Anthony Geoghegan Nov 12 '15 at 20:41
-1

You could send a bugreport against your kill manpage and ask why this includes non-standard options that are taken from pkill and use pkill whenever you like to get the features from pkill.

If you call:

pkill httpd

you avoid the problems you describe.

schily
  • 18,806
  • 5
  • 38
  • 60