0

Ran into an issue when running the following command in a fish shell:

❯ printf '%q\n' 'André Previn & London Symphony Orchestra'
%q: invalid conversion specification

I hadn't realized at first that fish actually has their own custom printf function that behaves in largely the same way as the GNU coreutils printf function, but does not support the q directive like GNU coreutils printf does.

fish-shell docs:

https://fishshell.com/docs/current/cmds/printf.html#format-specifiers

fish-shell printf documentation

GNU coreutils printf docs:

https://www.gnu.org/software/coreutils/manual/html_node/printf-invocation.html#printf-invocation

GNU coreutils printf documentation

Is there a way to tell fish-shell that I want it to use the GNU coreutils printf function instead of their customized printf function?

[Edit 1]: I hadn't realized that MacOS has it's own BSD-derived builtins that it uses, not the GNU coreutils builtins. Still am able to use the q option in zsh, but not in fish when running the following commands:

# in zsh
❯ printf '%q\n' 'test'
test

# fish, run in a zsh shell
❯ builtin printf '%q\n' 'test'
%q: invalid conversion specification

❯ command printf '%q\n' 'test'
printf: illegal format character q
5
  • How about calling it via the absolute path? Commented Aug 22, 2024 at 10:05
  • Not sure how you mean. GNU coreutils' printf is a builtin, so I'm not sure there is an absolute path I can reference - I think it's prebaked into the shell, in my case zsh Commented Aug 22, 2024 at 12:26
  • 1
    Coreutils, by definition, are a collection of utilities, so the idea of builtin is meaningless. So you want to run the builtin printf from your zsh? In this case, create a zsh wrapper, which just invokes its builtin printf and passes to it all its parameters. Commented Aug 22, 2024 at 13:32
  • 1
    Folks getting confused by your use of the word 'builtin' isn't helpful. You gave all the right info, even if you used imprecise terminology - if you installed coreutils, you got the gprintf utility which is all you need to solve your issue. See my answer below. Commented Sep 15, 2024 at 13:18
  • 1
    Appreciate the understanding and for the thorough answer @mattmc3. This solves the issue I was running into exactly, I was able to use gprintf for this purpose. Thanks again. Commented Sep 16, 2024 at 16:21

4 Answers 4

1

Typically when you install GNU coreutils, you get the utilities with a 'g' in front of them. For example: 'gls', 'ggrep', 'gawk', 'gsed', etc.

So, for MacOS after running brew install coreutils, you'll find gprintf probably lives in /opt/homebrew/bin/gprintf. Run this command to see where yours lives:

> type gprintf
/path/to/gnu/coreutils/gprintf

The answer to what you're asking would be to wrap gprintf in a printf function:

# BUT DON'T ACTUALLY DO THIS
> function printf
    gprintf $argv
end

> printf '%q\n' 'André Previn & London Symphony Orchestra'
'André Previn & London Symphony Orchestra'

Now, that being said, I would not recommend this. You likely have other scripts in your environment that expect printf to behave like Fish's printf does. Don't shadow it. If you want/need coreutils printf behavior, just use the 'g' varaiant. If you don't have a proper 'g' variant, then make that instead:

# Do this if you have gprintf
> gprintf '%q\n' 'André Previn & London Symphony Orchestra'
'André Previn & London Symphony Orchestra'

# Do this if you don't have gprintf
function gprintf
    # If you have a printf util:
    # /path/to/gnu/coreutils/printf $argv

    # Or, farm to bash
    # bash -c 'printf "%q\n" "$@"' $argv
end
Sign up to request clarification or add additional context in comments.

Comments

1

When you run unqualified printf in fish, bash or zsh, it will run the given shell's builtin version of printf.

There is an executable called printf on your system, probably in /usr/bin/printf or similar.

To run that one, you can use command printf in fish or e.g. env printf (which would also work in bash).

However, since you are on macOS it is not a GNU coreutils version, but macOS' own BSD-derived one.

And since %q is a GNU-extension, it won't have it, and so running it won't actually help you.

You can run e.g. bash's builtin printf by running bash:

bash -c 'printf "%q\n" "$@"' printf 'echo $(hahaha)'

(that second "printf" tells bash to call that process "printf" - it uses it as the argv0, $0)

Fish's printf does not have %q, and if it did it would perform escaping for fish, not bash/zsh/sh - the quoting rules are different. It does have string escape instead.

3 Comments

OP should install GNU coreutils and use gprintf for this.
%q is from ksh93, not GNU, only added to GNU coreutils's printf in 8.25 in 2015, and bash (the GNU shell) printf builtin in 2.02 in 1998 when a printf builtin was actually added to bash.
It's not really the process that is called printf, more like the inline script, the process will still be called bash. I'd use function bash_printf; bash -c 'printf "$@"' bash_printf $argv; end (or same with ksh93 or zsh which also support %q and much more than bash's) to make it clear that it is a bash printf and that the quoting it produces is specific to bash (though should be understood by other ksh93-like shells except maybe in a few corner cases).
0

Can use string escape for this

https://github.com/fish-shell/fish-shell/issues/10678#issuecomment-2303446296

Comments

0

The standard idiom to avoid shell builtins is to use env. I.e. env printf ...

3 Comments

When I run env printf '%q\n' 'test' in a fish shell, I get this error message: printf: illegal format character
Works for me with fish 3.7.0 ?
I'm on fish, version 3.7.1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.