Skip to content

Instantly share code, notes, and snippets.

@gene-pavlovsky
Last active November 5, 2023 14:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gene-pavlovsky/3f6df3bf916ea366e863d5e4943ca367 to your computer and use it in GitHub Desktop.
Save gene-pavlovsky/3f6df3bf916ea366e863d5e4943ca367 to your computer and use it in GitHub Desktop.
Run a program, converting UNIX and Windows format path arguments.
# Example usage: part of my .gitconfig file (Cygwin git)
[diff]
tool = winmerge
[difftool "winmerge"]
cmd = cygrun -w 'C:/Program Files/WinMerge/WinMergeU.exe' -r -u -e -dl \"Local\" -dr \"Remote\" \"$LOCAL\" \"$REMOTE\"
[difftool "diffmerge"]
cmd = cygrun -w 'C:/Program Files/SourceGear/Common/DiffMerge/sgdm.exe' \"$LOCAL\" \"$REMOTE\"
[difftool]
prompt = false
[merge]
tool = winmerge
[mergetool]
prompt = false
keepBackup = false
[mergetool "winmerge"]
trustExitCode = true
cmd = cygrun -w 'C:/Program Files/WinMerge/WinMergeU.exe' -r -u -e -dl \"Local\" -dr \"Remote\" \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"
[mergetool "diffmerge"]
trustExitCode = true
cmd = cygrun -w 'C:/Program Files/SourceGear/Common/DiffMerge/sgdm.exe' --merge --result=\"$MERGED\" \"$LOCAL\" \"$BASE\" \"$REMOTE\"
#!/bin/bash
#
# Run a program, converting UNIX and Windows format path arguments.
#
# Install in Cygwin's `bin` dir or elsewhere in your path.
# Create a symlink for convenience: `ln -s cygrun.sh cygrun`.
#
# Use `cygrun -w windows-program unix-path ...` to run Windows programs (e.g. from UNIX software).
# E.g., in my .gitconfig core.editor is set to `cygrun -w 'C:/Program Files/Notepad2/Notepad2.exe'`.
#
# Use `cygrun [-u] unix-program windows-path ...` to run Cygwin programs (e.g. from Windows software).
# Usually not needed since Cygwin translates Windows paths automatically (e.g. `ls 'C:\Windows'` works).
# Still it can be useful in case a Windows program might produce paths that mix backslashes and forward slashes,
# e.g. in Apache Ant `${dist.dir}/file` evaluates to `C:\path\to\project\dist/file`, which Cygwin won't
# translate automatically. So, to deploy the file, I use `cygrun -u scp ${dist.dir}/file ${deploy.url}`.
#
# Use the `-b` option to detach from tty, run the process in background, and return immediately.
#
# There is currently a limitation on passing non-path arguments to a program. Since there is no way
# to automatically distinguish path arguments from non-path arguments, all of the arguments are passed
# to `cygpath`. As long as non-path arguments don't contain `/` or `\` characters, and `-a` or `-C`
# options are not used, `cygpath` will return the arguments unmodified - no problem.
# Due to this, the `-a` and `-C` options are disabled at the moment.
# TODO: Add an option allowing to specify index positions of path arguments (comma-separated, support ranges).
#
# Most of the options are passed as is to `cygpath`, which is used for path conversion.
set -e -o pipefail
usage() {
local code=${1:-2}
test $code -ne 0 && exec >&2
echo "Usage: $(basename "$0" .sh) [options] program [argument]..."
echo
echo 'Run a program, converting UNIX and Windows format path arguments.'
echo
echo 'Output type options:'
echo
echo ' -d, --dos print DOS (short) form of NAMEs (C:\PROGRA~1\)'
echo ' -m, --mixed like --windows, but with regular slashes (C:/WINNT)'
echo ' -u, --unix (default) print Unix form of NAMEs (/cygdrive/c/winnt)'
echo ' -w, --windows print Windows form of NAMEs (C:\WINNT)'
echo
echo 'Path conversion options:'
echo
# echo ' -a, --absolute output absolute path'
echo ' -U, --proc-cygdrive Emit /proc/cygdrive path instead of cygdrive prefix'
echo ' when converting Windows path to UNIX path.'
echo ' -c, --codepage CP print DOS, Windows, or mixed pathname in Windows'
echo ' codepage CP. CP can be a numeric codepage identifier,'
echo ' or one of the reserved words ANSI, OEM, or UTF8.'
echo ' If this option is missing, cygrun defaults to the'
echo ' character set defined by the current locale.'
echo
echo "Other 'cygpath' options:"
echo
echo ' -f, --file FILE read FILE for input; use - to read from STDIN'
echo " -o, --option read 'cygpath' options from FILE as well (for use with --file)"
echo ' -h, --help output usage information and exit'
echo
echo 'Run options:'
echo
# echo ' -c, --directory DIR change to directory DIR (implies -a)'
echo ' -b, --background run in a new session'
echo ' -n, --dry-run print the command that would be executed, and exit'
exit $code
}
run() {
local -n _run_args=$2
if test "$dry_run"; then
test "$chdir" && printf 'cd %s\n' "$chdir"
printf '%s\n' "$1 ${_run_args[*]}"
else
test "$chdir" && cd -- "$chdir"
#printf '[debug] %s\n' "$1 ${_run_args[*]}" >&2 # DEBUG
if test "$background"; then
setsid "$1" "${_run_args[@]}" </dev/null &>/dev/null &
else
exec "$1" "${_run_args[@]}"
fi
fi
}
declare opts fileopts chdir background dry_run
while test $# -gt 0; do
case $1 in
# -d|--dos|-u|--unix|-m|--mixed|-w|--windows|-a|--absolute|-U|--proc-cygdrive|-o|--option)
-d|--dos|-u|--unix|-m|--mixed|-w|--windows|-U|--proc-cygdrive|-o|--option)
opts="${opts} $1"
;;
-C|--codepage|-f|--file)
opts="${opts} $1 $2"
shift
;;
-f|--file)
fileopts=1
opts="${opts} -i $1 $2"
shift
;;
# -c|--directory)
# shift
# chdir=$1
# ;;
-b|--background)
background=1
;;
-n|--dry-run)
dry_run=1
;;
-h|--help)
usage 0
;;
--)
shift
break
;;
-*)
script="$(basename "$0" .sh)"
echo "$script: Unknown option: $1" >&2
echo "Try '$script --help' for more information." >&2
exit 2
;;
*)
break
;;
esac
shift
done
test "$chdir" && opts="$opts -a"
program=$1
test "$program" || usage
shift
declare -a args
#test $# -gt 0 -o "$fileopts" && echo '[debug]' mapfile -t args \< \<\(cygpath $opts -- \""$@"\" \|\| exit 1\) >&2 # DEBUG
test $# -gt 0 -o "$fileopts" && mapfile -t args < <(cygpath $opts -- "$@" || exit 1)
run "$program" args
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment