1

is it possible to filter, i.e to put ESC in front of the inner quotes with sed, awk or other *NIX tool (no perl/python)?

Example (revised):

$ echo label=\"123 \"456\" 789\" \"AB C\" e f gh | magic-filter
label="123 \"456\" 789\" \"AB C" e f gh

label="123 \"456\" 789" "AB C"

In other words a filter is needed that will filter in the following way: the first and last " char will be passed as is, but all other " will be replaced with \ followed by ".

  • 6
    By which rule is it possible to define which " is "internal"? You could have as well selected the first and the last as external and the rest as internal. There are several other alternative selections. – sorontar Oct 23 '16 at 18:40
  • 2
    That is correct remark! Let's assume all quotes between th 1st and last as inner. – ivand58 Oct 23 '16 at 19:13
  • @ivand58 you should edit your question to include that information. – strugee Oct 24 '16 at 1:32
1

With GNU sed, which supports replacing all occurrences from particular count

$ echo label=\"123 \"456\" 789\" \"AB C\" e f gh | 
    sed -E 's/"/\\"/2g; s/\\("[^"]*)$/\1/'
label="123 \"456\" 789\" \"AB C" e f gh

All " (expect first ") are replaced with \" and then \ is removed from last \"


If GNU sed is not available, remove \ from first \" as well

$ echo label=\"123 \"456\" 789\" \"AB C\" e f gh | 
    sed -E 's/"/\\"/g; s/\\"/"/; s/\\("[^"]*)$/\1/'
label="123 \"456\" 789\" \"AB C" e f gh

Note: Some sed versions might need -r instead of -E


With perl

$ echo label=\"123 \"456\" 789\" \"AB C\" e f gh | 
    perl -pe 's/(^[^"]*"|"[^"]*$)(*SKIP)(*F)|"/\\"/g'
label="123 \"456\" 789\" \"AB C" e f gh

Here, the string up to first " and string from last " to end of line are skipped while the remaining " are replaced with \"

1

sed to the rescue.

sed 's/"/\x1b"/g;s/\x1b"/"/;s/\(.*\)\x1b"/\1"/'

Replace all quotes with ESC" and then revisit and replace first with just quote, and replace final with just quote. Detailed explanation:

  • s/"/\x1b"/g : replace all quote characters with \x1b (ESC) and quote.

  • s/\x1b"/"/ : replace first \x1b (ESC) and quote combination with single quote.

  • s/\(.*\)\x1b"/\1"/ : replace final \x1b (ESC) and quote combination with single quote

Example output:

$ echo label=\"123 \"456\" 789\" \"AB C\" e f gh |sed 's/"/\x1b"/g;s/\x1b"/"/;s/\(.*\)\x1b"/\1"/'|od -c
0000000   l   a   b   e   l   =   "   1   2   3     033   "   4   5   6
0000020 033   "       7   8   9 033   "     033   "   A   B       C   "
0000040       e       f       g   h  \n
0000050
$
  • the problem is not in the echo, I looking for a filter. Here the echo part is given for illustration a test case – ivand58 Oct 23 '16 at 19:22
0

There is a way to get what you ask for in several steps:
Assuming a variable could contain the string (the string doesn't have single quotes):

$ label='label=\"123 \"456\" 789\" \"AB C\" e f gh'

We could chop out the part after the \":

$ front=${label%\\\"*}
$ echo "$front"
\"123 \"456\" 789\" \"AB C

Then remove up to the first \":

$ mid=${front#*\"}
$ echo "$mid"
123 \"456\" 789\" \"AB C

Replace all \" by \e":

$ final=${mid//\\\"/\\e}
$ echo "$final"
123 \e456\e 789\e \eAB C

And finally, reconstruct the original string and use printf for the escapes:

$ printf "label=\"${front%%\\\"*}$final\"${label#"${front}"}\n"
label="123 456 789 AB C"" e f gh

$ printf "label=\"${front%%\\\"*}$final\"${label#"${front}"}\n" | od -vAn -t x1c
  6c  61  62  65  6c  3d  22  31  32  33  20  1b  22  34  35  36
   l   a   b   e   l   =   "   1   2   3     033   "   4   5   6
  1b  22  20  37  38  39  1b  22  20  1b  22  41  42  20  43  22
 033   "       7   8   9 033   "     033   "   A   B       C   "
  22  20  65  20  66  20  67  68  0a
   "       e       f       g   h  \n

Not the answer you're looking for? Browse other questions tagged or ask your own question.