Skip to content

Instantly share code, notes, and snippets.

@gene-pavlovsky
Last active January 4, 2019 21:48
Show Gist options
  • Save gene-pavlovsky/e81c8e18327540f70df4e5296ff3dd15 to your computer and use it in GitHub Desktop.
Save gene-pavlovsky/e81c8e18327540f70df4e5296ff3dd15 to your computer and use it in GitHub Desktop.
Cygwin: run a Cygwin or Windows program with pathname arguments converted to UNIX or Windows format.
#!/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"
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" && 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