Capture CMD output with AutoHotkey - cmd

I'm trying to read Windows CMD's stdout with AutoHotkey. For example, I'd like to have the output of the setconsole command inside AHK stored in a variable. I already achieved it a while ago, which makes me all the more perplex why it's not working now.
In the AHK forums, there's a rather old thread about CMDret, a DLL based functionality to do exactly what I want. The first problem was to find a working download for it, since all the links in the post were dead. Google gave me another site, hosting v3.1.2. Altough there seems to be a newer one (v3.2.1 respectively 4d Beta), I checked it out and tested a simple example:
msgbox % CMDret(COMSPEC " /C set")
CMDret(CMD)
{
VarSetCapacity(StrOut, 10000)
RetVal := DllCall("cmdret.dll\RunReturn", "str", CMD, "str", StrOut)
Return, %StrOut%
}
Unfortunately, the MsgBox contained nothing. I then checked out RetVal which had a value of 0; and the attached readme says:
If the function fails, the return value is zero.
Further down, it says:
Note: only 32 bit console applications will currently work with the
this dll version of CMDret (v3.1.2 or lower). Calls that require
command.com will likely not produce any output and may crash. To avoid
this I have included a file named "cmdstub.exe" with the download (in
the Win9x folder). This file should be used when calling 16 bit
console applications to enable returning output.
In conclusion, I am not sure what the problem is. My machine is running on 64 bit. But is the corresponding clause in the readme supposed to solely exclude 16 bit systems or does it rather only include 32 bit?
If the computing architecture is probably not the problem, then what could be?
What I am looking for is either one of the following:
Can I fix the problem and keep using v3.1.2?
Has anyone a working source (or even a local copy) of a newer version I could check out?
Is there another approach [library, .ahk code, etc.] I could use for my purpose? (preferably similar, because CMDret seems very straightforward)

If you don't need a live output, you could use the cmd box itself to save a text file of itself and then you could have autohotkey detect when the console's PID finished (using the returned result of runwait and read the outputted file into memory.
So you could do this in your run command (just a regular cmd parameter):
ipconfig > myoutput.txt
exit
Now you have a text file with the ipconfig output in it.
OR
you could do the same thing, but instead of outputting to a text file, you could output to the clipboard, like this:
ipconfig | clip
Then, since the output is on the clipboard, you can easily grab it into autohotkey.

New recommended 2 ways of doing as of Nov 2019 - https://www.autohotkey.com/docs/FAQ.htm#output:
How can the output of a command line operation be retrieved?
Testing shows that due to file caching, a temporary file can be very fast for relatively small outputs. In fact, if the file is deleted immediately after use, it often does not actually get written to disk. For example:
RunWait %ComSpec% /c dir > C:\My Temp File.txt
FileRead, VarToContainContents, C:\My Temp File.txt
FileDelete, C:\My Temp File.txt
To avoid using a temporary file (especially if the output is large), consider using the
Shell.Exec() method as shown in the examples for the Run command.
Example for the Run command - https://www.autohotkey.com/docs/commands/Run.htm#StdOut:
MsgBox % RunWaitOne("dir " A_ScriptDir)
RunWaitOne(command) {
shell := ComObjCreate("WScript.Shell")
exec := shell.Exec(ComSpec " /C " command)
return exec.StdOut.ReadAll()
}
Note: the latter method (shell.Exec) will cause quick display of a cmd window.
You can reduce the duration of its appearance by putting these lines at the top of your script, which will also cause the flickering to happen only once the first time you call the cmd command. From https://autohotkey.com/board/topic/92032-how-to-hide-a-wscript-comspec-window/:
;Following 2 lines : the cmd window will flash only once and quickly
DllCall("AllocConsole")
WinHide % "ahk_id " DllCall("GetConsoleWindow", "ptr")

How about this script, StdoutToVar ?
It has support for 64bit consoles.
http://www.autohotkey.com/board/topic/15455-stdouttovar/page-7

This has been bugging me for some time now - and finally this works !
The only prerequisite for this is MS sqlcmd.exe, a database called AHK_Dev
and of course AHK_DBA to read the value when you wish to make use of it.
PS. make sure you replace {yourDir} and {yourServer} with you own values!
USE AHK_DEV
CREATE TABLE [dbo].[AHK_DOS]([dos_out] [varchar](max) NULL) ON [PRIMARY];
insert into ahk_dos select 'empty'
Create the follow script ... call it dos_out.bat
#echo off
if "%1" == "" (
set v_cmd=""
) else (
set v_cmd=%1
)
set v_cmd=%v_cmd:~1,-1%
SETLOCAL ENABLEDELAYEDEXPANSION
if "!v_cmd!" == "" (
set v_cmd="echo ... %COMPUTERNAME% %USERNAME% %DATE% %TIME%"
set v_cmd=!v_cmd:~1,-1!
)
set v_data=""
FOR /F "usebackq delims=¬" %%i in (`!v_cmd!`) do (
set v_data="!v_data:~1,-1!%%i~"
)
set q_cmd="set nocount on;update ahk_dos set dos_out=N'!v_data:~1,-1!'"
"{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W
set q_cmd="set nocount on;select len(dos_out) as out_len, dos_out from ahk_dos"
"{yourDir}\Microsoft SQL Server\90\Tools\Binn\sqlcmd.exe" -S {yourServer} -E -d ahk_dev -Q !q_cmd! -W -w 8000
pause
you can run it from AHK using...
dosCmd2db(c) {
runwait, {yourDir\}dos_out.bat "%c%", , , dospid
msgbox %dospid% closed
}
dosCmd2db("")
dosCmd2db("echo This is a test")
dosCmd2db("dir")
As the same field is being updated each time, you would clearly need to do something between each one to make this example useful!
Try it, and let me know how you get on
Regards, Geoff

Just an update to #amynbe answer.
MsgBox % RunWaitOne("dir " A_ScriptDir)
RunWaitOne(command) {
shell := ComObjCreate("WScript.Shell")
exec := shell.Exec(ComSpec " /C " command)
return exec.StdOut.ReadAll() }
Note: the latter method (shell.Exec)
will cause quick display of a cmd window. You can reduce
> the duration of its appearance by putting these lines at the top of
> your script, which will also cause the flickering to happen only once
> the first time you call the cmd command.
You can just do this below to hide cmd and avoid flashing.
MsgBox % RunWaitOne("dir " A_ScriptDir)
RunWaitOne(command) {
DetectHiddenWindows On
Run %ComSpec%,, Hide, pid
WinWait ahk_pid %pid%
DllCall("AttachConsole", "UInt", pid)
shell := ComObjCreate("WScript.Shell")
exec := shell.Exec(ComSpec " /C " command)
DllCall( "FreeConsole" )
return exec.StdOut.ReadAll()
}

I found a script only solution that works for AutoHotKey L 64bit at:
http://www.autohotkey.com/board/topic/67687-ahkahk-lusing-wsh-to-interact-with-command-line-progs/
After playing with it a bit I was able to capthre the entire output of a 40k text file that I listed using the DOS Type command. There is a demo that shows how you can interact with time command, which is nice if you need limited two way interaction with a dos command or batch script.

Related

Catch errorlevel from cmd command in shellexecute

I have batch script, to create backup of some 30 locations on work network, and it works.
Now I am programming the same app, but in MFC VC++, so im using little trick to set parameters for xcopy in string, then execute it with ShellExecute.
m_destination is variable from editbox, as destination input, where files are going.
Code is:
if (m_line1.GetCheck() == BST_CHECKED)
{
temp_dest = _T("/min /c xcopy \"\\\\pc_name.sub_domain.domain.local\\c$\\Users\\Test\\Desktop\\Test\\*.*\" \"") + m_destination + _T("\" /Y /E /Q");
ShellExecute(
GetSafeHwnd(),
L"open", // open edit print
L"C:\\Windows\\System32\\cmd.exe", // FILE PATH,
temp_dest, // PARAMETERS
NULL, // WORKING DIR
SW_HIDE); // WINDOW SHOW HIDE
m_status = "Line 1 - OK\r\n";
}
This code above works, but there are some cases, when target PC is shutdown, and in cmd window, it says "path has changed or destination unreachable" (something like that). In that case, %errorlevel% has some value, and some other if copying successfully done. Then u have condition to trigger different warnings to user.
The thing is, I dont have clue how to catch it to indicate unsuccessful copying.
I need some kind of warning that copying from that PC is not done. How can I retrieve error code after copying in this way?
Or if there is simple function to replace copyx with (*.*) (all contents) copying, with ability to be directed to specific directory, I would like to hear about it.
Thanks in advance.
I don't think there's a straightforward way to do this with ShellExecute because it doesn't give you a process handle. However, ShellExecuteEx does, via the hProcess field of the SHELLEXECUTEINFO struct you pass in. With that handle, you can wait (WaitForSingleObject) and then get the exit code (GetExitCodeProcess). However, you can also do this with a simple CreateProcess. Also SHFileOperation supports some xcopy-like operations and gives you additional programmatic control.

How can you get the clipboard contents with a Windows command?

For example, I can copy a file to the clipboard like this:
clip < file.txt
(Now the contents of file.txt is in the clipboard.)
How can I do the opposite:
???? > file.txt
So that the contents of the clipboard will be in file.txt?
If it would be acceptable to use PowerShell (and not cmd), then you can use Get-Clipboard exactly as you were looking for.
Get-Clipboard > myfile.txt
The advantage of this method is that you have nothing to install.
Note: In place of clip you can use Set-Clipboard that has more options.
Note 2: If you really want to run it from cmd, you can call powershell as in the following example powershell -command "Get-Clipboard | sort | Set-Clipboard".
You can use the paste.exe software in order to paste text just like you are describing.
http://www.c3scripts.com/tutorials/msdos/paste.html
With it you can do:
paste | command
to paste the contents of the windows clipboard into the input of the specified command prompt
or
paste > filename
to paste the clipboard contents to the specified file.
Clarifying an answer from #Kpym:
powershell -command "Get-Clipboard" > file.txt
This directly answers the question without using a 3rd party tool.
To get contents of clipboard
From win cmd:
powershell get-clipboard
or (via a temp file from HTML parser) on cmd:
echo x = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text") > temp.vbs
echo WScript.Echo x >> temp.vbs
cscript //nologo temp.vbs
Output may be redirected to file.
Using the doskey macro definition feature, you can do:
doskey unclip=(powershell -command "Get-Clipboard") $*
Then (e.g.)
dir/b | clip
unclip | sort/r
I have a pair of utilities (from before the Clip command was part of windows) available on this page:
http://www.clipboardextender.com/general-clipboard-use/command-window-output-to-clipboard-in-vista
There are two utilities in there, Clip2DOS and DOS2Clip. You want Clip2DOS:
Clip2DOS Copyright 2006 Thornsoft Development
Dumps clipboard text (1024 bytes) to stdout.
Usage: Clip2Dos.exe > out.txt
Result: text is in the file.
Limits: 1024 bytes.
License: Free, as in Free Beer!
http://www.thornsoft.com/dist/techsupport/dos2clip.zip
DELPHI SOURCE INCLUDED!
And hey, here it is (Clip2DOS.dpr) :
{Clip2DOS - copyright 2005 Thornsoft Development, Inc. All rights reserved.}
program Clip2Dos;
{$APPTYPE CONSOLE}
uses
Clipbrd,
ExceptionLog,
SysUtils;
var
p : Array[0..1024] of Char;
begin
try
WriteLn('Clip2DOS Copyright 2006 Thornsoft Development');
Clipboard.GetTextBuf(p,1024);
WriteLn(p);
except
//Handle error condition
on E: Exception do
begin
beep;
Writeln(SysUtils.format('Clip2DOS - Error: %s',[E.Message]));
ExitCode := 1; //Set ExitCode <> 0 to flag error condition (by convention)
end;
end
end.
Pasteboard is another option. It can also work from WSL. First, install via choco:
choco install pasteboard
then the command is simply
pbpaste.exe > file.txt
And that works from cmd and wsl bash.
Well, from a million years ago, we did something like this:
type con > filename.txt
... and then you perform your paste operation (Ctrl-v, middle-click the mouse, or choose Edit->Paste from them menu) into the waiting prompt. This will capture the stdin buffer (the console device, named 'con'), and when an end-of-file is received, it will write the contents to the file. So, after your paste, you type 'Ctrl-z' to generate an EOF, and the type command terminates, and the contents of your paste buffer (the clipboard) are captured in 'filename.txt'.
There are third party clip commands that work bidirectionally.
Here's one:
CLIP - Copy the specified text file to the clip board
Copyright (c) 1998,99 by Dave Navarro, Jr. (dave#basicguru.com)
Here is the CLIP program by Dave Navarro, as referred to in the answer by #foxidrive. It is mentioned in an article here: copying-from-clipboard-to-xywrite
A link to the download, along with many other resources is on this page: http://www.lexitec.fi/xywrite/utility.html
Here is a direct link to the download:
"DOWNLOAD Clip.exe Copy from and to the clipboard by Dave Navarro, Jr."
It may be possible with vbs:
Option Explicit
' Gets clipboard's contents as pure text and saves it or open it
Dim filePath : filePath = "clipboard.txt"
' Use the HTML parser to have access to the clipboard and get text content
Dim text : text = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text")
' to open
If Not IsNull(text) then
Dim WshShell, somestring, txFldr2Open
Set WshShell = WScript.CreateObject("WScript.Shell")
txFldr2Open = "C:\Users"
txFldr2Open = text
somestring = "EXPLORER.exe /e," & txFldr2Open ', /select
WshShell.run somestring
Set WshShell = Nothing
else
msgbox("Empty")
end if
' Create the file and write on it
msgbox(text)
Dim fileObj : Set fileObj = CreateObject("Scripting.FileSystemObject").CreateTextFile(filePath)
fileObj.Write(text)
fileObj.Close
You can use cbecho, a program I wrote in plain C. It will send any clipboard text to stdout, from where you can pipe it to other programs.
I am not sure if this command was not supported at that time or not, but it surely does work
clip > file.txt
This dirty trick worked for my needs, and it comes with Windows!
notepad.exe file.txt
Ctrl + V, Ctrl + S, Alt + F, X

How to capture display output from a command prompt in Windows at any moment?

I'm tring to capture de information shown in a command prompt (cmd window) at a specific moment and send it to a text file.
I have an app in C/C++ that is launched by a batch script like this:
c:\myProgramInC.exe
echo "Ending with error"
PAUSE
myProgramInC.exe is always running (with an infinite loop) so if my script gets to the echo command it means my app ended with an abend.
What I want to get is the previous lines before the end execution of the script since my myProgramInC.exe always prints info about what is going on and it would be very useful to see what it was happening when the error ocurred. Something like this
c:\myProgramInC.exe
**** Execution of process to get te previous N lines in the command prompt ****
echo "Ending with error"
PAUSE
I know the cmd window have a buffer and I've been thinking to capture all of this data from such buffer. It is any way to get this info as text to store it in a file?
I'v been trying with something more professional shuch as to make a dump of the memory with ProcDump but since I have various myProgramInC.exe running at the same time (each one stored in a different location) I just get the message "Multiple processes match the specified name." and the rest of the options are just not useful for me since my app doesn't get unresponsive, it simply ends.
Any ideas?
Quick trick would be to execute in context of for /f, you do not even need a batch file for that, you could execute directly from cmd line:
for /f "tokens=*" %F in ('c:\myProgramInC.exe') do #echo %F >>myPrograminC.log
This will suppress all output until your program abends and only then would write all messages to file. If your app writes log messages infrequently (or fails quickly :-)) it should work. I tested it with 10 000 lines.
Batch code below is based on same idea - please note that even it writes only 5 last lines, it still has to scan through all of them so I'm not sure it's any better than above 1 liner.
#echo off
setlocal
for /f "tokens=*" %%P in ('c:\myProgramInC.exe') do (
for /L %%C in (4,-1,1) do (
set /A next=%%C+1
call set line_%%next%%=%%line_%%C%%
)
set line_1=%%P
)
echo program ended abnormally! %date% %time%
for /L %%C in (5,-1,1) do call echo %%line_%%C%%
Ok, there may be a more elegant way to do this, but this will work assuming you have PowerShell.
Create a PowerShell script file called Get-ConsoleAsText.ps1 that contains the script below. Note, I did not create this script. I found it at Windows PowerShell Blog - Capture console screen.
#################################################################################################################
# Get-ConsoleAsText.ps1
#
# The script captures console screen buffer up to the current cursor position and returns it in plain text format.
#
# Returns: ASCII-encoded string.
#
# Example:
#
# $textFileName = "$env:temp\ConsoleBuffer.txt"
# .\Get-ConsoleAsText | out-file $textFileName -encoding ascii
# $null = [System.Diagnostics.Process]::Start("$textFileName")
#
# Check the host name and exit if the host is not the Windows PowerShell console host.
if ($host.Name -ne 'ConsoleHost')
{
write-host -ForegroundColor Red "This script runs only in the console host. You cannot run this script in $($host.Name)."
exit -1
}
# Initialize string builder.
$textBuilder = new-object system.text.stringbuilder
# Grab the console screen buffer contents using the Host console API.
$bufferWidth = $host.ui.rawui.BufferSize.Width
$bufferHeight = $host.ui.rawui.CursorPosition.Y
$rec = new-object System.Management.Automation.Host.Rectangle 0, 0, ($bufferWidth), $bufferHeight
$buffer = $host.ui.rawui.GetBufferContents($rec)
# Iterate through the lines in the console buffer.
for($i = 0; $i -lt $bufferHeight; $i++)
{
for($j = 0; $j -lt $bufferWidth; $j++)
{
$cell = $buffer[$i, $j]
$null = $textBuilder.Append($cell.Character)
}
$null = $textBuilder.Append("`r`n")
}
return $textBuilder.ToString()
If you call the PowerShell script by itself, it will read the console buffer and write it back to the screen
PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1"
You can also call it like this to capture the contents to a file:
PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1 | Out-File MyOutput.txt -encoding ascii"
If you want to process it and perform some action within the batch file, you can call it and process the output using a FOR command. I will leave that exercise to you.
So, for example, your batch file would look like this to capture the console output to a file:
c:\myProgramInC.exe
echo "Ending with error"
PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1 | Out-File MyOutput.txt -encoding ascii"
PAUSE
You can do this in C easily enough, using the ReadConsoleOutputCharacter function.
Finally this is what I get to make it work. Following the advise of Harry Johnston
I searched about the way ReadConsoleOutputCharacter function works and I got the next program running.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <fstream>
#define BUFFER_SIZE 50000
int main(){
HANDLE hOut;
COORD location = {0, 0};
char *buffer=NULL;
DWORD numberRead;
std::ofstream fileLog;
buffer = new TCHAR[BUFFER_SIZE];
if ( buffer==NULL )
{
printf("\nError: memory could not be allocated.\n");
return 1;
}
fileLog.open ("logger.txt");
if ( fileLog.fail() )
{
printf("\nError: file could not be opened.\n");
return 1;
}
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
ReadConsoleOutputCharacter(hOut, buffer, BUFFER_SIZE, location, &numberRead);
fileLog << buffer ;
free(buffer);
fileLog.close();
return 0;
}
Much of the code I found it in internet and right now I don't have the URL (besides it's from another forum site and I don't know if it's OK to make references to competitor sites) but with a single search in google wouldn't be difficult to find it.
I added the part where it writes to a file and allocation of memory. It run's perfectly in VS C++ 6.
This works fine for me although could be better. Two things aren't very nice like
It writes all the content from the buffer to a file but it doesn't keep the newlines so I get a text file with all the information in ONE single line.
Can not be defined how much lines to be captured. It just takes everything from the beginnig of the buffer to the point where the program starts, wich is not bad but since this app should run in different servers where each one has a different buffer for each set of command prompt windows, is not much efficient just to use a buffer of 50000 bytes when in some cases the buffer is smaller. I think that could be a lot but since it is allocated in real time maybe is not so wrong to use as much memory.
If any error messages are going to be reported back to the command output stream, it can be easily redirected via Redirection Operators for STDOUT (Standard Output) and STDERR (Standard Errors). Depending on whether or not you want to capture output and/or errors, is up to you. More than likely, you are looking for something like:
command 2>> filename
The "2" is a part of the redirection operator, and indicates that STDERR will be redirected and appended to filename.
Sources, examples, and documentation:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx
http://ss64.com/nt/syntax-redirection.html
One thing that comes in minde immediately is redirecting output of your command line to to a file:
c:\myProgramInC.exe >> myProgramInC.log
then you can see what is in log file.

windows batch sqlite backup firefox history

Not familiar with windows stuffs.. , I'm trying to write a little MS Windows batch in order to backup firefox history but I'm not getting the expected result, eg the firefox history dump into a file (not implemented here), and can't figure out why and how to solve. Instead I get a dump of the database in a new window. Here is what I've done till now :
cmd windows terminal
start "TEST" sqlite.cmd
sqlite.cmd
REM backup firefox history
setlocal
set DB_src=places.sqlite
set DB_dest=places1.sqlite
set FF_profile=C:\Documents and Settings\User_A\Application Data\Mozilla\Firefox\Profiles\1e6xxxxx.default
set SQLITE_EXE=C:\Documents and Settings\Admin_User\SoftWare\sqlite3.exe
set SQLITE_SQL=C:\Documents and Settings\Admin_User\Bureau\sqlite.sql
copy "%FF_profile%\%DB_src%" "%FF_profile%\%DB_dest%"
#echo off
start "%SQLITE_EXE%" "%FF_profile%\%DB_dest%" < "%SQLITE_SQL%"
endlocal
sqlite.sql
.dump html
.output moz_places.html
SELECT moz_places.visit_count, moz_places.url FROM moz_places ORDER by visit_count DESC LIMIT 20;
[EDIT]:
Worked around :
- using the right sqlite query (updated in sqlite.sql below)as for these examples.
- using the sql html output "moz_places.html" as I could not get the redirection work.
linux stuffs are easier for me...
This question is lame, because it involves three completely unrelated domains: firefox, sqlite, and batch files. You should have isolated the problem by determining whether this is a firefox issue or an sqlite issue or a batch file issue and then you should have come up with a question regarding the domain in which the issue lies, with absolutely no mention to the other domains.
I am going to give you an answer as best as I can regarding batch files:
First of all, you need to stop needlessly using the start command and just invoke things directly. So, instead of:
start "%SQLITE_EXE%" "%FF_profile%\%DB_dest%" < "%SQLITE_SQL%"
You need this:
"%SQLITE_EXE%" "%FF_profile%\%DB_dest%" < "%SQLITE_SQL%"
Then, you need to redirect the output of the above command into a file of your choice. For this, you need to make use of the '>' operator. So:
"%SQLITE_EXE%" "%FF_profile%\%DB_dest%" < "%SQLITE_SQL%" > myfile.txt
That should do it, as far as batch files are concerned. If it does not do it, then it is a firefox or sqlite issue.

Is there an equivalent of 'which' on the Windows command line?

As I sometimes have path problems, where one of my own cmd scripts is hidden (shadowed) by another program (earlier on the path), I would like to be able to find the full path to a program on the Windows command line, given just its name.
Is there an equivalent to the UNIX command 'which'?
On UNIX, which command prints the full path of the given command to easily find and repair these shadowing problems.
Windows Server 2003 and later (i.e. anything after Windows XP 32 bit) provide the where.exe program which does some of what which does, though it matches all types of files, not just executable commands. (It does not match built-in shell commands like cd.) It will even accept wildcards, so where nt* finds all files in your %PATH% and current directory whose names start with nt.
Try where /? for help.
Note that Windows PowerShell defines where as an alias for the Where-Object cmdlet, so if you want where.exe, you need to type the full name instead of omitting the .exe extension. Alternatively, you can set an alias for it:
Set-Alias which where.exe
Update: Using Get-Command (alias: gcm) is recommended since it's native to PS and will get all command types: aliases, cmdlets, executables, and functions. Example:
gcm notepad*
While later versions of Windows have a where command, you can also do this with Windows XP by using the environment variable modifiers, as follows:
c:\> for %i in (cmd.exe) do #echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe
c:\> for %i in (python.exe) do #echo. %~$PATH:i
C:\Python25\python.exe
You don't need any extra tools and it's not limited to PATH since you can substitute any environment variable (in the path format, of course) that you wish to use.
And, if you want one that can handle all the extensions in PATHEXT (as Windows itself does), this one does the trick:
#echo off
setlocal enableextensions enabledelayedexpansion
:: Needs an argument.
if "x%1"=="x" (
echo Usage: which ^<progName^>
goto :end
)
:: First try the unadorned filenmame.
set fullspec=
call :find_it %1
:: Then try all adorned filenames in order.
set mypathext=!pathext!
:loop1
:: Stop if found or out of extensions.
if "x!mypathext!"=="x" goto :loop1end
:: Get the next extension and try it.
for /f "delims=;" %%j in ("!mypathext!") do set myext=%%j
call :find_it %1!myext!
:: Remove the extension (not overly efficient but it works).
:loop2
if not "x!myext!"=="x" (
set myext=!myext:~1!
set mypathext=!mypathext:~1!
goto :loop2
)
if not "x!mypathext!"=="x" set mypathext=!mypathext:~1!
goto :loop1
:loop1end
:end
endlocal
goto :eof
:: Function to find and print a file in the path.
:find_it
for %%i in (%1) do set fullspec=%%~$PATH:i
if not "x!fullspec!"=="x" #echo. !fullspec!
goto :eof
It actually returns all possibilities but you can tweak it quite easily for specific search rules.
Under PowerShell, Get-Command will find executables anywhere in $Env:PATH.
$ Get-Command eventvwr
CommandType Name Definition
----------- ---- ----------
Application eventvwr.exe c:\windows\system32\eventvwr.exe
Application eventvwr.msc c:\windows\system32\eventvwr.msc
And since powershell let's you define aliases, which can be defined like so.
$ sal which gcm # short form of `Set-Alias which Get-Command`
$ which foo
...
PowerShell commands are not just executable files (.exe, .ps1, etc). They can also be cmdlets, functions, aliases, custom executable suffixes set in $Env:PATHEXT, etc. Get-Command is able to find and list all of these commands (quite akin to Bash's type -a foo). This alone makes it better than where.exe, which.exe, etc which are typically limited to finding just executables.
Finding executables using only part of the name
$ gcm *disk*
CommandType Name Version Source
----------- ---- ------- ------
Alias Disable-PhysicalDiskIndication 2.0.0.0 Storage
Alias Enable-PhysicalDiskIndication 2.0.0.0 Storage
Function Add-PhysicalDisk 2.0.0.0 Storage
Function Add-VirtualDiskToMaskingSet 2.0.0.0 Storage
Function Clear-Disk 2.0.0.0 Storage
Cmdlet Get-PmemDisk 1.0.0.0 PersistentMemory
Cmdlet New-PmemDisk 1.0.0.0 PersistentMemory
Cmdlet Remove-PmemDisk 1.0.0.0 PersistentMemory
Application diskmgmt.msc 0.0.0.0 C:\WINDOWS\system32\diskmgmt.msc
Application diskpart.exe 10.0.17... C:\WINDOWS\system32\diskpart.exe
Application diskperf.exe 10.0.17... C:\WINDOWS\system32\diskperf.exe
Application diskraid.exe 10.0.17... C:\WINDOWS\system32\diskraid.exe
...
Finding custom executables
Unlike UNIX, where executables are files with the executable (+x) bit set, executables on windows are files present in one of the directories specified in the $PATH env. variable whose filename suffixes are named in the $PATHEXT env. variable (defaults to .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL).
As Get-Command also honours this env. variable, it can be extended to list custom executables. e.g.
$ $Env:PATHEXT="$Env:PATHEXT;.dll;.ps1;.psm1;.py" # temporary assignment, only for this shell's process
$ gcm user32,kernel32,*WASM*,*http*py
CommandType Name Version Source
----------- ---- ------- ------
ExternalScript Invoke-WASMProfiler.ps1 C:\WINDOWS\System32\WindowsPowerShell\v1.0\Invoke-WASMProfiler.ps1
Application http-server.py 0.0.0.0 C:\Users\ME\AppData\Local\Microsoft\WindowsApps\http-server.py
Application kernel32.dll 10.0.17... C:\WINDOWS\system32\kernel32.dll
Application user32.dll 10.0.17... C:\WINDOWS\system32\user32.dll
See Get-Command for more options and examples.
In Windows PowerShell:
set-alias which where.exe
If you have PowerShell installed (which I recommend), you can use the following command as a rough equivalent (substitute programName for your executable's name):
($Env:Path).Split(";") | Get-ChildItem -filter programName*
More is here:
My Manwich! PowerShell Which
The GnuWin32 tools have which, along with a whole slew of other Unix tools.
In Windows CMD which calls where:
$ where php
C:\Program Files\PHP\php.exe
Cygwin is a solution. If you don't mind using a third-party solution, then Cygwin is the way to go.
Cygwin gives you the comfort of *nix in the Windows environment (and you can use it in your Windows command shell, or use a *nix shell of your choice). It gives you a whole host of *nix commands (like which) for Windows, and you can just include that directory in your PATH.
In PowerShell, it is gcm, which gives formatted information about other commands. If you want to retrieve only path to executable, use .Source.
For instance: gcm git or (gcm git).Source
Tidbits:
Available for Windows XP.
Available since PowerShell 1.0.
gcm is an alias of Get-Command cmdlet.
Without any parameters, it lists down all the available commands offered by the host shell.
You can create a custom alias with Set-Alias which gcm and use it like: (which git).Source.
Official docs: https://technet.microsoft.com/en-us/library/ee176842.aspx
I have a function in my PowerShell profile named 'which'
function which {
get-command $args[0]| format-list
}
Here's what the output looks like:
PS C:\Users\fez> which python
Name : python.exe
CommandType : Application
Definition : C:\Python27\python.exe
Extension : .exe
Path : C:\Python27\python.exe
FileVersionInfo : File: C:\Python27\python.exe
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:
Go get unxutils from here: http://sourceforge.net/projects/unxutils/
gold on windows platforms, puts all the nice unix utilities on a standard windows DOS. Been using it for years.
It has a 'which' included. Note that it's case sensitive though.
NB: to install it explode the zip somewhere and add ...\UnxUtils\usr\local\wbin\ to your system path env variable.
If you can find a free Pascal compiler, you can compile this. At least it works and shows the algorithm necessary.
program Whence (input, output);
Uses Dos, my_funk;
Const program_version = '1.00';
program_date = '17 March 1994';
VAR path_str : string;
command_name : NameStr;
command_extension : ExtStr;
command_directory : DirStr;
search_dir : DirStr;
result : DirStr;
procedure Check_for (file_name : string);
{ Check existence of the passed parameter. If exists, then state so }
{ and exit. }
begin
if Fsearch(file_name, '') <> '' then
begin
WriteLn('DOS command = ', Fexpand(file_name));
Halt(0); { structured ? whaddayamean structured ? }
end;
end;
function Get_next_dir : DirStr;
{ Returns the next directory from the path variable, truncating the }
{ variable every time. Implicit input (but not passed as parameter) }
{ is, therefore, path_str }
var semic_pos : Byte;
begin
semic_pos := Pos(';', path_str);
if (semic_pos = 0) then
begin
Get_next_dir := '';
Exit;
end;
result := Copy(Path_str, 1, (semic_pos - 1)); { return result }
{ Hmm! although *I* never reference a Root drive (my directory tree) }
{ is 1/2 way structured), some network logon software which I run }
{ does (it adds Z:\ to the path). This means that I have to allow }
{ path entries with & without a terminating backslash. I'll delete }
{ anysuch here since I always add one in the main program below. }
if (Copy(result, (Length(result)), 1) = '\') then
Delete(result, Length(result), 1);
path_str := Copy(path_str,(semic_pos + 1),
(length(path_str) - semic_pos));
Get_next_dir := result;
end; { Of function get_next_dir }
begin
{ The following is a kludge which makes the function Get_next_dir easier }
{ to implement. By appending a semi-colon to the end of the path }
{ Get_next_dir doesn't need to handle the special case of the last entry }
{ which normally doesn't have a semic afterwards. It may be a kludge, }
{ but it's a documented kludge (you might even call it a refinement). }
path_str := GetEnv('Path') + ';';
if (paramCount = 0) then
begin
WriteLn('Whence: V', program_version, ' from ', program_date);
Writeln;
WriteLn('Usage: WHENCE command[.extension]');
WriteLn;
WriteLn('Whence is a ''find file''type utility witha difference');
Writeln('There are are already more than enough of those :-)');
Write ('Use Whence when you''re not sure where a command which you ');
WriteLn('want to invoke');
WriteLn('actually resides.');
Write ('If you intend to invoke the command with an extension e.g ');
Writeln('"my_cmd.exe param"');
Write ('then invoke Whence with the same extension e.g ');
WriteLn('"Whence my_cmd.exe"');
Write ('otherwise a simple "Whence my_cmd" will suffice; Whence will ');
Write ('then search the current directory and each directory in the ');
Write ('for My_cmd.com, then My_cmd.exe and lastly for my_cmd.bat, ');
Write ('just as DOS does');
Halt(0);
end;
Fsplit(paramStr(1), command_directory, command_name, command_extension);
if (command_directory <> '') then
begin
WriteLn('directory detected *', command_directory, '*');
Halt(0);
end;
if (command_extension <> '') then
begin
path_str := Fsearch(paramstr(1), ''); { Current directory }
if (path_str <> '') then WriteLn('Dos command = "', Fexpand(path_str), '"')
else
begin
path_str := Fsearch(paramstr(1), GetEnv('path'));
if (path_str <> '') then WriteLn('Dos command = "', Fexpand(path_str), '"')
else Writeln('command not found in path.');
end;
end
else
begin
{ O.K, the way it works, DOS looks for a command firstly in the current }
{ directory, then in each directory in the Path. If no extension is }
{ given and several commands of the same name exist, then .COM has }
{ priority over .EXE, has priority over .BAT }
Check_for(paramstr(1) + '.com'); { won't return if file is found }
Check_for(paramstr(1) + '.exe');
Check_for(paramstr(1) + '.bat');
{ Not in current directory, search through path ... }
search_dir := Get_next_dir;
while (search_dir <> '') do
begin
Check_for(search_dir + '\' + paramstr(1) + '.com');
Check_for(search_dir + '\' + paramstr(1) + '.exe');
Check_for(search_dir + '\' + paramstr(1) + '.bat');
search_dir := Get_next_dir;
end;
WriteLn('DOS command not found: ', paramstr(1));
end;
end.
Not in stock Windows but it is provided by Services for Unix and there are several simple batch scripts floating around that accomplish the same thing such this this one.
The best version of this I've found on Windows is Joseph Newcomer's "whereis" utility, which is available (with source) from his site.
The article about the development of "whereis" is worth reading.
None of the Win32 ports of Unix which that I could find on the Internet are satistactory, because they all have one or more of these shortcomings:
No support for Windows PATHEXT variable. (Which defines the list of extensions implicitely added to each command before scanning the path, and in which order.) (I use a lot of tcl scripts, and no publicly available which tool could find them.)
No support for cmd.exe code pages, which makes them display paths with non-ascii characters incorrectly. (I'm very sensitive to that, with the ç in my first name :-))
No support for the distinct search rules in cmd.exe and the PowerShell command line. (No publicly available tool will find .ps1 scripts in a PowerShell window, but not in a cmd window!)
So I eventually wrote my own which, that suports all the above correctly.
Available there:
http://jf.larvoire.free.fr/progs/which.exe
This batch file uses CMD variable handling to find the command that would be executed in the path. Note: that the current directory is always done before the path) and depending on which API call is used other locations are searched before/after the path.
#echo off
echo.
echo PathFind - Finds the first file in in a path
echo ======== = ===== === ===== ==== == == = ====
echo.
echo Searching for %1 in %path%
echo.
set a=%~$PATH:1
If "%a%"=="" (Echo %1 not found) else (echo %1 found at %a%)
See set /? for help.
You can first install Git from Downloading Git, and then open Git Bash and type:
which app-name
I am using GOW (GNU on Windows) which is a light version of Cygwin. You can grab it from GitHub here.
GOW (GNU on Windows) is the lightweight alternative to Cygwin. It uses
a convenient Windows installer that installs about 130 extremely
useful open source UNIX applications compiled as native win32
binaries. It is designed to be as small as possible, about 10 MB, as
opposed to Cygwin which can run well over 100 MB depending upon
options. - About Description(Brent R. Matzelle)
A screenshot of a list of commands included in GOW:
I have created tool similar to Ned Batchelder:
Searching .dll and .exe files in PATH
While my tool is primarly for searching of various dll versions it shows more info (date, size, version) but it do not use PATHEXT (I hope to update my tool soon).
Just have to post this Windows' one liner batch file:
C:>type wh.cmd
#for %%f in (%*) do for %%e in (%PATHEXT% .dll .lnk) do for %%b in (%%f%%e) do for %%d in (%PATH%) do if exist %%d\%%b echo %%d\%%b
A test:
C:>wh ssh
C:\cygwin64\bin\ssh.EXE
C:\Windows\System32\OpenSSH\\ssh.EXE
Not quite a one-liner if you wrap the code in setlocal enableextensions and endlocal.
For you Windows XP users (who have no where command built-in), I have written a "where like" command as a rubygem called whichr.
To install it, install Ruby.
Then
gem install whichr
Run it like:
C:> whichr cmd_here
TCC and TCC/LE from JPSoft are CMD.EXE replacements that add significant functionality. Relevant to the OP's question, which is a builtin command for TCC family command processors.
I have used the which module from npm for quite a while, and it works very well: https://www.npmjs.com/package/which
It is a great multi platform alternative.
Now I switched to the which that comes with Git. Just add to your path the /usr/bin path from Git, which is usually at C:\Program Files\Git\usr\bin\which.exe. The which binary will be at C:\Program Files\Git\usr\bin\which.exe. It is faster and also works as expected.
try this
set a=%~$dir:1
If "%for%"=="" (Echo %1 not found) else (echo %1 found at %a%)
It is possible to download all of the UNIX commands compiled for Windows, including which from this GitHub repository: https://github.com/George-Ogden/UNIX
Here is a function which I made to find executable similar to the Unix command 'WHICH`
app_path_func.cmd:
#ECHO OFF
CLS
FOR /F "skip=2 tokens=1,2* USEBACKQ" %%N IN (`reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\%~1" /t REG_SZ /v "Path"`) DO (
IF /I "%%N" == "Path" (
SET wherepath=%%P%~1
GoTo Found
)
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%PROGRAMFILES%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%PROGRAMFILES(x86)%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
FOR /F "tokens=* USEBACKQ" %%F IN (`where.exe /R "%WINDIR%" %~1`) DO (
SET wherepath=%%F
GoTo Found
)
:Found
SET %2=%wherepath%
:End
Test:
#ECHO OFF
CLS
CALL "app_path_func.cmd" WINWORD.EXE PROGPATH
ECHO %PROGPATH%
PAUSE
Result:
C:\Program Files (x86)\Microsoft Office\Office15\
Press any key to continue . . .
https://www.freesoftwareservers.com/display/FREES/Find+Executable+via+Batch+-+Microsoft+Office+Example+-+WINWORD+-+Find+Microsoft+Office+Path

Resources