Flethuseo
Flethuseo

Reputation: 6189

Escaping backslash in windows paths passed to unix programs

I am trying to escape backslashes in cygwin, but it seems almost impossible I have tried a lot of things, but none work right..

  echo "C:\Users\Ted\Documents\Unix\Scripts" | xargs echo
  echo 'C:\Users\Ted\Documents\Unix\Scripts' | xargs echo

More specifically I need to get a command to receive input in bash without losing the backslash characters. every time I try to pass an argument, the backslashes always disappear, destroying my input. And I don't know how I can tell it to just leave the backslashes on the input alone.

I have tried the following but neither seems work

  alias cyg0='cygpath '$*'  '
  alias cyg1='cygpath "$*"  '
  alias cyg2='cygpath "'$*'"'

  alias cyg3='cygpath '$@'  '
  alias cyg4='cygpath "$@"  '
  alias cyg5='cygpath "'$@'"'


  Ted@Machine01 ~
  $ cyg0 C:\Users\Ted\Music\Enigma
  C:UsersTedMusicEnigma

  Ted@Machine01 ~
  $ cyg1 C:\Users\Ted\Music\Enigma
  cygpath: can't convert empty path

  Ted@Machine01 ~
  $ cyg2 C:\Users\Ted\Music\Enigma
  cygpath: can't convert empty path

  Ted@Machine01 ~
  $ cyg3 C:\Users\Ted\Music\Enigma
  C:UsersTedMusicEnigma

  Ted@Machine01 ~
  $ cyg4 C:\Users\Ted\Music\Enigma
  C:UsersTedMusicEnigma

  Ted@Machine01 ~
  $ cyg5 C:\Users\Ted\Music\Enigma
  cygpath: can't convert empty path

By the way, I want to be able to type C:\Users\Ted\Music\Enigma without quotes. One of those aliases works when using quotes.

Ted

Upvotes: 12

Views: 31275

Answers (6)

newby2000
newby2000

Reputation: 142

Sometimes it is useful to give manuals of the used programs a try :-) . From man xargs:

OPTIONS
   -0, --null
          Input items are terminated by a null character instead  of  by
          whitespace,  and the quotes and backslash are not special (ev-
          ery character is taken literally).  Disables the end  of  file
          string, which is treated like any other argument.  Useful when
          input items might contain white space, quote marks,  or  back-
          slashes.   The GNU find -print0 option produces input suitable
          for this mode.

This option indeed preserves the whole line as is. Try:

echo 'C:\Users\Ted\Documents\Unix\Scripts' | xargs -0 echo

The "-0" does it for you!

Note that for more than one arg you must zero-terminate the arguments, for example by find ... -print0 or grep ... --null.

Upvotes: 7

CrashNeb
CrashNeb

Reputation: 484

I recognize this answer is quite late to the party... but I stumbled across this question while having the same trouble myself.

I was using 'xargs' from a Cygwin installation - AND - I was invoking it from a regular cmd.exe shell.

For me, I simply used the tr utility that is also in the Cygwin folder:

dir /b /s /a *.txt | tr "\\" "/" | xargs ...

This completely solved my "slash problem".

Upvotes: 5

lkisac
lkisac

Reputation: 2137

Try:

echo "C:\Users\Ted\Documents\Unix\Scripts" | xargs -0 echo
echo 'C:\Users\Ted\Documents\Unix\Scripts' | xargs -0 echo

xargs -0 option explanation (from gnu.org):

"Input file names are terminated by a null character instead of by whitespace, and any quotes and backslash characters are not considered special (every character is taken literally). Disables the end of file string, which is treated like any other argument."

OR

You can also achieve this with sed:

echo "C:\Users\Ted\Documents\Unix\Scripts" | sed 's/\\/\\\\/g' | xargs echo
echo 'C:\Users\Ted\Documents\Unix\Scripts' | sed 's/\\/\\\\/g' | xargs echo

This will replace all single \ with double \\ before it is passed to xargs where the double \\ will then be stripped back to single \, instead of the original case of single to none.

sed is a useful stream editor tool that can be used for simple text replacement. Here's a tutorial with some good examples.

Syntax for substitution is as follows:

s/string_to_replace/replace_with/g

  • The g modifier is used to replace all occurrences. Without it, only the first match would be replaced.
  • The extra backslashes in sed 's/\\/\\\\/g' are used to escape each \ character's special meaning. So, \\ would be the literal character \, and \\\\ would be \\.

For no quotes, you would have to use \\ instead of \, as the marked answer suggests.

 echo C:\\Users\\Ted\\Documents\\Unix\\Scripts | xargs echo

Upvotes: 2

Urthona
Urthona

Reputation: 21

I had the same problem, when converting filenames like c:\dirname to unix using cygpath I was getting c:dirname, and even weirder stuff.

Turns out, though, that my problem was the filenames I was using were input by users which I was retrieving with the "read" command. The read, not cygpath, was causing my problems by throwing out the backslash. "read -r" fixed the problem.

Upvotes: 2

Vittorio Muth
Vittorio Muth

Reputation: 51

I had a similar problem when reading a path from windows explorer pasted into command line:

echo "Please enter path (copy n paste from Win-Explorer):"
read -r myWinPath;

The "read -r xyz" command (with arg. "-r") does not interprete single backslashes in the pasted path.

myNewCygwinPath=`cygpath -a $myWinPath`;
echo $myNewCygwinPath;

Vittorio

Upvotes: 5

Andrew Schulman
Andrew Schulman

Reputation: 3544

Please read the section titled QUOTING in bash(1) (man bash).

In your first example,

echo "C:\Users\Ted\Documents\Unix\Scripts" | xargs echo

the shell interprets (and then removes) backslashes within double quotes, so the first echo command only sees the string C:UsersTedDocumentsUnixScripts.

In your second example,

echo 'C:\Users\Ted\Documents\Unix\Scripts' | xargs echo

the single quotes correctly protect the backslashes, as you can see by running just the first echo command, without the | xargs echo.

In all of your remaining examples,

cyg0 C:\Users\Ted\Music\Enigma
cyg1 C:\Users\Ted\Music\Enigma
...

since the argument (C:\Users\Ted\Music\Enigma) is unquoted, once again the shell interprets and removes the backslashes before the command (cyg0, cyg1, ...) ever sees them. And...

I want to be able to type C:\Users\Ted\Music\Enigma without quotes.

Sorry, it can't be done. If you don't put the backslashes in single quotes, the shell will interpret each one as a quoting character for the character that follows it, then remove the backslash.

You have a few options:

  1. Use single quotes on the command line, to protect the backslashes:

    cyg4 'C:\Users\Ted\Music\Enigma'
    
  2. Quote each backslash character separately, by doubling it, e.g.

    cyg4 C:\\Users\\Ted\\Music\\Enigma
    
  3. Use forward slashes instead:

    cyg4 C:/Users/Ted/Music/Enigma
    

Option 3 could really be your best solution. Unfortunately the backslash character is just a very special character for all Unix shells, and this causes a lot of quoting hassles when dealing with DOS/Windows paths. But what's not widely known is that DOS/Windows is also perfectly happy to use the forward slash (/) as its path separator. Try it.

Note: Of your several cyg* functions, only cyg1 and cyg4 are correct. The others use incorrect quoting. cyg1 is only useful with one argument since it joins all of the arguments together into one quoted string, while cyg4 can accept and pass on multiple arguments to cygpath. However, since cyg4 only passes on its quoted arguments, it doesn't add any value; it's really no different from just e.g.

cygpath C:/Users/Ted/Music/Enigma

Good luck.

Upvotes: 19

Related Questions