Bash printf %q invalid directive
Asked Answered
H

3

8

I want to change my PS1 in my .bashrc file. I've found a script using printf with %q directive to escape characters :

#!/bin/bash
STR=$(printf "%q" "PS1=\u@\h:\w\$ ")
sed -i '/PS1/c\'"$STR" ~/.bashrc

The problem is that I get this error :

script.sh: 2: printf: %q: invalid directive

Any idea ? Maybe an other way to escape the characters ?

Hungary answered 26/9, 2014 at 22:8 Comment(0)
I
15

The printf command is built into bash. It's also an external command, typically installed in /usr/bin/printf. On most Linux systems, /usr/bin/printf is the GNU coreutils implementation.

Older releases of the GNU coreutils printf command do not support the %q format specifier; it was introduced in version 8.25, released 2016-10-20. bash's built-in printf command does -- and has as long as bash has had a built-in printf command.

The error message implies that you're running script.sh using something other than bash.

Since the #!/bin/bash line appears to be correct, you're probably doing one of the following:

sh script.sh
. script.sh
source script.sh

Instead, just execute it directly (after making sure it has execute permission, using chmod +x if needed):

./script.sh

Or you could just edit your .bashrc file manually. The script, if executed correctly, will add this line to your .bashrc:

PS1=\\u@\\h:\\w\$\ 

(The space at the end of that line is significant.) Or you can do it more simply like this:

PS1='\u@\h:\w\$ '

One problem with the script is that it will replace every line that mentions PS1. If you just set it once and otherwise don't refer to it, that's fine, but if you have something like:

if [ ... ] ; then
    PS1=this
else
    PS1=that
fi

then the script will thoroughly mess that up. It's just a bit too clever.

Izard answered 26/9, 2014 at 23:27 Comment(3)
This is one of those situations where doing it manually is simpler and safer than doing it with a fancy command line. :)Peccary
RE: The GNU coreutils printf command does not support the %q format specifier The coreutils documentation now mentions the %q directive: gnu.org/software/coreutils/printfAbsquatulate
@JanusTroelsen: Updated.Izard
P
2

Keith Thompson has given good advice in his answer. But FWIW, you can force bash to use a builtin command by preceding the command name with builtin eg

builtin printf "%q" "PS1=\u@\h:\w\$ "

Conversely,

command printf "%s\n" some stuff

forces bash to use the external command (if it can find one).

command can be used to invoke commands on disk when a function with the same name exists. However, command does not invoke a command on disk in lieu of a Bash built-in with the same name, it only works to suppress invocation of a shell function. (Thanks to Rockallite for bringing this error to my attention).

It's possible to enable or disable specific bash builtins (maybe your .bashrc is doing that to printf). See help enable for details. And I guess I should mention that you can use

type printf

to find out what kind of entity (shell function, builtin, or external command) bash will run when you give it a naked printf. You can get a list of all commands with a given name by passing type the -a option, eg

type -a printf 

You can use grep to see the lines in your .bashrc file that contain PS1:

grep 'PS1' ~/.bashrc 

or

grep -n0 --color=auto 'PS1=' ~/.bashrc

which gives you line numbers and fancy coloured output. And then you can use the line number to force sed to just modify the line you want changed.

Eg, if grep tells you that the line you want to change is line 7, you can do

sed -i '7c\'"$STR" ~/.bashrc

to edit it. Or even better,

sed -i~ '7c\'"$STR" ~/.bashrc

which backs up the original version of the file in case you make a mistake.

When using sed -i I generally do a test run first without the -i so that the output goes to the shell, to let me see what the modifications do before I write them to the file.

Peccary answered 27/9, 2014 at 19:25 Comment(9)
I don't think using builtin printf would help in this case. bash will use the builtin printf by default. If it's not using it in this case, it's because the shell being invoked isn't bash.Izard
Well yeah. Unless it's been disabled during shell startup by enable -n printf for some weird reason. But I agree that your theory is more likely, I was just mentioning it as a possibility.Peccary
Hmm. Somehow I've managed to avoid learning about the enable command until now. Interesting.Izard
I admit it is a bit obscure. I only learned about it recently myself: I stumbled across it in the bash man while I was looking up the details of some other builtin. I'm sure it'll come in handy one day... :)Peccary
enable -n enable is tempting.Izard
ROFL! I guess using it temporarily would be ok, but I'd never set in .bashrc, as I generally try to avoid fiddling with default shell parameters that could interfere with script portability. And if you forget you've done stuff like that it can make debugging a PITA.Peccary
command printf also invokes the shell builtin. Run command -V printf and check it.Tillion
@Tillion Oops! It looks like I didn't test that very well, did I? ;) I've repaired my answer. Thanks for bringing that blunder to my attention.Peccary
@KeithThompson FWIW, I had a mistake in this answer, but I've fixed it now.Peccary
R
0

For anyone else that's using fish-shell, I didn't realize that fish-shell actually has its own "builtin" printf function that doesn't have the %q directive supported. When I tried using that directive inside a fish shell I would get an error message like:

%q: invalid conversion specification

When I exited fish-shell and directly used zsh, I was able to use printf '%q\n' ... without issue.

Recommend answered 22/8 at 1:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.