20

I'm trying to fix a script that use echo, that is using the builtin command instead of the command, how can I prevent that?

I know I can do /bin/echo to force the usage of that, but I wouldn't like to hardcode the path (for portability).

I thought using something as:

$ECHO=`which echo`
$ECHO -e "text\nhere"

but which echo returns: "echo: shell built-in command".


I've ended up defining an echo function that uses env as @Kenster recommends. This way I don't need to modify the calls to echo in the script.

echo() {
  env echo $*
}

# the function is called before the built-in command.
echo -en "text\nhere"
eloyesp
  • 3,135
  • 1
  • 32
  • 47
  • 1
    'the builtin command instead of the command' WTF? – ForceBru Apr 09 '15 at 13:56
  • Why not jsut call it something else ? –  Apr 09 '15 at 13:59
  • @El_Hoy: Just curious, how is the internal `echo` worse than `/bin/echo` in your scenario? – Michael Jaros Apr 09 '15 at 14:02
  • 1
    There are certain commands (like `echo`) that you can fairly reliably hard-code the path for I believe. – Etan Reisner Apr 09 '15 at 14:05
  • You can get all the possible paths for a command with `which -a`. Hence, you can for example say: `for c in $(which echo -a); do echo "$c"; type "$c"; done` to see which one is not builtin. – fedorqui Apr 09 '15 at 14:50
  • 1
    If portability is your concern, use `printf`, not any form of `echo`. – chepner Apr 09 '15 at 14:53
  • 1
    @MichaelJaros the non-builtin `echo` support `-e` to add escape sequenses and `-n` to prevent the trailing new line. – eloyesp Apr 10 '15 at 13:58
  • @ForceBru I do not now how to name the binary echo command (`/bin/echo`) over the builtin echo in bash and I'm not native English speaker, should I rephrase it? – eloyesp Apr 10 '15 at 14:03
  • 4
    A deleted (and incorrect) answer suggests using the `command` builtin: `command echo ...`. This doesn't work because `command` bypasses shell functions, but doesn't bypass builtin commands. I mention this here for the benefit of those who can't read deleted answers and might try to use `command`. – Keith Thompson Apr 04 '18 at 22:52

2 Answers2

18

Use the env program. Env is a command which launches another program with a possibly modified environment. Because env is a program, it doesn't have access to shell builtins, aliases, and whatnot.

This command will run the echo program, searching for it in your command path:

$ env echo foo

You can verify this by using strace to monitor system calls while running echo vs env echo:

$ strace -f -e trace=process bash -c 'echo foo'
execve("/bin/bash", ["bash", "-c", "echo foo"], [/* 16 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f153fa14700) = 0
foo
exit_group(0)                           = ?

$ strace -f -e trace=process bash -c 'env echo foo'
execve("/bin/bash", ["bash", "-c", "env echo foo"], [/* 16 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f474eb2e700) = 0
execve("/usr/bin/env", ["env", "echo", "foo"], [/* 16 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f60cad15700) = 0
execve("/usr/local/sbin/echo", ["echo", "foo"], [/* 16 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/bin/echo", ["echo", "foo"], [/* 16 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/echo", ["echo", "foo"], [/* 16 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/bin/echo", ["echo", "foo"], [/* 16 vars */]) = -1 ENOENT (No such file or directory)
execve("/sbin/echo", ["echo", "foo"], [/* 16 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/echo", ["echo", "foo"], [/* 16 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7f0146906700) = 0
foo
exit_group(0)                           = ?
Kenster
  • 23,465
  • 21
  • 80
  • 106
  • 1
    I've ended up using your solution, but using a function to simplify implementation: `echo() { env echo $* }` worked great! – eloyesp Apr 10 '15 at 17:49
13

You can disable the builtin echo:

enable -n echo

Now simply doing echo anything will run the external version. It only affects the current script process, so you can safely do it in your scripts.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 1
    Cool, I didn't know that. Is `enable` a built-in command? Does it works in `sh`? – eloyesp Apr 14 '15 at 22:34
  • 6
    `enable` is a builtin in bash (unless you `enable -n enable`, of course!). It does not work in POSIX `sh` because POSIX has no real notion of builtin vs external. – that other guy Apr 14 '15 at 22:50
  • 1
    The first thing that came to mind when I was `builtin` and `enable` together was `enable -n enable` :D. – Samveen Jan 05 '18 at 09:00