48
votes

I'd like to run a long rsync command in Cygwin by double clicking on a .sh file in Windows. It must start in the file's containing directory (e.g. /cygdrive/c/scripts/) so that relative paths work. Anyone gotten this to work?

Note: I've just found here, a Cygwin package that manages Windows context menus (Bash Prompt Here). It might have some clues.

13

13 Answers

41
votes

Ok, I've found something that works. Associating a batch file as Vladimir suggested didn't work, but the bash arguments were key.

Short and sweet: associate with this command: "C:\cygwin\bin\bash.exe" -li "%1" %*

Long version if you don't know how:

  1. In Explorer, go to Tools/Folder Options/File Types.
  2. I already had an SH entry for Bash Script. If you don't have one, click New and enter "SH" to create one.
  3. With the SH extension selected, click Advanced.
  4. Choose the "open" action and click edit (or create the action).
  5. This is the command to use: "C:\cygwin\bin\bash.exe" -li "%1" %*. Note that without the -li, it was returing "command not found" on my scripts.

You may also want to add SH to your PATHEXT environment variable:

WinKey+Pause / Advanced / Environment Variables / System Variables / PATHEXT

Thanks for your help, guys!

16
votes

Here is my solution. It works well for my *.sh scripts regardless of where they are in the directory hierarchy. Notice that I cd to the cygpath dirname before calling bash on the cygpath. It just works.

assoc .sh=bashscript

ftype bashscript=C:\cygwin\bin\bash.exe --login -i -c 'cd "$(dirname "$(cygpath -u "%1")")"; bash "$(cygpath -u "%1")"'
6
votes

I've been working with Dragos' solution for some time now and I regard it as the best one because it eleminates the need to use "cygpath -u" inside your shell scripts.

I then wanted to have additional features like drag&drop support for .sh and .bash files. After some digging around I wrote a .bat that associates .sh and .bash files as "bashscript" and activates the Windows Explorer drag&drop handler for them. I had to edit Dragos' command to make it handle 1 argument (the path to the item dropped on a shell script).

The .bat file roughly goes as follows:

echo Registering .sh and .bash files as "bashscript"...
assoc .sh=bashscript
assoc .bash=bashscript
echo.
echo Setting the run command for the file type "bashscript"...
ftype bashscript=C:\cygwin\bin\bash.exe --login -i -c 'cd "$(dirname "$(cygpath -u "%%1")")"; bash "$(cygpath -u "%%1")" "$(/argshandler.sh "%%2")"'
echo.
echo Activating the drag^&drop capability for "bashscript" files (only 1 dropped item
echo will be passed to the script, multiple items are not supported yet)...
reg add HKEY_CLASSES_ROOT\bashscript\shellex\DropHandler /v "" /t REG_SZ /d "{60254CA5-953B-11CF-8C96-00AA00B8708C}" /f

The "argshandler.sh" script in the Cygwin root just cygpaths back the first argument it receives and nothing at all if there aren't any (e.g. if you just double click on a script file):

#!/bin/bash
if [ ! "$1" == "" ]
then
    cygpath -u "$1"
fi

All this workes quite nicely so far. However, there are still some drawbacks that would be nice to be resolved:

  • Dragos' command and my derivative of it fail when it comes to scripts that are located on UNC paths, e.g. \\myserver\myshare\scriptfile.sh
  • Only 1 dropped item will be passed to the shell script.

Somehow, concerning the 1-dropped-item-only issue, changing the argument handler script to give back something like

"cygpathed-arg1" "cygpathed-arg2" "cygpathed-arg3"

and changing the setter of Dragos' command to something like

...; bash "$(cygpath -u "%%1")" $(/argshandler.sh "%%2" "%%3" ... "%%9")'

(note that the "" around the argshandler.sh part are gone) does not seem to work properly: If some of the items dragged onto a script contain a blank in their path, said paths will be broken up into multiple arguments at the blanks even though each of them is enclosed in double quotes ... weird.

Are there any command line professionals who feel up to it to solve one or both of these issues?

2
votes

This doesn't associate .sh files, but it might get you what you want. I started with the cygwin.bat batch file that launches the Cygwin bash shell, and modified it like so:

$ cat test.bat
@echo off

set MYDIR=C:\scripts

C:\cygwin\bin\bash --login -c "cd $MYDIR && echo 'Now in' `pwd`; sleep 15"

That's a toy script but you could modify it to call rsync or call a separate shell script. I admit that it would be nicer if it didn't have MYDIR hard coded. There's probaby a way do get it to automagically set that.

Oh yeah, when I created the .bat file in a bash shell in Cygwin, I noticed I had to actually "chmod +x test.bat" before I could launch it with a double-click. I think it's setting NTFS permissions. You wouldn't need to do that if you just used notepad.

2
votes

This is the command I'm using:

"C:\cygwin\bin\mintty.exe" -w max -h always -t "%1" -e /bin/bash -li -c 'cd "$(dirname "$(cygpath -u "%1")")" && bash "$(cygpath -u "%1")"'

It runs it in mintty, maximised, sets the window title to the script being ran (Windows path to it), changes directory to where the script is, runs it and stays open after it completes.

Alternatively, this will set the title to the cygwin path to the script:

"C:\cygwin\bin\mintty.exe" -w max -h always -t "%1" -e /bin/bash -li -c 'printf "\033]0;$(cygpath -u "%1")\007" && cd "$(dirname "$(cygpath -u "%1")")" && bash "$(cygpath -u "%1")"'

Batch scripts to set the association for you:

Windows path in title:

@echo off
assoc .sh=shellscript
ftype shellscript="C:\cygwin\bin\mintty.exe" -w max -h always -t "%%1" -e /bin/bash -li -c 'cd "$(dirname "$(cygpath -u "%%1")")" ^&^& bash "$(cygpath -u "%%1")"'
pause

And cygwin path in title:

@echo off
assoc .sh=shellscript
ftype shellscript="C:\cygwin\bin\mintty.exe" -w max -h always -t "%%1" -e /bin/bash -li -c 'printf "\033]0;$(cygpath -u "%%1")\007" ^&^& cd "$(dirname "$(cygpath -u "%%1")")" ^&^& bash "$(cygpath -u "%%1")"'
pause
2
votes

After looking around different places. What I have managed to come up with is, first select C:\cygwin64\bin\mintty.exe from the windows "Open with..." dialog Then edit the registry value of

[Computer\HKEY_CLASSES_ROOT\Applications\mintty.exe\shell\open\command]

to,

C:\cygwin64\bin\mintty.exe -t "%1" /bin/bash -l -i -c "v1=\"$(cygpath -u \"%0\" -a)\" && v2=\"$(dirname \"$v1\")\" && cd \"$v2\" ; exec bash  \"%1\" %*"  
1
votes

You should be able to associate .sh files with \CYGWIN\usr\bin\bash.exe. The script will have to change its own working directory, I suggest sticking something like this at the top:

cd `dirname "$0"`
1
votes

I use PuttyCyg (awesome putty in Cygwin window) here's how to get it all going:

Create a batch script, eg. on my machine I used

C:\Dev\scripts\cygbashrun.bat

with contents

SET CYGWIN=nodosfilewarning
C:\Cygwin\bin\putty.exe -cygterm /bin/bash.exe %1

Obviously adapt to contain the paths of your install of PuttyCyg.

Then in Windows File Explorer go to Tools - Folder Options - File Types

Create a ".sh" entry if there isn't already (or .bash depending on what you like your scripts to have).. then Advanced..

[optional step] change the icon and select the Cygwin icon from your install

Then:

  1. New..
  2. Action = Run Bashscript..
  3. Application used to perform this action = C:\Dev\scripts\cygbashrun.bat "%1"

Works like a charm for me :O)

1
votes
    Windows Registry Editor Version 5.00
    ;File:ConfigureShToBeRunUnderExplorer.reg v:1.0 docs at the end
    [HKEY_CLASSES_ROOT\Applications\bash.exe] 

    [HKEY_CLASSES_ROOT\Applications\bash.exe\shell]

    [HKEY_CLASSES_ROOT\Applications\bash.exe\shell\open]

    [HKEY_CLASSES_ROOT\Applications\bash.exe\shell\open\command]
    @="C:\\cygwin\\bin\\bash.exe -li \"%1\" %*"

    ; This is a simple registry file to automate the execution of sh via cygwin on windows 7, might work on other Windows versions ... not tested 
    ; you could add this setting by issueing the following command: reg import ConfigureShToBeRunUnderExplorer.reg 
    ; Note the path of your bash.exe
    ; Note that you still have to add the .sh to your %PATHTEXT%
            ; usage: double - click the file or reg import file 
0
votes

One solution that works is to create a .bat file that will open cygwin and execute your script.

The script to execute the script go.sh located on my home directory:

@echo off

C:
chdir C:\cygwin\bin

bash --login -i ./go.sh
0
votes

I just didn't bother. I associated .sh files with Crimson Editor (since I spend as much time working out the bugs as I do actually running them). Now it's a matter of getting the right "open with/edit with" combination to work in File Types>Advanced. If I knew what DDE code Crimson Editor used, that would make things easier; as of this post I've not been able to find it, however.

This reminds me of my Mac days (1993-2008) when I used to try and scan applications for more than rudimentary AppleScript scriptability.

BZT

0
votes

I developed a .bat script on my own (not originated from other's answer) to associate a file type (e.g. *.cygwin) to open with this .bat, as follows:

=== file run-script-with-Cygwin-in-same-dir.bat ===

@echo off
REM   Info: A script created by Johnny Wong.  (last modified on 2014-7-15)
REM   It is used to pass a file argument to run a bash script file.  The current directory is setting to the path of the script file for convenience.
REM   Could be copied to C:\cygwin;  and then you manually associate .cygwin file extension to open with this .bat file.
set CYGWIN=nodosfilewarning

C:\cygwin\bin\bash --login -i -c 'cd "`dirname "%~1"`"; exec bash "%~1" %2 %3 %4 %5 %6 %7 %8 %9'

REM finally pause the script (press any key to continue) to keep the window to see result
pause

=== file run-script-with-Cygwin-in-same-dir.bat ===

Detail explanations on syntax used (if you are interested) :

  1. %1 is "..." quoted if associated a file to open with this .bat. For dragging a file to this .bat, it is "..." quoted only if the file's path has spaces.
  2. %~1 is same as %1 with surrounding double-quotes eliminated, if they exist
  3. to remove surrounding double-quotes from %p%, use for %%a in (%p%) do set p=%%~a
  4. you must use "%~1" to force the script file's path double-quoted, so that its folder separators '\' (in %1) won't be removed by bash when being treated as escape characters. Otherwise, it does not work when dragging a file, which has no spaces in its path, to this .bat.
  5. "exec bash" can be just "bash", the former is for saving resources for one more bash process.

Enjoys :)

0
votes

Look at the assoc and ftype commands in a dos box. Here's an example for .jpg on my machine

c:\>assoc .jpg
.jpg=jpegfile

c:\>ftype jpegfile
jpegfile="C:\Program Files\Common Files\Microsoft Shared\PhotoEd\PHOTOED.EXE" "%1"

assoc .sh=bashscript

ftype bashscript="c:\cygwin\bin\bash.exe" "%1"

Make sure you change the path to bash in the ftype command to match where you have cygwin installed