3

SOLVED: Escaping strings in Bash

 3 years ago
source link: https://qntm.org/bash
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Update, 2011-06-13:

I raised a question on Stack Overflow, How can I escape an arbitrary string for use as a command line argument in Bash?. Several people contributed incomplete or incorrect answers but eventually I figured out the following.

To escape a string for use as a command line argument in Bash, simply put a backslash in front of every non-alphanumeric character. Do not wrap the string in single quotes or double quotes. Escape everything that is non-alphanumeric, including spaces, exclamation marks, dollar signs, ampersands, angle brackets, double quotes and single quotes.

In Perl, the code looks like this:

my @args = (
    # a list of arbitrary strings...
);

$cmd = join " ", map { escape($_); } @args;

sub escape {
    my $arg = shift;
    $arg =~ s/([^a-zA-Z0-9])/\\$1/g;
    return $arg;
}

In fact, you can leave some characters unescaped safely, such as underscores. But there seems to be no complete list of "dangerous" characters in Bash, and you can escape any character in Bash with impunity, so it is safest to just escape everything.

Original problem

This has been driving me nuts at work lately and I can't find anything about it using Google. Since this site seems to get a decent number of highly intelligent readers, I'm sure one of you will be able to answer this. This should also prove my "if you want to find the right answer to something, the quickest way is to put the wrong answer online and wait for someone to correct you" hypothesis.

In the Bash shell, how do you escape an arbitrary string for encapsulation in quotes?

Let's say my string was:

"That's impossible!" he said.

Note that this string includes double quotes, an apostrophe and an exclamation mark. Now, I want a function or procedure which will give me a new string so that I can do:

$ echo <the new string>
"That's impossible!" he said.

Here are the problems I get, though:

$ echo "That's impossible!" he said
bash: !": event not found

$ echo "That's impossible\!" he said
That's impossible\! he said

$ echo ""That's impossible!" he said"
>

$ echo "\"That's impossible!\" he said"
bash: !\": event not found

$ echo "\"That's impossible\!\" he said"
"That's impossible\!" he said

$ echo '"That's impossible!" he said'
bash: !": event not found

$ echo '"That\'s impossible!" he said'
bash: !": event not found

$ echo '"That\'
"That\

$ echo '\''
>

$ echo '''
>

$ echo "!"
bash: !: event not found

$ echo "\!"
\!

There's no way to put a literal single quote into a single-quoted string because a single-quoted string doesn't allow you to escape anything. The single quote will invariably terminate the string in progress and everything afterwards will be interpreted or whatever. But there's also no way to put a literal exclamation mark into a double-quote string because either it will get expanded into some previous event, or if you do backslash-escape it, the backslash gets printed as well! So if you have a string containing both an exclamation mark and a single quote - or arbitrary numbers of both - is there no way to escape it at all?

Is there something I'm missing here? I hope the solution isn't "install a different shell" or "radically modify the existing shell" because I interact with dozens of distinct machines which have this problem, as do dozens of other people and automated test processes.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK