Why is xkvbd eating newlines when I it is fed from xsel?

1

I have this script that I assign t oa keyboard shortcut to simulate pasting through middle click:

#!/bin/bash
aa=0
for randstring in `xsel`
do
  if [[ "$randstring" =~ [ěščřžýáíéúůóťďň] ]]
  then
      xxx=`xsel|sed 's/ě/\\\[ecaron]/g' |sed 's/š/\\\[scaron]/g' |sed 's/č/\\\[ccaron] g' |sed 's/ř/\\\[rcaron]/g' |sed 's/ž/\\\[zcaron]/g' |sed 's/ý/\\\[yacute]/g' |sed 's/á/\\\[aacute]/g' |sed 's/í/\\\[iacute]/g' |sed 's/é/\\\[eacute]/g' |sed 's/ú/\\\[uacute]/g' |sed 's/ů/\\\[uring]/g'  |sed 's/ó/\\\[oacute]/g' |sed 's/ď/\\\[dcaron]/g' |sed 's/ň/\\\[ncaron]/g' |sed 's/ť/\\\[tcaron]/g'  |sed ':a;N;$!ba;s/\n/\\n/g'`
      xvkbd -text "$xxx" 2>/dev/null
      aa=1
      break
  else
    aa=0
  fi
done
if [[ $aa -eq 0 ]]
    then
        xsel | xvkbd  -file - 2>/dev/null
fi

I use -text with xvkbd when the text is in Czech (my language) because xvkbd does not understand diacritics like ě but only in form like \[ecaron]. Now, with this option, if there is a newline int xsel, it does not get printed with xvkbd. However, when I do

xx="---8<-----\nToday date is: $(date +%Y%m%d)\n---8<-----" 
xvkbd -text "$xx"  2>/dev/null

Newlines do get printed.

I suspect the trouble is in the last sed expression sed ':a;N;$!ba;s/\n/\\n/g', but I do not know how to make it better. I think that I need to take care for \ns somehow?

sup

Posted 2012-04-18T15:07:29.553

Reputation: 527

Answers

1

EDIT: I found the problem, but the rest may still be worth reading for when you actually do want a trailing newline `...

You are missing an extra \\ or even just \

  sed ':a;N;$!ba;s/\n/\\\\n/g'`  

MORE INFO: Regarding the issue of $( ) vs backquotes for command substitution, mentioned a comment I put to this question, here is an excerpt from man bash.

When  the  old-style  backquote form of substitution is used, backslash
retains its literal meaning except when followed by $, `,  or  \.   The
first backquote not preceded by a backslash terminates the command sub‐
stitution.  When using the $(command) form, all characters between  the
parentheses make up the command; none are treated specially.

-- Original Post --

It won't print the last \n but it adds intermediate \n into $xx ...

Here is a simplified version of your last sed call:

printf '%s\n' 'a\[ecaron]' b c '\[rcaron]d' |
   sed ':a;N;$!ba;s/\n/\\n/g' 

Output:

a\[ecaron]\nb\nc\n\[rcaron]d

The reason it doesn't have a final \n (in the above example) is that there simply won't be a trailing newline char in the pattern-space for the lastline (only ones from previous line-endings, via N, will be there)... sed will subsequently output the final newline char as it exits, but even so, it will be gobbled up by the xx=$(command substitution) either by $( ) or backticks ...

To include a final \n, it needs just one final substitution.

sed ':a;N;$!ba;s/\n/\\n/g;s/$/\\n/'

Or is it a case of you are not getting even the intermediate \ns?


Just a side note: You don't need to have umpteen seds calling that many processes. You can concatenate them via ; (colon), eg. sed 's/ě/\\\[ecaron]/g; s/š/\\\[scaron]/g; .... or not even use the colons and just put each substitution expression on a new line.. which allows them to line up nicely...

Peter.O

Posted 2012-04-18T15:07:29.553

Reputation: 2 743

On the side note, it has been pointed to me before I could use sed -e 'bla/bla' -e 'bleble' but the thing about having each expression on a new line is very neat and readable, thanks. Thanks for the answer (even the long one) anyway.

One thing I am still unclear on is why I need so many \? I mean, with s/\n/\\n/g, I would expect it to catch any newline (\n) and replace it with one backslash (\\ would do that) and then print n. Does sed for some reason think I am trying to escape a newline? – sup – 2012-04-18T18:42:32.020

I've just found the real(?) problem this time (I hope)... It is to do with backticks vs $( ) ... your \\n works fine with $( ) but loses the newlines otherwise... (but I'm starting to phase out on this one...need sleep occasionally :) ... It works when I use $( ) ... which, by the way, is a much preferred and encouraged (newer) style.. – Peter.O – 2012-04-18T18:50:16.553

Using \\n (not \\\\n), $( ) works for me, but backticks lose every intermediate newline... There is one more issue though, and that is of having to determine before you process your xsel data whether or not is had a trailing newline, and then selectively add (or not add) that final trailing newline via a sed expression... – Peter.O – 2012-04-18T19:04:42.680

I will remember $(), thanks for that, it works with just two backslashes now. As for the trailing \n, it seems to be keeping it now, so I will let it be. In case I need to test for it, I just do xsel | grep -qE "/n$ and then act accordingly on $?. – sup – 2012-04-18T23:54:36.173