438

Below is the snippet of a shell script from a larger script. It removes the quotes from the string that is held by a variable. I am doing it using sed, but is it efficient? If not, then what is the efficient way?

#!/bin/sh

opt="\"html\\test\\\""
temp=`echo $opt | sed 's/.\(.*\)/\1/' | sed 's/\(.*\)./\1/'`
echo $temp
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user1263746
  • 5,788
  • 4
  • 24
  • 28
  • I would suggest using `sed "s/^\(\"\)\(.*\)\1\$/\2/g" <<<"$opt"`. This syntax will remove qoutes only when there is a matching pair. – John Smith Aug 30 '17 at 18:33
  • @JohnSmith I also have to automatically escape quotes in a shell script, but I need to do so whether they are matching or not, so I probably will not use that expression you posted. – Pysis Oct 11 '17 at 13:46
  • If you found this question while simply wanting to remove all quotes, see this answer: https://askubuntu.com/a/979964/103498. – Gordon Bean May 13 '19 at 18:35

19 Answers19

549

Use tr to delete ":

 echo "$opt" | tr -d '"'

NOTE: This does not fully answer the question, removes all double quotes, not just leading and trailing. See other answers below.

Mike Q
  • 6,716
  • 5
  • 55
  • 62
wieczorek1990
  • 7,403
  • 1
  • 25
  • 19
  • 30
    This is not what OP asked for since it also removes all quotes, not just the leading and trailing ones. – Lenar Hoyt Dec 24 '15 at 14:51
  • 35
    Although not answering OP explicitly this solves for me because there are only double quotes at beginning and end of "/path/file-name". There are no embedded double quotes. +1 – WinEunuuchs2Unix Feb 19 '17 at 15:55
  • 14
    This solution will be what a lot of people want. In many cases, scripting can easily get the workable data down to a string surrounded by quotes. – Shadoninja Jun 25 '18 at 19:27
  • 5
    Elegant and saved me from a lot more trial-error-debug time. Thx. – Scott Wade Nov 22 '18 at 00:07
  • 1
    This is what ideally one wants and thats why it has more votes than the accepted answer. – apurvc Nov 13 '19 at 17:37
  • so how is this the correct answer ? Either the question needs to be updated or the answer. – Mike Q Mar 09 '22 at 22:34
  • While this may not be strictly correct, it works very well for a lot of scenarios, and doesn't require additional fiddling if you can pipe your result – Andrew Knackstedt Jul 17 '23 at 14:34
399

There's a simpler and more efficient way, using the native shell prefix/suffix removal feature:

temp="${opt%\"}"
temp="${temp#\"}"
echo "$temp"

${opt%\"} will remove the suffix " (escaped with a backslash to prevent shell interpretation).

${temp#\"} will remove the prefix " (escaped with a backslash to prevent shell interpretation).

Another advantage is that it will remove surrounding quotes only if there are surrounding quotes.

BTW, your solution always removes the first and last character, whatever they may be (of course, I'm sure you know your data, but it's always better to be sure of what you're removing).

Using sed:

echo "$opt" | sed -e 's/^"//' -e 's/"$//'

(Improved version, as indicated by jfgagne, getting rid of echo)

sed -e 's/^"//' -e 's/"$//' <<<"$opt"

So it replaces a leading " with nothing, and a trailing " with nothing too. In the same invocation (there isn't any need to pipe and start another sed. Using -e you can have multiple text processing).

Yuri
  • 4,254
  • 1
  • 29
  • 46
huelbois
  • 6,762
  • 1
  • 19
  • 21
  • 17
    You can get rid of the pipe in the sed solution with `sed -e 's/^"//' -e 's/"$//' <<< $opt`. – jfg956 Mar 17 '12 at 15:15
  • 1
    This could misbehave it the string doesn't have both a leading and trailing quote character. Any suggestions for handling that gracefully? – jsears Apr 13 '16 at 21:37
  • To handle only matched outer quotes, see the answer by @Joachim Pileborg – jsears Apr 13 '16 at 21:43
  • 2
    @jsears Huh? This specifically trims one from the start if there is one, and one from the end if there is one. If you don't want to remove unless both are present; yes, a single `sed` regex could be used, or the variable substitution could be wrapped in something line `case $opt in '"'*'"') ... do it ... ;; esac` – tripleee Oct 07 '16 at 06:24
  • @tripleee : in my bash version (4.3-14), quoting $opt does not change anything to "<<<". Even if opt contains spaces or "*", it is not expanded. Not sure if it was always the case though – huelbois Oct 07 '16 at 08:28
  • Expands on OSX Yosemite (10.10); Bash 3.2.57(1). – tripleee Oct 07 '16 at 08:31
  • 5
    You could avoid the multiple expressions by using `sed 's/^"\|"$//g'` – tzrlk Oct 18 '17 at 23:42
  • Awesome... now you can do something like this `$REPLACEME=$(sed -e 's/^"//' -e 's/"$//' <<<"$REPLACEME")` so you can have a working variable. – lu1s Dec 06 '17 at 22:58
  • @tzrlk don't you need parentheses if you want to use the or in the expression like so: sed -E 's/(^")|("$)//g') – Wolfgang Brehm Nov 11 '19 at 15:18
  • 1
    You can join multiple sed expressions with a semicolon, like this `echo '"foo"' | sed 's/^"//;s/"$//'` – Sam Jan 07 '20 at 09:50
  • I think it would be helpful if the question was more explicit about the range of inputs, the desired output function, and the meaning of "efficient". Being accurate comes before being efficient. The answers below are divided between those that care about about balanced quotes and those that don't, because the question is not clear on that issue. Those two cases should really be separate questions. – Craig Hicks Jul 19 '20 at 22:54
  • One more option to combine prefix and suffix removal $ echo ${opt//[\"]} – Alex Glukhovtsev Jun 09 '22 at 12:45
185

If you're using jq and trying to remove the quotes from the result, the other answers will work, but there's a better way. By using the -r option, you can output the result with no quotes.

$ echo '{"foo": "bar"}' | jq '.foo'
"bar"

$ echo '{"foo": "bar"}' | jq -r '.foo'
bar
killjoy
  • 3,665
  • 1
  • 19
  • 16
86

There is a straightforward way using xargs:

> echo '"quoted"' | xargs
quoted

xargs uses echo as the default command if no command is provided and strips quotes from the input, see e.g. here. Note, however, that this will work only if the string does not contain additional quotes. In that case it will either fail (uneven number of quotes) or remove all of them.

user1587520
  • 3,777
  • 1
  • 21
  • 20
43

If you came here from AWS CLI --query, try this. --output text

Jehong Ahn
  • 1,872
  • 1
  • 19
  • 25
31

You can do it with only one call to sed:

$ echo "\"html\\test\\\"" | sed 's/^"\(.*\)"$/\1/'
html\test\
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
30

The shortest way around - try:

echo $opt | sed "s/\"//g"

It actually removes all "s (double quotes) from opt (are there really going to be any more double quotes other than in the beginning and the end though? So it's actually the same thing, and much more brief ;-))

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • 7
    If you want to remove all the double quotes, then it's even better to use : "${opt//\"/}". No pipe, no subshell... And beware that if you have spaces in opt, you may loose them. Always quote variables like : echo "$opt" – huelbois Mar 16 '12 at 07:35
  • Well, the variable basically reads the path of DocumentRoot from httpd's config file, so I dont think there could be case where a quote character could be possibly there in the string. And this sed is much neater and efficient.... Thanks! – user1263746 Mar 16 '12 at 08:49
29

The easiest solution in Bash:

$ s='"abc"'
$ echo $s
"abc"
$ echo "${s:1:-1}"
abc

This is called substring expansion (see Gnu Bash Manual and search for ${parameter:offset:length}). In this example it takes the substring from s starting at position 1 and ending at the second last position. This is due to the fact that if length is a negative value it is interpreted as a backwards running offset from the end of parameter.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
17

Update

A simple and elegant answer from Stripping single and double quotes in a string using bash / standard Linux commands only:

BAR=$(eval echo $BAR) strips quotes from BAR.

=============================================================

Based on hueybois's answer, I came up with this function after much trial and error:

function stripStartAndEndQuotes {
    cmd="temp=\${$1%\\\"}"
    eval echo $cmd
    temp="${temp#\"}"
    eval echo "$1=$temp"
}

If you don't want anything printed out, you can pipe the evals to /dev/null 2>&1.

Usage:

$ BAR="FOO BAR"
$ echo BAR
"FOO BAR"
$ stripStartAndEndQuotes "BAR"
$ echo BAR
FOO BAR
Jonathan Lin
  • 19,922
  • 7
  • 69
  • 65
  • 5
    Though I really like the simple and elegant, I think it is worth noticing that eval doesn't do just double quote trimmings, but actually evaluates as a line of code. Therefore this technique may yield unexpected results when BAR contains other sequences that may substitute by the shell (e.g. BAR=\'abc\' or BAR=ab\'c or BAR=a\$b, or BAR=a\$\(ls \*\) ) – MaxP Nov 06 '17 at 13:23
14
Linux=`cat /etc/os-release | grep "ID" | head -1 | awk -F= '{ print $2 }'`

echo $Linux
Output:
"amzn"

Simplest ways to remove double quotes from variables are

Linux=`echo "$Linux" | tr -d '"'` 
Linux=$(eval echo $Linux)
Linux=`echo ${Linux//\"/}`
Linux=`echo $Linux | xargs`

All provides the Output without double quotes:

echo $Linux

amzn

13

This is the most discrete way without using sed:

x='"fish"'
printf "   quotes: %s\nno quotes:  %s\n" "$x" "${x//\"/}"

Or

echo $x
echo ${x//\"/}

Output:

   quotes: "fish"
no quotes:  fish

I got this from a source.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nimig18
  • 797
  • 7
  • 10
  • 1
    This will remove quotes from within the string as well as those surrounding it. – jsears Apr 13 '16 at 21:41
  • 1
    Apart from the commentary, this is identical to [@StevenPenny's answer from 2012](http://stackoverflow.com/a/10943857/874188). – tripleee Oct 07 '16 at 06:26
  • Note that this makes use of bash-specific variable substitution and won't work in `sh`. That said, I think that when using bash this is the best answer. – Christian Fritz Mar 28 '22 at 00:45
13

I know this is a very old question, but here is another sed variation, which may be useful to someone. Unlike some of the others, it only replaces double quotes at the start or end...

echo "$opt" | sed -r 's/^"|"$//g'

If you need to match single or double quotes, and only strings that are properly quoted. You can use this slightly more complex regex...

echo $opt | sed -E "s|^(['\"])(.*)\1$|\2|g"

This uses backrefences to ensure the quote at the end is the same as at the start.

user1751825
  • 4,029
  • 1
  • 28
  • 58
7

In Bash, you could use the following one-liner:

[[ "${var}" == \"*\" || "${var}" == \'*\' ]] && var="${var:1:-1}"

This will remove surrounding quotes (both single and double) from the string stored in var while keeping quote characters inside the string intact. Also, this won't do anything if there's only a single leading quote or only a single trailing quote or if there are mixed quote characters at start/end.


Wrapped in a function:

#!/usr/bin/env bash

# Strip surrounding quotes from string [$1: variable name]
function strip_quotes() {
    local -n var="$1"
    [[ "${var}" == \"*\" || "${var}" == \'*\' ]] && var="${var:1:-1}"
}

str="'hello world'"
echo "Before: ${str}"
strip_quotes str
echo "After: ${str}"
Fonic
  • 2,625
  • 23
  • 20
  • a simpler 1 liner... echo $opt | sed -E "s|^(['\"])(.*)\1$|\2|g" – user1751825 Mar 30 '22 at 04:10
  • @user1751825: The OP's question was to _remove quotes from a string held by a variable_, thus you would need to change your one-liner to `temp=$(echo "$opt" | sed -E "s|^(['\"])(.*)\1$|\2|g")` to achieve that result (also, don't forget to quote `$opt`). Thus, you would need to use a subshell + pipe to sed. As the OP also asked for an _efficient_ solution, your one-liner is in no way simpler or better than my Bash-only solution. – Fonic Mar 31 '22 at 07:10
3
STR='"0.0.0"' ## OR STR="\"0.0.0\""
echo "${STR//\"/}"
## Output: 0.0.0 
DEV Tiago França
  • 1,271
  • 9
  • 9
2

My version

strip_quotes() {
    while [[ $# -gt 0 ]]; do
        local value=${!1}
        local len=${#value}
        [[ ${value:0:1} == \" && ${value:$len-1:1} == \" ]] && declare -g $1="${value:1:$len-2}"
        shift
    done
}

The function accepts variable name(s) and strips quotes in place. It only strips a matching pair of leading and trailing quotes. It doesn't check if the trailing quote is escaped (preceded by \ which is not itself escaped).

In my experience, general-purpose string utility functions like this (I have a library of them) are most efficient when manipulating the strings directly, not using any pattern matching and especially not creating any sub-shells, or calling any external tools such as sed, awk or grep.

var1="\"test \\ \" end \""
var2=test
var3=\"test
var4=test\"
echo before:
for i in var{1,2,3,4}; do
    echo $i="${!i}"
done
strip_quotes var{1,2,3,4}
echo
echo after:
for i in var{1,2,3,4}; do
    echo $i="${!i}"
done
Gene Pavlovsky
  • 1,515
  • 17
  • 14
2

I use this regular expression, which avoids removing quotes from strings that are not properly quoted, here the different outputs are shown depending on the inputs, only one with begin-end quote was affected:

echo '"only first' | sed 's/^"\(.*\)"$/\1/'

Output: >"only first<

echo 'only last"' | sed 's/^"\(.*\)"$/\1/'

Output: >"only last"<

echo '"both"' | sed 's/^"\(.*\)"$/\1/'

Output: >both<

echo '"space after" ' | sed 's/^"\(.*\)"$/\1/'

Output: >"space after" <

echo ' "space before"' | sed 's/^"\(.*\)"$/\1/'

Output: > "space before"<

1

There is another way to do it. Like:

echo ${opt:1:-1}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
DevilTour
  • 39
  • 4
  • This answer has [already been given](https://stackoverflow.com/a/56445048/7233423) and the other is more detailed. – xhienne Feb 11 '21 at 13:20
0

If you're here because your terraform output is quoting values, add -raw to remove them. I haven't tracked down the release notes, but at some point around .13, .14, maybe even 1.0.0 there appears to be a change that caused output values to be quoted when they weren't previously.

-2

If you try to remove quotes because the Makefile keeps them, try this:

$(subst $\",,$(YOUR_VARIABLE))

Based on another answer: https://stackoverflow.com/a/10430975/10452175

James Bond
  • 2,229
  • 1
  • 15
  • 26