shell-quote escapes strings in a way that they can be inserted into shell scripts without the risk that they're interpreted as, say, multiple arguments (like with Bash's word splitting), paths (Bash's pathname expansion), shell metacharacters, function calls, or other syntax. This is frequently not as simple as wrapping a string in quotes.
This package implements escaping for GNU Bash, Z Shell,
fish, and /bin/sh
-like shells including Dash.
It can take as input many different string and byte string types:
&str
andString
&bstr::BStr
andbstr::BString
&[u8]
andVec<u8>
&OsStr
andOsString
(on UNIX)&Path
andPathBuf
and produce output as (or push into) the following types:
Inspired by the Haskell shell-escape package.
When quoting using raw bytes it can be convenient to call Sh
's, Dash
's,
Bash
's, Fish
's, and Zsh
's associated functions directly:
use shell_quote::{Bash, Dash, Fish, Sh, Zsh};
// No quoting is necessary for simple strings.
assert_eq!(Sh::quote_vec("foobar"), b"foobar");
assert_eq!(Dash::quote_vec("foobar"), b"foobar"); // `Dash` is an alias for `Sh`
assert_eq!(Bash::quote_vec("foobar"), b"foobar");
assert_eq!(Zsh::quote_vec("foobar"), b"foobar"); // `Zsh` is an alias for `Bash`
assert_eq!(Fish::quote_vec("foobar"), b"foobar");
// In all shells, quoting is necessary for strings with spaces.
assert_eq!(Sh::quote_vec("foo bar"), b"foo' bar'");
assert_eq!(Dash::quote_vec("foo bar"), b"foo' bar'");
assert_eq!(Bash::quote_vec("foo bar"), b"$'foo bar'");
assert_eq!(Zsh::quote_vec("foo bar"), b"$'foo bar'");
assert_eq!(Fish::quote_vec("foo bar"), b"foo' bar'");
It's also possible to use the extension trait QuoteRefExt
which provides a
quoted
function:
use shell_quote::{Bash, Sh, Fish, QuoteRefExt};
let quoted: String = "foo bar".quoted(Bash);
assert_eq!(quoted, "$'foo bar'");
let quoted: Vec<u8> = "foo bar".quoted(Sh);
assert_eq!(quoted, b"foo' bar'");
let quoted: String = "foo bar".quoted(Fish);
assert_eq!(quoted, "foo' bar'");
Or the extension trait QuoteExt
for pushing quoted strings into a buffer:
use shell_quote::{Bash, QuoteExt};
let mut script: bstr::BString = "echo ".into();
script.push_quoted(Bash, "foo bar");
script.extend(b" > ");
script.push_quoted(Bash, "/path/(to)/[output]");
assert_eq!(script, "echo $'foo bar' > $'/path/(to)/[output]'");
Here we will use Bash
for the example, but other shells may have similar or
different behaviours; check their documentation.
When we use &str
or String
as an input type, UTF-8 code points of U+0080
and above are written into the quoted form just as they are encoded in UTF-8,
i.e. the bytes are the same and there are no escape sequences. Compare this to
using a different input type:
# use shell_quote::{Bash, QuoteRefExt};
let data: &str = "café";
let data_utf8_quoted_from_string_type: Vec<u8> = data.quoted(Bash);
assert_eq!(&data_utf8_quoted_from_string_type, b"$'caf\xC3\xA9'"); // UTF-8, verbatim.
let data_utf8_quoted_from_bytes: Vec<u8> = data.as_bytes().quoted(Bash);
assert_eq!(&data_utf8_quoted_from_bytes, b"$'caf\\xC3\\xA9'"); // Now hex escaped!
It follows then, supposing you need to use a text encoding that is not UTF-8, that string types must be encoded before passing to the functions from this crate.
For example, the character 'é' (U+00E9):
- In ISO-8859-1, it is represented by the single byte
0xE9
. - In UTF-8, it is represented by the two bytes
0xC3 0xA9
.
Using a hypothetical encode_iso_8859_1
function:
# use shell_quote::{Bash, QuoteRefExt};
# fn encode_iso_8859_1(_s: &str) -> &[u8] {
# &[99, 97, 102, 233]
# }
let data = "café";
let data_utf8_quoted: Vec<u8> = data.quoted(Bash);
assert_eq!(&data_utf8_quoted, b"$'caf\xC3\xA9'"); // UTF-8: 2 bytes for é.
let data_iso_8859_1: &[u8] = encode_iso_8859_1(data);
let data_iso_8859_1_quoted: Vec<u8> = data_iso_8859_1.quoted(Bash);
assert_eq!(&data_iso_8859_1_quoted, b"$'caf\\xE9'"); // ISO-8859-1: 1 byte, hex escaped.
Sh
can serve as a lowest common denominator for Bash, Z Shell, and
/bin/sh
-like shells like Dash. However, fish's quoting rules are different
enough that you must use Fish
for fish scripts.
Note that using Sh
as a lowest common denominator brings with it other
issues; read its documentation carefully to understand the limitations.
The following are all enabled by default:
bstr
: Supportbstr::BStr
andbstr::BString
.bash
: Support Bash and Z Shell.fish
: Support fish.sh
: Support/bin/sh
-like shells including Dash.
To limit support to specific shells, you must disable this crate's default
features in Cargo.toml
and re-enable those you want. For example:
[dependencies]
shell-quote = { version = "*", default-features = false, features = ["bash"] }