I have a variable, e.g. a'b, containing a single quote which I need to replace by two single quotes before writing it to a file: a''b.

The following bash code used to get the job done for me ...

echo "${line//\'/\'\'}" > out.txt

... until today when I discovered diverging outputs depending on the version of bash being used:

  • GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu): a''b
  • GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu): a\'\'b

I tried to modify the line above in numerous ways but was unable to have it produce the same output a''b in both bash shells.

I ended up using a combination of echo and sed instead,

echo "$(echo $line | sed "s/'/''/g")" > out.txt

but I am still wondering if the job can be gotten done with a more concise pure bash expression. Can it?

  • the first thing that comes to mind is that you don't need the echo. just printf "%s\n" $line | sed "s/'/''/g" > out.txt will do. BTW, the echo "${line//\'/\'\'}" works as desired in bash 5.0.3 for me.
    – cas
    Aug 28, 2019 at 8:58
  • Thou should never use variables in format argument of printf.
    – pLumo
    Aug 28, 2019 at 8:58
  • 2
    are you parsing the output of ls? if so then a) don't do that and b) run man ls and search for --quoting-style and c) run typeset -p QUOTING_STYLE and d) see Why is 'ls' suddenly wrapping items with spaces in single quotes?
    – cas
    Aug 28, 2019 at 9:02
  • 1
    If you are parsing ls, then this is an XY Problem for at least two reasons. Firstly because there are many better ways to process a list of files than to parse ls. Secondly because the need to double any single-quotes probably only arises because you're trying to parse the output of ls and then (presumably) use the filenames as arguments to some other program. What are you actually trying to achieve that you think parsing ls is part of the solution for?
    – cas
    Aug 28, 2019 at 9:09
  • @pLumo you are right, and I had replaced the printf call with an echo call by now. Edited the question this way too.
    – Bastian
    Aug 28, 2019 at 9:21

2 Answers 2


One of the changes between bash-4.3-alpha, and the previous version, bash-4.2-release:

When using the pattern substitution word expansion, bash now runs the replacement string through quote removal, since it allows quotes in that string to act as escape characters. This is not backwards compatible, so it can be disabled by setting the bash compatibility mode to 4.2.


echo "${line//\'/''}"



For a method that would work regardless of the version (and shell implementation like ksh93 where it comes from and mksh and zsh which also support it), you can store the pattern and replacement in a variable:

printf '%s\n' "${line//$pattern/$replacement}"


printf '%s\n' "${line//$q/$q$q}"

(note that in zsh, the $pattern is taken as a fixed string. If you want it to be taken as a wildcard pattern (which doesn't apply here as $pattern doesn't contain wildcard characters), you need to replace $pattern with $~pattern. In other shells, if you want $pattern to be taken as a fixed string, you need to quote it (${line//"$pattern"/$replacement}).

  • Also: "${line//\47/\47\47}"
    – Léa Gris
    Jan 22, 2021 at 13:18
  • @LéaGris this doesn't work for me in Bash 5.1 but a similar ANSI-quoted string does. "${line//$'\47'/'}". Only had to do this because of a syntax highlight bug in my editor that choked on ${line//"'"/'}
    – miken32
    Apr 23, 2022 at 17:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .