g.bat to jump between folders - need help to make it pure batch!

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
misol101
Posts: 475
Joined: 02 May 2016 18:20

g.bat to jump between folders - need help to make it pure batch!

#1 Post by misol101 » 13 May 2016 06:28

Hi, some time ago I made g.bat. This is based on an old dos program I used to have which no longer works.

What is does is create a text database of all folders (on one or more drives), and then search that database to quickly jump to where you want to go.

Example:
g fold

would jump to the first folder in the database containing the string "fold". If you were already in such a folder, it wold jump to the next one instead.

If you knew that the folder you want to jump to was inside another folder called e.g. "mine", you could do:

g ne\fold or g mine/fold
to make it more likely to get to the right spot.

Often g.bat can really save time instead of using cd to get around.

To create or update database, type g * [drives(default c)] e.g.
g * cd (creates database of folders in drives C and D)


So anyway this script uses a bunch of external tools, and I was wondering if you guys could help me out to make it pure Batch? I suppose the different replace utilities I have seen here will be useful. Also, hopefully without losing speed.

Here is the script:

Code: Select all

@echo off
if "%~1" == "" echo.&echo Usage: g [input]&echo        g ^* [drives(default c)] to create/update folder database&goto :eof
set GTMP=
if not "%TMP%" == "" set GTMP=%TMP%
if not "%TEMP%"=="" set GTMP=%TEMP%
if "%GTMP%" == "" echo Set TMP environment variable to temporary folder first.&goto :eof
if not "%~1" == "*" goto GODIR
  echo "%CD%">%GTMP%\tmp-cd.dat
  set ARG=c&if not "%~2" == "" set ARG=%~2
  del /Q %GTMP%\go.dat 2>nul
  echo Updating go database...
  :DBLOOP
  set DRV=%ARG:~0,1%
  set ARG=%ARG:~1%
  cd /D %DRV%: 2>nul
  if not %ERRORLEVEL% == 0 goto SKIPDRIVE 
  dir /s /b /-p /ad \ 2>nul|tr \\ /|find /V "%%" |find /V "$" >>%GTMP%\go.dat
  :SKIPDRIVE
  if not "%ARG%" == "" goto DBLOOP
  for /F %%a in (%GTMP%\tmp-cd.dat) do cd /D %%a
  echo Done.
set ARG=
set DRV=
goto :eof
:GODIR
if not "%~2" == "" echo.&echo Usage: g [input]&echo        g ^* [drives(default c)] to create/update folder database&goto :eof
if not exist %GTMP%\go.dat echo No database, run g * first.&goto :eof
echo "%CD%"| gawk "{ print substr($0,2,length($0)-2) }" | tr \\ />%GTMP%\go-temp1.dat
set ARG="%~1"
set ARG=%ARG:\=/%
type %GTMP%\go.dat|grep -i "%ARG:~1,-1%[^/]*$">>%GTMP%\go-temp1.dat
type %GTMP%\go-temp1.dat | gawk -f %GTMP%\g.awk> %GTMP%\go-temp2.bat
call %GTMP%\go-temp2.bat
set ARG=
It uses tr.exe to replace \ with / (because I want the script to accept both / and \ as input to g)
It uses grep.exe to search for a pattern where the input given is found, and is not followed by any /, and is ending in a newline
It uses gawk.exe, first to remove beginning and trailing ", and second in this separate script (this must be copied to the TEMP folder in a file called g.awk)

Code: Select all

{ IN[NR]=$0 } 
END { 
 if (NR>1) { 
  CH=2; 
  for(i=2; i<NR; i++) { 
    if(index(IN[1],IN[i])==1 && length(IN[1])==length(IN[i])) 
	  CH=i+1;
  } 

  i=CH;
  do {
    printf("cd /D \"%s\" 2>nul\nIF \"%%ERRORLEVEL%%\" == \"0\" GOTO :EOF\n",IN[i]);
    i++;
    if(i>NR) i = 2;
  } while(i!=CH);
 } 
}
This one creates a bat file based on the input, comparing the first line (which is the current path) to the ones found by grep. The resulting bat file uses CD to go to the folder.

Help, please!
Last edited by misol101 on 06 Jul 2020 03:30, edited 7 times in total.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch

#2 Post by misol101 » 13 May 2016 06:39

I put the script files and gawk,tr,grep in this archive in case anybody wants to run this and don't have those files:

http://www.mediafire.com/download/xdacdm28fzwyxfb/g.zip

(archive was updated due to the next post, but also attached to the thread)

Note that g.awk must be copied to the folder pointed to by %TEMP%, or if not set, the one pointed to by %TMP%

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch

#3 Post by misol101 » 03 Jul 2020 13:24

Ahh, one of misol's hidden gems! :mrgreen:

Seriously though, I use this script pretty much every day. It's great for quickly jumping between folders in command line (especially if Windows is scheduled to update its folder database once a day or so).

As the title says (4 years ago!), I wanted to convert this to "pure batch", but I didn't get around to trying it before now (though using Jscript (JRepl in this case) is of course a debatable form of "pure batch").

This post could also be called my first experiments with Jrepl, since I hadn't used it before.

The challenge then was to replace ALL of tr.exe, gawk.exe, and grep.exe with Jrepl. Did it work? The answer is yes... and no.

First up: tr.exe. This was super simple:

Code: Select all

tr \\ /
=>
jrepl \\ /
Then the first instance of using gawk.exe to remove " characters. This was simple too (and in the end I didn't need it):

Code: Select all

echo "%CD%" | gawk "{ print substr($0,2,length($0)-2) }"
=>
echo "%CD%" | jrepl \q "" /XSEQ
Then came the real challenge of replacing the external awk script (included last in the first post above). I finally came up with this (I'm sure it could be much nicer):

Code: Select all

type %GTMP%\go-temp1.dat|jrepl "%CURRCD:~1,-1%$" . /MATCH > nul
if %errorlevel% gtr 0 (
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /B > %GTMP%\go-temp2.bat
) else (
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /A /B /INC "/^%CURRCDP:~1,-1%$/+1:/^IncludeTheRest/" > %GTMP%\go-temp2.bat
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /A /B /EXC "/^%CURRCDP:~1,-1%$/+0:/^ExcludeTheRest/" >> %GTMP%\go-temp2.bat
)
With this, both gawk.exe and tr.exe was out. It was a bit slower than without Jrepl (average jump time about 1.5s instead of 1s), but nothing too bad, and 2 externals were gone!

In the last step I was trying to take out grep.exe:

Code: Select all

type %GTMP%\go.dat|grep -i "%ARG:~1,-1%[^/]*$">%GTMP%\go-temp1.dat
=>
type %GTMP%\go.dat|jrepl ".*%ARG:~1,-1%[^/]*" - /MATCH /I /E>%GTMP%\go-temp1.dat
While it is working, unfortunately this is where things really came to a halt. The Jrepl version now took 15+ seconds for a single run of g.bat, compared to 1.5s before. I'm guessing this is related to my folder database file being 19+ Mb large.

**************************
Entire script is:

Code: Select all

@echo off
if "%~1" == "" echo.&echo Usage: g [input]&echo        g ^* [drives(default c)] to create/update folder database&goto :eof
set GTMP=
if not "%TMP%" == "" set GTMP=%TMP%
if not "%TEMP%"=="" set GTMP=%TEMP%
if "%GTMP%" == "" echo Set TMP environment variable to temporary folder first.&goto :eof
if not "%~1" == "*" goto GODIR
  echo "%CD%">%GTMP%\tmp-cd.dat
  set ARG=c&if not "%~2" == "" set ARG=%~2
  del /Q %GTMP%\go.dat 2>nul
  echo Updating go database...
  :DBLOOP
  set DRV=%ARG:~0,1%
  set ARG=%ARG:~1%
  cd /D %DRV%: 2>nul
  if not %ERRORLEVEL% == 0 goto SKIPDRIVE 
  dir /s /b /-p /ad \ 2>nul|jrepl \\ /|find /V "%%" |find /V "$" >>%GTMP%\go.dat
  :SKIPDRIVE
  if not "%ARG%" == "" goto DBLOOP
  for /F %%a in (%GTMP%\tmp-cd.dat) do cd /D %%a
  echo Done.
set ARG=&set DRV=&set GTMP=
goto :eof
:GODIR
if not "%~2" == "" echo.&echo Usage: g [input]&echo        g ^* [drives(default c)] to create/update folder database&goto :eof
if not exist %GTMP%\go.dat echo No database, run g * first.&goto :eof
set CURRCD="%CD%"
set CURRCD=%CURRCD:\=/%
set ARG="%~1"
set ARG=%ARG:\=/%
type %GTMP%\go.dat|findstr /I /E "%ARG:~1,-1%[^/]*$">%GTMP%\go-temp1.dat
rem type %GTMP%\go.dat|jrepl ".*%ARG:~1,-1%[^/]*" - /MATCH /I /E>%GTMP%\go-temp1.dat & rem 15+ x slower

set CURRCDP=%CURRCD:/=\/%

type %GTMP%\go-temp1.dat|jrepl "%CURRCD:~1,-1%$" . /MATCH > nul
if %errorlevel% gtr 0 (
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /B > %GTMP%\go-temp2.bat
) else (
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /A /B /INC "/^%CURRCDP:~1,-1%$/+1:/^IncludeTheRest/" > %GTMP%\go-temp2.bat
	type %GTMP%\go-temp1.dat|jrepl .* "cd /D \q$&\q 2>nul\nif \x25ERRORLEVEL\x25==0 goto :eof" /XSEQ /A /B /EXC "/^%CURRCDP:~1,-1%$/+0:/^ExcludeTheRest/" >> %GTMP%\go-temp2.bat
)
call %GTMP%\go-temp2.bat
set ARG=&set GTMP=&set CURRCD=&set CURRCDP=
**************************

I would be curious to hear from Dave (or someone else knowledgeable about Jrepl) if there is a way I could speed up the grep replacement bit? Also, perhaps the awk bit before that could be turned into something shorter?

Anyway, I decided to update the archive above and to attach it here as well. At the moment, g-jrepl.bat in the archive still uses grep.exe, since things got unbearably slow otherwise. Gawk, tr, and grep are still included in the archive, as well as the original g.bat. Jrepl.bat you can get elsewhere, obviously :)


EDIT: now uses findstr instead of grep, see below. Takes care of the speed issue while using a default program instead of grep
Attachments
g.zip
(90.63 KiB) Downloaded 559 times
Last edited by misol101 on 11 Jul 2020 05:57, edited 5 times in total.

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: g.bat to jump between folders - need help to make it pure batch (JREPL help wanted!)

#4 Post by Aacini » 05 Jul 2020 21:21

Mmmm... Sorry... I did not read your code, but I wrote a pure Batch file that achieve what you described, unless I misunderstood it...

Code: Select all

@echo off
setlocal EnableDelayedExpansion

if "%~1" neq "" goto checkStar
echo Usage:
echo    g * [drives]   Create databases per drive
echo    g name         Goto first folder named name;
echo                   if already in it, goto next folder
echo    g paren/name   Goto folder name inside paren at any level
goto :eof

:checkStar
if "%~1" neq "*" goto checkNames
rem Create databases
set "drive=C"
set "drives=%2"
:nextDrive
if defined drives set "drive=%drives:~0,1%" & set "drives=%drives:~1%"
echo Creating databases in drive %drive%
(for /R %drive% /D %%d in (*) do echo %%d) > FoldersInDrive%Drive%.txt
if defined drives goto nextDrive
echo Databases created
goto :EOF

:checkNames
for /F "tokens=1,2 delims=/\" %%a in ("%~1") do set "paren=%%a" & set "folder=%%b"
if not defined folder (set "folder=%paren%" & set "paren=") else set "paren=%paren%\"
for /F "delims=:" %%a in ("%cd%") do set "drive=%%a"
set "name1=" & set "name2="
for /F "delims=" %%a in ('findstr /I /R "%paren%.*%folder%" FoldersInDrive%drive%.txt') do (
   if not defined name1 set "name1=%%a" else if not defined name2 set "name2=%%a"
)
if not defined name1 echo Such a folder don't exists & goto :EOF
if "%cd%" equ "%name1%" set "name1=%name2%"
if defined name1 cd "%name1%"
Antonio

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch (JREPL help wanted!)

#5 Post by misol101 » 06 Jul 2020 02:04

Looks like a promising approach Aacini,and thanks for taking the time!

It's not running here though. First it created an empty folder file when running: g *

I had to add :\ on line 20 so it looks like:
(for /R %drive%:\ /D %%d in (\*) do echo %%d) > FoldersInDrive%Drive%.txt

Then when running g.bat after successfully creating database, I get the error "The filename, directory name, or volume label syntax is incorrect."

I think perhaps the last line should be something like this, because without endlocal, the cd does not seem to actually change folder:

Code: Select all

if defined name1 endlocal & cd "%name1%"
Still getting "The filename, directory name, or volume label syntax is incorrect." on the last line.

The actual faulty line (if defined name1 cd "%name1%") when echoed looks like this, where the "else if" part is actually part of the name1 variable:

Code: Select all

if defined name1 cd "C:\Batch\git\\cmdgfx" else if not defined name2 set "name2=C:\Batch\git\\cmdgfx"
Do you know why?

EDIT: I made a few more changes:
line 31 changed to:
if not defined name1 (set "name1=%%a") else if not defined name2 set "name2=%%a"

and then I hardcoded (for now) the path to the folders database file, since g.bat should work no matter which folder the user is currently in.

Now it works on the first jump, but subsequent runs will stay in the same folder. Also, trying to do something like "g ch\bi" to jump to e.g. c:\batch\bin fails with "Such a folder don't exist".
Last edited by misol101 on 06 Jul 2020 05:14, edited 1 time in total.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch!

#6 Post by misol101 » 06 Jul 2020 05:00

Oh... actually Findstr is the perfect replacement for grep in this case :oops: :) While waiting for Aacini's updated version, I replaced the archives above and removed grep.exe. That means I have a (pure batch, kind of) working version now using only jrepl and findstr. Still also keeping the first version in the archive (that uses tr.exe and gawk.exe) because it is about 50% faster...

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch!

#7 Post by misol101 » 11 Jul 2020 06:03

Updated Mediafire link and above archive one more time, since I removed the use of tr.exe, so that the original g.bat now only relies on findstr and gawk.exe (while the ~50% slower g-jrepl.bat does not rely on gawk.exe at all)

Aacini: I'd still be interested if you have some idea why your proposed solution isn't working here?

Eureka!
Posts: 137
Joined: 25 Jul 2019 18:25

Re: g.bat to jump between folders - need help to make it pure batch!

#8 Post by Eureka! » 13 Jul 2020 09:15

Long ago I wrote something similar (batch only) Deleted it as I now use something better (my opinion, of course) as the batch-only version was too slow to my liking.

This was the idea behind it (re-created from memory, so I might be off here and there). Mainly to give you some extra ideas:

Code: Select all

@Echo off

::________________________________________________________________________
::
::              SETTINGS
::________________________________________________________________________
::
:: What to do with the found path?
    rem set COMMAND=CD /D
    set COMMAND=pushd

::________________________________________________________________________
::
::              Search matching folders; create menu entries
::________________________________________________________________________
::
    if .%1. == .. goto :EOF
    setlocal enabledelayedexpansion
       set tel=0
       echo.
    :: Enumerate first 10 folders
        for /f "usebackq delims=" %%x in (`findstr /i /r /c:"^.*%*[^\\]*$" ALLFOLDERS.db`) DO (
            if !tel! LEQ 9 (
            set optie!tel!=%%x
            set choices=!choices!!tel!
            set /a tel+=1
            )
        )

    :: %tel% represent the number of matching folders

        if %tel% == 0 (echo No matching folders .... & set KEUZE=.)
        if %tel% == 1 set KEUZE=%optie0%
        if %tel% GEQ 2 call :MENU

    endlocal & if "%KEUZE%" NEQ "." %COMMAND% "%KEUZE%"

goto :EOF


::________________________________________________________________________
::
            :MENU
::________________________________________________________________________
::

:: "Add" Quit to menu options ..
    set choices=!choices!Q
    set optie!tel!=.
    set /a tel-=1
    for /L %%x in (0,1,%tel%) DO echo [%%x]  !optie%%x!
    echo [Q]  QUIT

    choice /c !choices! /M "Give number or Q to quit"
:: Choice options start at 1; our options start at 0, so offset -1 ..
    set /a NMBR=%ERRORLEVEL% -1
    set KEUZE=!optie%NMBR%!

goto :EOF

I now use something based on Everything. Everything is a search utility for your files/folders and gives *instant* results. There is a command-line utility (ES.exe) to interface with Everything, giving you almost real-time results.
It uses quite a few external utilities, so it is not what you are looking for, but it also has some extra functionality that you could incorporate in your g.cmd:
SpeedDial function.

When there are certain folders you use very frequently, you can define those as a SpeedDial number.
For example: you go often to "C:\Program Files\Everything". Then make that number 1.
SD 1 will bring you directly to that folder.

Beside numbers, you can also use aliases: if you defined SD_pics=D:\data\photos, SD pics will bring you directly to D:\data\photos.

You can define the SpeedDial entries in SpeedDial.ini.
A couple of examples are included to get you going.

SD 0 will show you the currently defined speeddials


Directory up function.

With CMD's CD command you can go one directory up with the command CD .. If you want to go 2 levels up, you have to run that command twice.
SD can do that in one step: SD ... will go 2 level up; SD .... 3 levels, etcetera


"Back button" function

Just like your browser, SD has a back function. Actually: CMD has, but SD is using it.
You can go to the previous folder with the command SD -
For multiple folders back, add extra ---: SD --- will go 3 folders back.

If you don't want this function, you can change it with set COMMAND=CD /D in the SETTINGS section SpeedDial.ini.
(from: https://www.voidtools.com/forum/viewtop ... f=4&t=8242 )

SpeedDial code and settings file are in the attached SpeedDial.zip.

I hope this can help you with your G.bat
Attachments
SpeedDial.zip
(4.19 KiB) Downloaded 573 times

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch!

#9 Post by misol101 » 13 Jul 2020 11:55

Eureka!:

Thanks, that looks pretty useful! I’ll have a go.

Actually much more useful than turning g.bat into ”batch only”, which was just a little exercise anyway :)

Eureka!
Posts: 137
Joined: 25 Jul 2019 18:25

Re: g.bat to jump between folders - need help to make it pure batch!

#10 Post by Eureka! » 22 Jul 2020 14:41

misol101 wrote:
13 Jul 2020 11:55
Thanks, that looks pretty useful! I’ll have a go.
If you are also going to use Everything [1], you might be interested that it has an option Replace forward slashes with backslashes. So you can search for - for example - /windows


[1] I have yet to encounter someone that doesn't like Everything after using it.
I use it (among a lot of other things) for preventive searching on all my (network) volumes for filenames with poisonous/ problematic characters. Or finding long paths (>259). Results come back in milliseconds.

bakemonogatari
Posts: 21
Joined: 08 Jul 2019 05:22

Re: g.bat to jump between folders - need help to make it pure batch!

#11 Post by bakemonogatari » 28 Jul 2020 15:54

@Eureka!

interesting, thanks.

Eureka!
Posts: 137
Joined: 25 Jul 2019 18:25

Re: g.bat to jump between folders - need help to make it pure batch!

#12 Post by Eureka! » 30 Aug 2020 10:55

Just stumbled upon another option, posted on a different forum : WCD
It is modeled after NCD (Norton's Change Directory) with lots of options.

Just reporting; no experience with it (yet)

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: g.bat to jump between folders - need help to make it pure batch!

#13 Post by dbenham » 30 Aug 2020 12:56

I developed CDX.BAT to address the same issues long ago, based on an idea found at the ss64.org cmd forum. Rather than build an index of the entire volume(s), it allows each user to develop their own custom list of commonly accessed folders. Full help is available via CDX /?

I haven't had a chance to examine g.bat yet. I will take a look to see how JREPL.BAT (or JREN.BAT) might be better used.


Dave Benham

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: g.bat to jump between folders - need help to make it pure batch!

#14 Post by siberia-man » 30 Aug 2020 16:56

As far as I understand correctly, the original g.bat script rotates over all folders matching a particular pattern. For example, if we have some folders:
another/path/to/mydir
one/another/path/to/my
one/more/my/dir
the script invoked as below

Code: Select all

g.bat my
will do CD to the first and second one (highlighted in green) in the loop and skip the third one (highlighted in red).

Frankly speaking, I am not sure I like the idea of the script. However if your initial request to make the script pure batch is still actual, I can give you some hints (at least some improvements for the beginning). To this moment I see some thoughts how to avoid usage of sed/grep/find/findstr/awk. It could be some batch and jscript hybrid.

misol101
Posts: 475
Joined: 02 May 2016 18:20

Re: g.bat to jump between folders - need help to make it pure batch!

#15 Post by misol101 » 31 Aug 2020 07:50

siberia-man wrote:
30 Aug 2020 16:56
As far as I understand correctly, the original g.bat script rotates over all folders matching a particular pattern. For example, if we have some folders:
another/path/to/mydir
one/another/path/to/my
one/more/my/dir
the script invoked as below

Code: Select all

g.bat my
will do CD to the first and second one (highlighted in green) in the loop and skip the third one (highlighted in red).

Frankly speaking, I am not sure I like the idea of the script. However if your initial request to make the script pure batch is still actual, I can give you some hints (at least some improvements for the beginning). To this moment I see some thoughts how to avoid usage of sed/grep/find/findstr/awk. It could be some batch and jscript hybrid.
Thank you for wanting to help out. There were some modifications since the first version, which used tr/grep, and gawk. In the latest archive there are two versions of g.bat: one that still uses gawk, and one that uses jrepl instead of gawk (but is unfortunately about 50% slower). Tr/grep are no longer used.

Your description of how g.bat works is basically correct, except the third line with "my" would also be found since somewhere in the database of folders, that "my" folder is also going to be at the end of the line, like "one/more/my". You can also use / or \, so if you were looking to go to "project/folder" you could try "g ct/fo" or similar.

Sometimes you do have to run it a few times in a row, like if I try "g m32" I end up somewhere deep inside Visual Studio folders, but if I repeat it with a quick up-arrow-return, I get to "windows\System32" where I actually wanted to go.

It's useful for people with a bad memory of where they put things... I sometimes forget which drive or which folder structure I put a project, but g.bat will find it for me as long as I can remember a few continuous letters of what the project folder *itself* was called.

Post Reply