2

Since it overwrites my history when used in multiple terminals, I want to turn off the functionality fc -W. Unfortunately I have a habit of typing it often.

I think it's not possible to make an alias, since there is a whitespace in fc -W.

So I tried making a function, something like this:

# Make sure to never invoke fc -W
fc(){
    for x; do
        if [[ "${x}" == -W ]]; then
            echo "I'm sorry Dave. I'm afraid I can't do that."
            return
        fi
    done
    fc "${@}"
}

However, now the call fc "${@}" calls itself, and I get infinite recursion. Typically I would avoid this by using e.g. /usr/bin/fc, instead of fc, however:

$ type fc
> fc is a shell builtin

How can I avoid the infinite recursion in this case? Or is there a better way to disable one flag of a command?

3

Use builtin:

builtin fc "$@"

This will ensure that the built-in fc command is called.


Style: The diagnostic message should go to the standard error stream and the function should return a non-zero status when failing:

echo 'Sorry, can not do that' >&2
return 1
  • If I type command fc in zsh, I get: zsh: command not found: fc. – pfnuesel Mar 2 '18 at 19:07
  • @pfnuesel Ah, it's zsh. Use builtin instead. – Kusalananda Mar 2 '18 at 19:17
2

You can override a built-in command or an external command or a function with an alias. While the alias is executing, an alias with the same name won't be expanded. Thus, if the expansion of the alias uses the name of the alias, this calls the built-in or external command or function of the same name. This means you can use an alias in a simple way to pass extra options to a command. But you can't do much more, due to the way alias expansion works — it's a simple substitution of words, without evaluation.

You can override a built-in command or an external command with a function. The fact that a function by a certain name is currently executing does not affect the execution, and in particular does not affect the meaning of the function name: it keeps referring to the function. Recursive functions aren't common in shell programming, but there's no rule against them.

In POSIX shells, you can force a name to refer to a built-in or external command by putting command before the call. In other words, command bypasses aliases and functions. In zsh, command forces a call to an external command (unless the option posix_builtins is set), i.e. it bypasses builtins as well. To force the execution of a builtin in zsh, use the builtin builtin.

This leads to one solution: in your wrapper function, call builtin fc instead of fc.

However, this is not necessarily the best solution in your case, because your function will be invoked everywhere, including calls to fc from other functions. When you override built-in behavior, I recommend sticking to an alias. An alias only applies to what you type at the command line or load with . (a.k.a. source), not to functions loaded with autoload -U (which is the recommended way to autoload a function). So define an fc alias that calls your wrapper function, and give your wrapper function a different name.

fc_wrapper () {
    for x; do
        if [[ "${x}" == -*W* ]]; then
            echo >&2 "I'm sorry Dave. I'm afraid I can't do that."
            return 1
        fi
    done
    \fc "${@}"
}
alias fc=fc_wrapper

I put \fc instead of fc in the function definition. This isn't necessary when loading the definition from .zshrc before the alias definition, but it is necessary, for example, if you edit or reload the function definition during your interactive session. I made a few minor improvements to the function as well:

  • Print the error message on standard error.
  • If you aren't doing the job, return an error status.
  • Also catch cases such as fc -WI. This isn't fully robust, since it will also catch a call such as fc -R -- -W (reading history from a file called -W), but it's good enough for this use case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.