Skip to content

A Rust library for shell quoting strings, e.g. for interpolating into a Bash script. This is not as simple as most people think!

License

Notifications You must be signed in to change notification settings

allenap/shell-quote

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shell-quote

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:

and produce output as (or push into) the following types:

Inspired by the Haskell shell-escape package.

Examples

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]'");

Notes on string encoding

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.

Compatibility

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.

Feature flags

The following are all enabled by default:

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"] }

About

A Rust library for shell quoting strings, e.g. for interpolating into a Bash script. This is not as simple as most people think!

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages