Bash equivalent of Python's shlex.quote
Asked Answered
A

4

5

Python's standard library has a shlex.quote function that takes a string and returns one that is guaranteed to be interpreted as that same string by Unix shells. This is achieved by putting the string in single quotes and escaping any occurrences of the single quote character that appear in it.

This function is useful e.g. when you're templating out shell scripts and can't guarantee that the substituted values only contain "shell-safe" strings.

My question: Is there an equivalent of this written in pure bash with at most coreutils as a dependency? Or perhaps even a bash builtin mechanism I'm not aware of?


Minimal example to show how you would use such a utility (called shlex_quote here as a placeholder):

generate_greeting_script.sh:

#!/bin/bash
cat > greet.sh <<HEREDOC
#!/bin/bash
greeting=$(shlex_quote "$1")
echo "\$greeting \$(whoami)!"
HEREDOC
chmod +x greet.sh
$ ./generate_greeting_script.sh "'ello"
$ ./greet.sh
'ello govnah!

Which works just fine with a shlex_quote utility using Python's shlex.quote, but having Python as a dependency just for that is overkill.

Apperception answered 23/1, 2022 at 12:37 Comment(0)
H
4

Or using @Q variable expansion

cat generate_greeting_script:

#!/usr/bin/env bash

cat >greet <<HEREDOC
#!/usr/bin/env bash

printf ${1@Q}' %s!\n' "\$USER"
HEREDOC
chmod +x greet
./generate_greeting_script 'I say hello to'

A .sh extension on executable scripts with a shebang is not required.

Generated greet:

#!/usr/bin/env bash

printf 'I say hello to'' %s!\n' "$USER"

Output:

./greet
I say hello to lea!

Reference man bash:

${parameter@operator}

Parameter transformation.

The expansion is either a transformation of the value of parameter or information about parameter itself, depending on the value of operator. Each operator is a single letter:

Q

The expansion is a string that is the value of parameter quoted in a format that can be reused as input.

Homochromous answered 23/1, 2022 at 15:6 Comment(3)
That seems like the most idiomatic answer to me (and is a bit shorter than printf %q-based ones). Interesting how printf %q and ${...@Q} use different approaches to how exactly quoting is performed: 'ello there becomes \'ello\ there with printf but ''\''ello there' with the Q transformation. I guess printf's representation is more "efficient" if there are a lot of quotes, while @Q does better if there are a lot of spaces.Apperception
When was @Q added? I think is was around 4.x? Living on a clean MacOS with bash 3.2 it's not something i get to use often :-)Piglet
@AndreasLouv if you are going to use Bash often, then it is worth upgrading to Bash 5.1 or 5.2. The Bash 3.2 is from 2006 (15 years old, is pretty-much archaic on a computing scale).Zwick
P
6

Since you refer to bash, you can use the bash specific format character "q" in printf:

$ printf '%q\n' "The world is fuiled by \$s isn't it?" 
The\ world\ is\ fuiled\ by\ \$s\ isn\'t\ it\?

From the documentation on bash:

%q
Causes printf to output the corresponding argument in a format that can be reused as shell input.

https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html

Piglet answered 23/1, 2022 at 12:44 Comment(0)
H
4

Or using @Q variable expansion

cat generate_greeting_script:

#!/usr/bin/env bash

cat >greet <<HEREDOC
#!/usr/bin/env bash

printf ${1@Q}' %s!\n' "\$USER"
HEREDOC
chmod +x greet
./generate_greeting_script 'I say hello to'

A .sh extension on executable scripts with a shebang is not required.

Generated greet:

#!/usr/bin/env bash

printf 'I say hello to'' %s!\n' "$USER"

Output:

./greet
I say hello to lea!

Reference man bash:

${parameter@operator}

Parameter transformation.

The expansion is either a transformation of the value of parameter or information about parameter itself, depending on the value of operator. Each operator is a single letter:

Q

The expansion is a string that is the value of parameter quoted in a format that can be reused as input.

Homochromous answered 23/1, 2022 at 15:6 Comment(3)
That seems like the most idiomatic answer to me (and is a bit shorter than printf %q-based ones). Interesting how printf %q and ${...@Q} use different approaches to how exactly quoting is performed: 'ello there becomes \'ello\ there with printf but ''\''ello there' with the Q transformation. I guess printf's representation is more "efficient" if there are a lot of quotes, while @Q does better if there are a lot of spaces.Apperception
When was @Q added? I think is was around 4.x? Living on a clean MacOS with bash 3.2 it's not something i get to use often :-)Piglet
@AndreasLouv if you are going to use Bash often, then it is worth upgrading to Bash 5.1 or 5.2. The Bash 3.2 is from 2006 (15 years old, is pretty-much archaic on a computing scale).Zwick
M
2

I think you're looking Bash-specific printf %q format specifier:

$ read &&  printf "%q\n" "$REPLY"
"very*complicated_&&;$stuff--string'""""'"<<!!
\"very\*complicated_\&\&\;\$stuff--string\'\"\"\"\"\'\"\<\<\!\!
$ echo \"very\*complicated_\&\&\;\$stuff--string\'\"\"\"\"\'\"\<\<\!\!
"very*complicated_&&;$stuff--string'""""'"<<!!
Merchandise answered 23/1, 2022 at 12:45 Comment(0)
S
1

To put the quoted contents of $1 in the variable greeting:

printf -v greeting '%q' "$1"

That uses the Bash-specific -v option and %q format string for printf.

Suffruticose answered 23/1, 2022 at 13:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.