msys path conversion (or cygpath for msys?)
Asked Answered
C

9

44

I need to pass /DEF:c:\filepath\myLib.def" command line option from a bash script to MS compiler/linker. The path is generated as part of build process by a bash script. Basically, the argument that my script passes is:

-DEF:/c/filepath/myLib.def

MSYS path conversion can't handle it properly because it doesn't understand /DEF: part. It works if I do

-DEF=/c/filepath/myLib.def

but then ms tools don't understand this parameter. In short, what's the proper way to write that parameter in MSYS bash so that it converts it to proper argument?

On cygwin I could use cygpath, but there is no equivalent, because somebody from msys thinks that it's not needed (even if there are scripts for cygwin that uses cygpath).

Cribbing answered 18/8, 2012 at 2:25 Comment(5)
The "not needed" argument can be stretched a long way. But sometimes it's useful, for example with Jython's shell script allowing for Cygwin but not MSYS. Jython's shell script calls cygpath. If we add cygpath.exe from @user1307996's answer into MSYS bin, it works.Absentee
Both directions handled here: https://mcmap.net/q/24061/-windows-path-to-posix-path-conversion-in-bash/321973Print
@TobiasKienzler Try to convert \Windows to posix using these hairy scripts. It makes no sense not to use cygpath on cygwinCribbing
of course not, unless you don't have cygpath available...Print
I'm voting to close this question as off-topic because the problem described has been solved in a later version of the software - msys2 includes cygpath.Merla
C
60

Update (Aug-2016):

This question is no longer relevant, as msys2 now comes with cygpath in its installation.

...

I'll summarize my research here.

The cygpath equivalent in MSYS is to use this command:

{ cd /c/some/path && pwd -W; } | sed 's|/|\\|g'

The problem with this approach is that it requires existing path, e.g. the c:\some\path has to be an existing directory; however, real cygpath supports paths that do not exist.

So, if you need to get path to a directory that doesn't exist, then you can fallback to sed conversion of the path:

{ cd 2>/dev/null /c/some/path && pwd -W ||
  echo /c/some/path | sed 's|^/\([a-z,A-Z]\)/|\1:/|'; } | sed 's|/|\\|g'

The mouthful of slashes is there to satisfy quoting rules of sed. So, if c:\some\path doesn't exist on your PC, it will try to convert forward to back slashes and replace /c/ with c:\ (or any other drive letter). The only drawback for this is that it won't work correctly non-existing paths that contain a mounted component, such as /bin/does-not-exist or /usr/bin/does-not-exist.

One more approach is to use cygpath from cygwin in MSYS. It seems that cygwin sets global environment variable CYGPATH, that is, you can use it from regular cmd.exe:

%CYGPATH% -w /c/some/path
C:\some\path

or from MSYS:

$CYGPATH -w /c/some/path
C:\some\path

as long as you set to point /c to /cygdrive/c in cygwin. But this approach will print you /usr located in cygwin installation, not in MSYS.

In short, I think msys should really include real cygpath in the default set of tools just for some cases that aren't handled automatically by msys command line argument conversion logic

Cribbing answered 21/8, 2012 at 21:57 Comment(6)
There's no need to escape backslashes if you are already using single-quotes.Hagy
Excellent answer, however if your sed script is modified to change ([a-z]) to ([a-z,A-Z]) , then capital drive letters may be used too.Convexoconcave
Also, by removing the forwars slashes at the end of the first sed script it allows something like /c to be converted to C: instead of \c.Convexoconcave
@Convexoconcave But then /abc will also get converted to a:bc or a:\bc depending on what /a gets converted to.Cribbing
The sed part is imprecise - pwd -W generates uppercase drive letter whereas sed keeps the lowercase one (yes, this does matter sometimes).Fulvia
The issue with any sed approach is that it depends on "only get paths that have the "/x/" form. But there can be different mount points that don't match this, most important ones are "/mingw" and "/home/". The best option is to always use pwd -W for the first part of the entry, then convert /->\ and uppercase the first character (pwd -W converts according to the mounts and may also use lowercase letters).Drubbing
S
14

use pwd -W

or download cygpath for msys from here http://mingw.5.n7.nabble.com/enhanced-version-of-cygpath-td28556.html

and use cygpath -wa

Staub answered 28/8, 2013 at 12:36 Comment(3)
How is it different from my own answer?Cribbing
link I provided points to cygpath.exe that is probably different from cygwin one.Staub
The link provided provides a cygpath that doesn't work properly. E.g. cygpath -w /c/foobar prints c:/foobar while it should print c:\foobar.Hatley
A
8

Similar to dmitri-rubinstein@ above, I've cleaned up the code a bit and added the reverse conversion as well.

winpath() {
    if [ ${#} -eq 0 ]; then
        : skip
    elif [ -f "$1" ]; then
        local dirname=$(dirname "$1")
        local basename=$(basename "$1")
        echo "$(cd "$dirname" && pwd -W)/$basename" \
        | sed \
          -e 's|/|\\|g';
    elif [ -d "$1" ]; then
        echo "$(cd "$1" && pwd -W)" \
        | sed \
          -e 's|/|\\|g';
    else
        echo "$1" \
        | sed \
          -e 's|^/\(.\)/|\1:\\|g' \
          -e 's|/|\\|g'
    fi
}

unixpath() {
    echo "$1" \
    | sed -r \
      -e 's/\\/\//g' \
      -e 's/^([^:]+):/\/\1/'
}
Arched answered 1/10, 2014 at 4:36 Comment(0)
A
6

I am using this with msysgit:

winpath() {
    if [ -z "$1" ]; then
        echo "$@"
    else
        if [ -f "$1" ]; then
            local dir=$(dirname "$1")
            local fn=$(basename "$1")
            echo "$(cd "$dir"; echo "$(pwd -W)/$fn")" | sed 's|/|\\|g';
        else
            if [ -d "$1" ]; then
                echo "$(cd "$1"; pwd -W)" | sed 's|/|\\|g';
            else
                echo "$1" | sed 's|^/\(.\)/|\1:\\|g; s|/|\\|g';
            fi
        fi
    fi
}
Auer answered 15/3, 2013 at 14:10 Comment(0)
W
3

My bash foo is weak and I couldn't get regexes working in bash 3.1 so I hacked out a perl script for it:

#!/bin/env perl
use strict;

my @r;
foreach my $e (@ARGV) {
 $e=~s/\//\\/g;
 $e=~s/^\\([A-Za-z])\\/\1:\\/;
 push @r, $e;
}

print join(" ", @r);
Ware answered 21/8, 2012 at 19:22 Comment(1)
I know how to do that using scripts; I think that there is some MSYS specific way to quote arguments so that it converts this path automatically. That's what I'm looking for.Cribbing
S
3

MSYS cygpath

Program

This program convert a DOS path to a UNIX path and vice versa

#!/bin/env perl
# DOS to UNIX path conversion
# © John S. Peterson. License GNU GPL 3.
use strict;
use Getopt::Std;

# usage
if ($#ARGV == -1) {
    print 'Usage: cygpath (-w) NAME...

Convert Unix and Windows format paths

Output type options:

  -w, --windows         print Windows form of NAMEs (C:\WINNT)
';
    exit 0;
}

# option
my %opt;
getopts('w', \%opt);

# convert path
my @r;
foreach my $e (@ARGV) {
    if ($opt{w}) {
        # add drive letter suffix
        $e =~ s,^\/([A-Za-z])\/,\1:\/,;
        $e =~ s,\/,\\,g;

    } else {
        $e =~ s,\\,\/,g;
        # add leading slash
        $e = "/$e";
        # remove drive letter suffix
        $e =~ s,:,,;
    }

    push @r, $e;
}

print join("\n", @r);

Compared to Cygwin cygpath

The output from this program is better than the output from Cygwin cygpath in MSYS because

  • Cygwin cygpath remove the Cygwin home from a converted path, f.e.
cygpath "$CYGWIN/usr/local/bin"
/usr/local/bin

which is a problem because

  • it's sometimes useful to convert a DOS Cygwin path to a UNIX path for the purpose of copying files from Cygwin to MSYS

This program doesn't remove the Cygwin home

cygpath "$CYGWIN/usr/local/bin"
/c/file/program/cygwin/usr/local/bin

Compared to automatic MSYS path conversion

Manual path conversion has a use in MSYS because

  • the automatic path conversion is inadequate

for f.e.

Simpatico answered 13/7, 2013 at 19:28 Comment(0)
D
3

How about this one ? cmd //c echo <your path>

It may not work always but it is the shortest I found

Dekameter answered 19/11, 2015 at 13:49 Comment(2)
Solution based on this how to pass it from a Makefile is provided in stackoverflow.com/questions/40257679/…Ubiquitous
Commented there that the syntax may actually be causing issues for me, and a slight change to cmd /c echo ... may work better.Mateusz
D
0

This works for me

df "$1" | tac >k
read b <k
rm k
set "$1" $b
echo ${1/$7/$2/}

ref

Daphne answered 30/1, 2013 at 8:11 Comment(1)
Did you try my own solution: sh -c 'cd /c/some/path; pwd -W'? it's functionally equivalent to cygpath -w '/c/some/path' on cygwin. Your solution maybe has that functionality of cygpath, but it's not very clear how to translate it to cygpath-like command.Cribbing
D
-1

nearly pure GNU bash solution (which is what you commonly run in MSYS) (interestingly not working with MSYS2, leave a comment if you know why):

#!/bin/bash

# check if we have cygpath (cygwin, newer MSYS2), then just use that 
which cygpath 1>/dev/null 2>&1
[[ $? = 0 ]] && (cygpath -wa "$1"; exit $?)

# check if it looks like a Windows path, in which case we directly convert and exit
[[ ${1} =~ ^[a-zA-Z]: ]] && \
  echo "${1}" | sed -e 's|/|\\|g' -e 's/\(.\)/\u\1/' && exit 0

# split first path entry (if any) with trailing slash and filename
[[ ${1} =~ ^\([/a-zA-Z0-9_.-]\\w*/\)?\(.*\)$ ]]

chk_root="${BASH_REMATCH[1]}"
chk_rest="${BASH_REMATCH[2]}"

# check if the root path exists and more important: let pwd binary resolve the translation according to the mount
chk_winroot="$(cd "${chk_root}." 2>/dev/null && pwd -W)"
[[ "${chk_winroot}" == "" ]] && echo "${chk_root}: No such file or directory" && exit 1

# using substition to replace all / by \ and uppercasing the first character
# pure bash solution; sadly: the first part needs a newer bash than old MSYS have ...
# chk_drv="${chk_winroot:0:1}"
# chk_all="${chk_winroot:1}/${chk_rest}"
# echo "${chk_drv^^}${chk_all//\//\\}"

# ... so fallback to GNU sed
echo "${chk_winroot}/${chk_rest}" | sed -e 's|/|\\|g' -e 's/\(.\)/\u\1/'

There's still an issue with it: if MinGW's fstab contains an entry like /mnt/c the pwd -W of /mnt/. done in this script won't work.
To fix it: replace pwd -W by inspecting $ cat /etc/fstab | cut -d'#' -f1 | grep -v "^\s*$" entries manually and replace the first match - while this will never work for cygwin or msys2 which use a different format this is covered by using cygpath there.

Drubbing answered 5/4, 2022 at 16:16 Comment(2)
Is there any reason to use it now when MSYS2 ships cygpath?Missis
Yes, if you use the original MSYS (or a very old MSYS2). Because of completely different dependencies and missing support for "old" Windows versions one may still want to use MSYS/MinGW instead of MSYS2's environments.Drubbing

© 2022 - 2024 — McMap. All rights reserved.