With starting command prompt with cmd /e:off or using setlocal disableExtensions (in a batch file) or excluding them through the registry value HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions (by setting the value to 0) the feature called "extensions" is disabled (turned on by default).
What are the changes that this causes?
Turning off extensions affects some built-in environment variables and some internal for the command prompt commands. I've never seen full list of non-working variables ,though the internal commands are more or less well documented - but never put in a list together.
I. I'll start with the variables:
%CD% is not working. But surprisingly %__CD__% works though it sets additional backslash at the end.
%CMDEXTVERSION% - logical at some degree as the command prompt is reduced only to its earlier versions
%CMDCMDLINE% - command line could be retrieved with some external tools that will give you information about the current PID.
%ERRORLEVEL% - as alternative %=ExitCode% can be used. Also %=ExitCodeAscii% if the exit code is above 32. IF ERRORLEVEL also still works.
%DATE% and %TIME% - though in disabled extensions mode you won't be able to assign the current date to a variable there are at least a lot of ways to display it.
%RANDOM%
%HighestNumaNodeNumber% - this was the biggest surprise for me as this variable was introduced in the newer versions of Windows when the support for multicore processors was added. It is highly related to the /NODE switch of the START command which is still working.
II. And the commands:
TAB is no longer autocomplete file names.Only in command prompt.
Starting a file by its extension - in disabled extensions mode the cmd will not get into account the HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts and //HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ext\OpenWithList and will start directly only a "programs" - i.e. extensions described in %PATHEXT% variable.
Variable manipulation - replacements and substrings will not work with disabled extensions. Which means only with disabled extensions you can print the value of %=::% - undocumented variable that is defined only when the command prompt is not started with admin privileges.
ASSOC and FTYPE - not available under disabled extensions - related to the first point here.
CALL - no longer able to call labels but only other scripts.Argument modifiers are no longer supported (also in FOR command) - %~0, %~dp0, %~n0 will no longer working. Only plain access with %0,%1 ..
COLOR - no longer available. Though colors can be changed with other scripts.
IF - /I is not working. Three letter comparison operators (that can be used for integer comparisons) are not available - only ==. CMDEXTVERSION is not part of the options as well as DEFINED.
EXIT - Exit /b works ,but prints an error message as it tries to find the :EOF label. The printed error is not 'officially' documented.Only observable in batch files.
FOR - reduced to only basic for loops - the additional switches /F , /D , /R , /L are not available. Token modifiers will no longer working in a similar fashion of CALL arguments.
SHIFT - the /N switch is not working ,though is not 'officially' documented.
GOTO - :EOF label is not available anymore.
SET - switches /P and /A are no more working. Does not accept double quotes as part of the command (e.g. set "variable=value" will result in an error). Listing variables starting with a string is no longer possible (e.g. set path will cause an error). Only plain setting a variable value.
PUSHD - does no longer map temporary drives to UNC paths, and POPD does not delete such drives. Not officially documented.
PROMPT - does not support $+ and $M special codes
The HELP command deserves a note as it will not detect if the extensions are on or off and will print help messages as the extensions are available.
As COLOR , ASSOC , FTYPE (and IF DEFINED is not working) are the only commands turned off with disabled extensions are one of the best option to check if they are enabled (though COLOR will change the colour scheme).
But their output will be too big without the help message and help message is also not so small - which means even the redirection to null will slow down the check.
Eventually this also can be used to detect if the extensions are enabled:
call ::eof >nul 2>&1 && (
echo extensions are enabled
)||(
echo extensions are disabled
)
Or using assoc or ftype with an extension that will be available by default:
assoc .bat >nul 2>&1 && (
echo extensions are enabled
)||(
echo extensions are disabled
)
with ftype:
ftype batfile >nul 2>&1 && (
echo extensions are enabled
)||(
echo extensions are disabled
)
Related
In a post build step i want check if OpenSSH.Client and OpenSSH.Server is installed and install it if it is not there. Checking the installed features with Powershell needs administrative privileges.
Therefore i test the existence of the relevant OpenSSH commands with the following code (extract of long script)
SET /a res=0
SET /a three=3
%windir%\system32\OpenSSH\ssh-keygen.exe /?
echo Errorlevel %ERRORLEVEL%
IF %ERRORLEVEL%==%three% (SET /a res=%res%+1)
%windir%\system32\OpenSSH\ssh-keyscan.exe /?
echo Errorlevel %ERRORLEVEL%
IF %ERRORLEVEL%==%three% (SET /a res=%res%+1)
SET /a check=0
IF %res% == %check% (
echo already installed
goto skipopenSSH
)
echo installation
:skipopenSSH
By checking the existence of the commands no admin privileges are necessary for the check so a normal build will not cause a administrative popup.
On cmd.exe it works fine, but as a post build step in Visual Studio both commands in %windir%\systrem32\OpenSSH are not found, although the variable is expanded to the same c:\Windows\System32\OpenSSH\*.exe as if executed on commandline.
After trying the different find mechanisms which all fail i made a test batch file C:\Windows\System32\OpenSSH\ssh-keyscan.exe /?
which leads to a file not found error if executed as a post build step. So the real question should be: Modifies the visual studio build step commandline the path?
The directory OpenSSH exists in directory %SystemRoot%\System32 with the files ssh-keygen.exe and ssh-keyscan.exe depending on version of Windows 10. The directory %SystemRoot%\System32 is for 64-bit applications on 64-bit Windows. But Visual Studio is a 32-bit application and for that reason 32-bit Windows command processor in directory %SystemRoot%\SysWOW64 is executed to process the batch file with the commands to execute as post build step.
Microsoft documented with WOW64 Implementation Details, File System Redirector and Registry Keys Affected by WOW64 and other documentation pages how Windows on Windows 64 works.
All file system accesses to %windir%\system32\OpenSSH in the batch file processed by 32-bit %SystemRoot%\SysWOW64\cmd.exe being started by 32-bit Visual Studio results in the approach to access %SystemRoot%\SysWOW64\OpenSSH which does not exist at all. There is no subdirectory OpenSSH in Windows system directory for 32-bit applications.
One solution would be using the following code for the batch file executed as post build step.
rem This simple check is for 32-bit Windows and for 64-bit Windows with batch
rem file executed in 64-bit environment by 64-bit Windows command processor.
set FolderSSH=%SystemRoot%\System32\OpenSSH
if exist %FolderSSH%\ssh-keygen.exe if exist %FolderSSH%\ssh-keyscan.exe goto UseOpenSSH
rem This check is for 64-bit Windows with batch file executed
rem in 32-bit environment by 32-bit Windows command processor.
if exist %SystemRoot%\Sysnative\cmd.exe set FolderSSH=%SystemRoot%\Sysnative\OpenSSH
if exist %FolderSSH%\ssh-keygen.exe if exist %FolderSSH%\ssh-keyscan.exe goto UseOpenSSH
rem Search entire system drive on machines without Windows 10 or with older
rem versions of Windows 10 with OpenSSH not installed at all by default.
for /F "delims=" %%I in ('%SystemRoot%\System32\where.exe /R %SystemDrive%\ ssh-keygen.exe 2^>nul') do (
if exist "%%~dpIssh-keyscan.exe" for %%J in ("%%~dpI.") do set "FolderSSH=%%~fJ" & goto UseOpenSSH
)
echo ERROR: ssh-keygen.exe AND ssh-keyscan.exe not found.
rem More code to handle this use case.
goto :EOF
:UseOpenSSH
echo Found ssh-keygen and ssh-keyscan in: "%FolderSSH%"
rem More code to handle this use case with existing SSH tools.
The remarks explain most of the code. The inner FOR loop is used to get the full qualified name of the directory containing ssh-keygen.exe and ssh-keyscan.exe without backslash at end of the folder path to have FolderSSH defined always without a backlash at end for further usage in the batch file.
Please note that it is safe to use %FolderSSH%\ssh-keygen.exe and %FolderSSH%\ssh-keyscan.exe without surrounding " at beginning of the batch script as it is impossible that %FolderSSH% expands to a folder path containing a space or one of these characters &()[]{}^=;!'+,`~.
But "%FolderSSH%\ssh-keygen.exe" and "%FolderSSH%\ssh-keyscan.exe" must be used on the command lines below the label UseOpenSSH because it could happen that WHERE was used to find the two executables anywhere on system drive and for that reason %FolderSSH% could expand now to a folder path containing a space or a character with a special meaning for Windows command processor outside a double quoted argument string.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
goto /?
if /?
rem /?
set /?
where /?
Read also the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded where command line with using a separate command process started in background with %ComSpec% /c and the command line between ' appended as additional arguments on which 2^>nul became already 2>nul.
The following idea also uses the where.exe command, as mentioned in the comments. This one will search the system drive for ssh-keyscan.exe, and if found will ensure that ssh-keygen.exe is also located there. If both are located in the same place, then it is considered as already installed:
#Set "dirOpenSSH="
#For /F Delims^= %%G In (
'%__AppDir__%where.exe /R %SystemDrive%\ ssh-keyscan.exe 2^>NUL'
)Do #For /F Delims^= %%H In (
'%__AppDir__%where.exe "%%~dpG.":ssh-keygen.exe 2^>NUL'
)Do #Set "dirOpenSSH=%%~dpH"
#If Defined dirOpenSSH (
Echo OpenSSH is available at %dirOpenSSH%.
%__AppDir__%timeout.exe 3 /NoBreak>NUL
GoTo skipopenSSH
)
#Echo OpenSSH is not available, beginning installation.
#%__AppDir__%timeout.exe 3 >NUL
:skipopenSSH
Please note that where.exe and timeout.exe require a minimum OS version of Windows NT 6.x
As your application is currently a 32-bit one, the console session it is using is not accessing the 64-bit locations. To fix that you could replace %__AppDir__%where.exe, with %SystemRoot%\SysNative\where.exe
It is probably worth mentioning that in the versions of windows-10 with OpenSSH part of the base OS, the location, %SYSTEMROOT%\System32\OpenSSH\, should exist in %PATH%, so you should be able to find your files directly using them:
%__AppDir__%where.exe ssh-keygen.exe
%__AppDir__%where.exe ssh-keyscan.exe
For %I In (ssh-keygen.exe) Do #Echo %~$PATH:I
For %I In (ssh-keyscan.exe) Do #Echo %~$PATH:I
I have found on the systems which have upgraded from an earlier version of Windows, that many of the default locations using C:\Windows were replicated, but using %SYSTEMROOT%/%SystemRoot% instead.
In that state, the Where command and For's %~$PATH: failed to locate some items which did exist and should have been found.
Removing the duplicated items, I removed those which were carried over, (those not using variables), although removing either format seems to have worked fine.
Both where and %~$PATH: were then functioning as intended.
I was reading an article on ss64 about Environment variables in Command Prompt.
Later in the article there exists a table which states environment variables found commonly in Command Prompt. Some of the variables listed there are termed as Volatile (Read Only). A sentence found in the article states:-
Dynamic environment variables are read-only and are computed each time the variable is expanded. When all variables are listed with SET, these will not appear in the list. Do not attempt to directly SET a dynamic variable.
I understood the later two statements. But i can't understand the first one.
Doubts:-
%userprofile% is an non-volatile variable, which resolves to %SystemDrive%\Users\{username}, and %homepath% is a volatile variable which resolves to Users\{Username}. Both commands are quite similar (except for the systemdrive). Then why is one volatile and the other one non-volatile?
What is the criteria for a variable to be dynamic? what makes %appdata% (just an ex.) an non volatile variable?
Dynamic Variables are computed each time the variable is expanded, this makes sense for variables like %CD% %DATE% %TIME% %RANDOM% etc. as they would lose their functionality if they would be non-volatile. But how would it effect %homepath%?
Some non-volatile variables have sort-of dynamic components in them. Ex. %userprofile% has %SystemDrive% and {username} in it's path. Then how are those variables not dynamic?
There are three types of variables of which value is accessed using the syntax %variable% or !variable! on having enabled delayed environment variable expansion with using the option EnableDelayedExpansion of the command setlocal from within a Windows command prompt window or a batch file, i.e. using %SystemRoot%\System32\cmd.exe.
1. Persistent stored variables
There are environment variables stored persistent in Windows registry.
User variables stored in Windows registry under the key:
HKEY_CURRENT_USER\Environment
System variables stored in Windows registry under the key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
The user variables are defined for just the account in which user registry hive they are stored (file %UserProfile%\ntuser.dat). The system variables are defined for all accounts used on a Windows machine (file %SystemRoot%\System32\config\SYSTEM).
The persistent stored variables can be viewed, edited and deleted by opening the Windows Control Panel, clicking on System, clicking next (on left side) on Advanced system settings and clicking on button Environment Variables. The upper half is for the user variables of the current user account and the lower half is for the system variables since Windows XP.
There are defined by default as user variables only TEMP and TMP since Windows XP.
The list of predefined system variables is since Windows XP:
ComSpec
NUMBER_OF_PROCESSORS
OS
PATH
PATHEXT
PROCESSOR_ARCHITECTURE
PROCESSOR_IDENTIFIER
PROCESSOR_LEVEL
PROCESSOR_REVISION
TEMP
TMP
windir
There is on Windows Vista and newer Windows versions defined by default also the system variable PSModulePath.
None of the predefined system variables with exception of PATH and PATHEXT should be deleted or modified ever as this could cause lots of troubles which can even result in Windows not starting anymore. I would strongly recommend using a virtual machine on experimenting with the predefined system variables for which a backup of the entire virtual machine image exists before starting the experiments.
1.1 Backup of persistent stored variables
It is advisable to make a backup of the user and system variables before playing around with them by opening a command prompt window and running for example:
md C:\VariablesBackup 2>nul
%SystemRoot%\System32\reg.exe EXPORT HKCU\Environment "C:\VariablesBackup\UserVariables.reg"
%SystemRoot%\System32\reg.exe EXPORT "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "C:\VariablesBackup\SystemVariables.reg"
1.2 Restore of persistent stored variables
Restoring the user variables can be done from within a command prompt window on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /f
%SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\UserVariables.reg"
Restoring the system variables can be done from within a command prompt window opened as administrator on having made a backup before with:
%SystemRoot%\System32\reg.exe DELETE HKCU\Environment /f
%SystemRoot%\System32\reg.exe IMPORT "C:\VariablesBackup\SystemVariables.reg"
It is advisable to restart Windows on having restored user and system variables from a backup to make sure that really all processes use the restored variables.
1.3 Modification of PATH with a batch file
Batch file programmers thinking about modification of user or system PATH with a batch file should read first:
What is the reason for "X is not recognized as an internal or external command, operable program or batch file"?
Why are other folder paths also added to system PATH with SetX and not only the specified folder path?
How can I use a .bat file to remove specific tokens from the PATH environment variable?
How to search and replace a string in environment variable PATH?
Adding the current directory to Windows path permanently
ATTENTION: It is an absolute NO GO - NEVER EVER to use command SETX with %PATH% in a batch file to modify user or system PATH variable.
The only reason to modify user or system PATH with a batch file on installation of a program (executable or script) is that this program is designed for being mainly used by users from the Windows command line. A program is bad designed if it requires that its directory or one of its subdirectories is in PATH to work at all. A program is very bad designed if it adds a folder path to system PATH left to the folder paths defined by default by Windows.
The system PATH variable should start always with:
%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\Wbem
The Windows system directory is the directory containing most executables and dynamic linked libraries. For that reason it should be always the first directory searched for executables and libraries after the current directory.
2. Windows shell variables
There are a lot more Windows environment variables predefined as it can be seen on:
SS64: How-to: Windows Environment Variables
Wikipedia: Environment variable
These variables are defined by Windows shell which is by default explorer.exe being started on Windows start as Windows shell according to registry value Shell under registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
The Window shell elements noticed most by users are the Windows desktop, the Windows Start menu and the Windows taskbar with the system tray.
The Windows shell defines in its memory lots of environment variables depending on various values in Windows registry for the current user account not stored persistent in Windows registry as described above. The current list of environment variables is copied whenever a new process is created like starting an executable from Windows shell.
The list of environment variables defined by Windows shell consisting of the persistent stored user and system variables and the shell variables for current user account can be seen by opening a command prompt window and running the command SET without any additional argument.
Most of the shell variables are defined from the registry strings under
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Most of the registry string values exist in User Shell Folders of type REG_EXPAND_SZ and in Shell Folders of type REG_SZ. But there are some registry string values existing only under one of the two registry keys.
See also: How to create a directory in the user's desktop directory?
In this answer is explained in detail how Windows Explorer evaluates these registry string values and handles them on manual modification by a user using regedit.exe or reg.exe on the example of the shell folder Desktop.
The function CreateEnvironmentBlock and the private shell32 function RegenerateUserEnvironment are used by explorer.exe with GetUserNameExW and GetComputerNameExW to create the environment variables list copied by Windows Explorer on starting an executable from Windows shell using the Windows kernel library function CreateProcess.
See also the comments written by Eryk Sun on question Where are the environment variables for cmd.exe stored?
There are some environment variables on 64-bit Windows which depend on starting a 64-bit or a 32-bit executable. Microsoft documented them on WOW64 Implementation Details as follows:
64-bit process:
PROCESSOR_ARCHITECTURE=AMD64 or PROCESSOR_ARCHITECTURE=IA64 or PROCESSOR_ARCHITECTURE=ARM64
ProgramFiles=%ProgramFiles%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles%
CommonProgramW6432=%CommonProgramFiles%
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The ProgramW6432 and CommonProgramW6432 environment variables were added starting with Windows 7 and Windows Server 2008 R2.
32-bit process:
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=%PROCESSOR_ARCHITECTURE%
ProgramFiles=%ProgramFiles(x86)%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles(x86)%
CommonProgramW6432=%CommonProgramFiles%
The value of the environment variable PROCESSOR_ARCHITECTURE cannot be used to find out from within a batch file if the installed Windows is a 32-bit (x86) or a 64-bit (AMD64) Windows. The value depends on processing of the batch file by either 64-bit %SystemRoot%\Sysem32\cmd.exe or by 32-bit %SystemRoot%\SysWOW64\cmd.exe on 64-bit Windows.
See also the Microsoft documentations:
File System Redirector
Registry Redirector
The usage of environment variables defined by the Windows shell must be done wisely on writing a batch file which should be designed for execution by other accounts on same or a different Windows machine. Many batch files working fine in environment of author of the batch file do not work in environment set up on running the same batch file as scheduled task with system account or on a different machine because of the differences in environment variables list.
The environment variables defined by a process on starting an executable with the Windows kernel library function CreateProcess determine the environment variables which the started executable can use.
Most applications use CreateProcess with value null for the parameter lpEnvironment. Therefore CreateProcess makes a copy of the current environment variables of the current process. For that reason every executable started from Windows desktop, start menu or taskbar gets the environment variables as defined by the explorer.exe instance running as Windows shell.
A really very good coded executable or script using environment variables which are defined by default on Windows verify explicitly that every used environment variable is really defined and use otherwise a suitable default value like C:\Windows on environment variable SystemRoot is not defined with checking if there is really a directory C:\Windows and exit with an appropriate error message on an important environment variable not defined before causing perhaps damage.
SystemRoot is an example for a Windows shell variable defined by explorer.exe as environment variable which is not determined by the registry string values of the shell folders. Some environment variable values should not be modified by a user at any time independent on its real source which no script author ever needs to know as being a Windows implementation detail.
3. Dynamic variables of Windows command processor
There are some variables listed in help of command SET output on running in a command prompt window set /? which cannot be found in the list of environment variables on running just set.
These variables are:
CD
DATE
TIME
RANDOM
ERRORLEVEL
CMDEXTVERSION
CMDCMDLINE
HIGHESTNUMANODENUMBER
The dynamic variables are internal variables of cmd.exe. So these variables are available only in a Windows command prompt window which is a running cmd.exe process or a batch file processed by cmd.exe. The dynamic variables are not available in other executables or scripts as these variables are not environment variables.
The most common used dynamic variables are:
CD
The current directory path not ending with a backlash, except the current directory is the root directory of the drive.
DATE
The current local date in format as defined for the account in region and language settings of Windows.
TIME
The current local time in format as defined for the account in region and language settings of Windows.
ERRORLEVEL
Exit value of previously executed command or program.
RANDOM
A random decimal number between 0 and 32767.
There are some more dynamic variables, but they are rarely used in batch files.
There is additionally the variable __AppDir__ containing the path of currently running cmd.exe always ending with a backslash which is not documented by Microsoft. I recommend not using this undocumented variable as there is no guarantee that a future version of cmd.exe still has this variable. __AppDir__ is on 64-bit Windows, for example, %SystemRoot%\System32\ on 64-bit %SystemRoot%\System32\cmd.exe currently running, or %SystemRoot%\SysWOW64\ on 32-bit %SystemRoot%\SysWOW64\cmd.exe currently running, or any other path on copying cmd.exe to any other folder and starting this copy of cmd.exe. There are some more undocumented dynamic variables listed on SS64 page How-to: Windows Environment Variables which should be used only with special care for the same reason.
The values of the most used dynamic variables are changed dynamically by the Windows command processor itself while the values of the environment variables change only if command SET is used during execution of a batch file to redefine an environment variable. This is an important difference between environment and dynamic variables.
4. Accessing the values of dynamic variables
Every environment variable can be deleted or redefined in the local environment of a processed batch file. There is no environment variable read-only.
cmd.exe contains internally in its code the file extension list .COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC which is used as value for PATHEXT if the environment variable PATHEXT does not exist in local environment variables list to be able to find nevertheless scripts and executables specified on command line or in a batch file without file extension. But cmd.exe does not contain a list of folder paths as fallback list if the environment variable PATH does not exist in local environment. So a batch file writer should be careful on modification of local environment variable PATH for whatever reason.
The values of the dynamic variables are referenced like the values of the environment variables with %variable% or !variable! on enabled delayed expansion. But the Windows command processor always searches first in the current list of environment variables if there is a variable with specified name. Only if there is no environment variable with that name, cmd searches next in its internal list of dynamic variables if there is one with the specified name.
The current value of a dynamic variable cannot be accessed anymore on definition of an environment variable with same name as a dynamic variable. For that reason a batch file writer should never use one of the dynamic variable names as name for an environment variable.
Here is a code which demonstrates what happens if a batch file writer has the really not good idea to define an environment variable with name ERRORLEVEL.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo/
echo Define environment variable ERRORLEVEL with string value "014".
echo/
set ERRORLEVEL=014
if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.
if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.
if errorlevel 0 (
if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.
) else echo IF: ERRORLEVEL is less than 0.
echo/
echo Delete the environment variable ERRORLEVEL.
echo/
set ERRORLEVEL=
if %ERRORLEVEL% EQU 12 (echo EQU: ERRORLEVEL is 12.) else echo EQU: ERRORLEVEL is not 12.
if %ERRORLEVEL% == 014 (echo ==: ERRORLEVEL is 14.) else echo ==: ERRORLEVEL is not 14.
if errorlevel 0 (
if not errorlevel 1 (echo IF: ERRORLEVEL is 0.) else echo IF: ERRORLEVEL is greater than 0.
) else echo IF: ERRORLEVEL is less than 0.
echo/
echo In all test cases the value of dynamic variable ERRORLEVEL was 0.
echo/
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output of this batch file is:
Define environment variable ERRORLEVEL with string value "014".
EQU: ERRORLEVEL is 12.
==: ERRORLEVEL is 14.
IF: ERRORLEVEL is 0.
Delete the environment variable ERRORLEVEL.
EQU: ERRORLEVEL is not 12.
==: ERRORLEVEL is not 14.
IF: ERRORLEVEL is 0.
In all test cases the value of dynamic variable ERRORLEVEL was 0.
It can be seen on debugging the batch file that the first IF condition if %ERRORLEVEL% EQU 12 results in replacing %ERRORLEVEL% by the string 014 because of there is the environment variable ERRORLEVEL defined with this string value. The function wcstol is used by command IF because of operator EQU to convert the string 014 being interpreted as octal number because of the leading 0 and the string 12 to 32-bit signed integer values and compare them on equality. Therefore the first condition is true.
The second IF condition if %ERRORLEVEL% == 014 results also in replacing %ERRORLEVEL% by the string 014 because of there is the environment variable ERRORLEVEL defined with this string value. But now the function lstrcmpW is used by command IF because of operator ==. Therefore the second condition is also true.
The third IF condition uses the recommended syntax explained by help of command IF output on running if /? in a command prompt window. It can be seen that using the recommended syntax results in evaluation of the value of the internal dynamic variable ERRORLEVEL of Windows command processor even if an environment variable is defined with name ERRORLEVEL. The recommended syntax to evaluate the exit code of a previously executed command or program works always and anywhere within a batch file as demonstrated here.
See also:
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Further it must be taken into account that accessing current value of a dynamic variable is always on variable reference being expanded by the Windows command processor and not real execution of the command or executable.
A code to demonstrate that difference:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /L %%I in (1,1,3) do (
echo %%DATE%% %%TIME%% is: %DATE% %TIME%
echo ^^!DATA^^! ^^!TIME^^! is: !DATE! !TIME!
echo %%RANDOM%%/^^!RANDOM^^!: %RANDOM%/!RANDOM!
if exist %SystemRoot%\System32\timeout.exe (
%SystemRoot%\System32\timeout.exe /T 3 /NOBREAK >nul
) else (
%SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul
)
)
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output is for example:
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:30,68
%RANDOM%/!RANDOM!: 18841/27537
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:33,12
%RANDOM%/!RANDOM!: 18841/16705
%DATE% %TIME% is: 31.01.2021 13:54:30,67
!DATA! !TIME! is: 31.01.2021 13:54:36,16
%RANDOM%/!RANDOM!: 18841/32668
%DATE% %TIME% results in printing to console window three times the same date/time because of these two variable references are expanded already by the Windows command processor on parsing the entire command block before command FOR is executed at all. %RANDOM% results in printing three times the same number for the same reason while !RANDOM! prints usually three different numbers.
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Variables are not behaving as expected
if errorlevel number and if not errorlevel number work also within a command block!
The dynamic environment variables can be accessed only with command extensions enabled as by default. Otherwise Windows command processor emulates COMMAND.COM behavior (more or less) of MS-DOS and Windows 95/98/ME not supporting dynamic variables at all as demonstrated by this code:
#echo off
setlocal DisableExtensions DisableDelayedExpansion
echo/
echo With command extensions disabled:
echo/
echo Date/time is: %DATE% %TIME%
echo Current dir: "%CD%"
endlocal
setlocal EnableExtensions DisableDelayedExpansion
echo/
echo With command extensions enabled:
echo/
echo Date/time is: %DATE% %TIME%
echo Current dir: "%CD%"
echo/
for %%I in (%CMDCMDLINE%) do if /I "%%~I" == "/c" pause & goto EndBatch
:EndBatch
endlocal
The output is for example:
With command extensions disabled:
Date/time is:
Current dir: ""
With command extensions enabled:
Date/time is: 31.01.2021 14:17:42,92
Current dir: "C:\Temp\Development & Test!"
The values of dynamic variables can be only read. It is not possible to modify the value of a dynamic variable with command SET as it results in the definition of an environment variable with the name of a dynamic variable which takes precedence over the dynamic variable.
I'm pretty sure this is a simple command but I just couldn't find it anywhere.
Example file content:
A-VERY-LONG-LINE-OF-GARBAGE-VERSION123-CONTINUE-LONG-LINE-OF-GARBAGE
1.)Assume the line is really really long
2.)I need to find out what version it is. I know it contains Version but I wouldn't know it is version123.
3.)What I want is a command that would go through the file looking for the sub-string "VERSION" and if it finds it prints out VERSION123 instead of the super duper long line that would most probably causes the system to freeze.
Thank you
Assuming that version is purely numeric and does not start with zero, the following should do it:
set VAR=A-VERY-LONG-LINE-OF-GARBAGE-VERSION123-CONTINUE-LONG-LINE-OF-GARBAGE
set /A VAR=%VAR:*VERSION=%
echo VERSION%VAR%
If there occur multiple VERSION portions, the first one is taken.
Note, that this works for Windows command prompt (cmd.exe) only, it will not work for MS-DOS (command.com) due to set /A which is not supported there (I'm even not sure whether the string substitution syntax works there)!
In case the version code is not purely numeric, you might use the following:
set VAR=A-VERY-LONG-LINE-OF-GARBAGE-VERSION123-CONTINUE-LONG-LINE-OF-GARBAGE
set VAR=%VAR:*VERSION=%
for /F "tokens=1 delims=- eol=-" %%L in ("%VAR%") do (set VAR=%%L)
echo VERSION%VAR%
This relies on the fact that the - character delimits the version code.
If you want to try this in the command prompt directly rather than in a batch file, replace %%L by %L (twice).
I'm currently using the serve script to serve up directories with Node.js on Windows 7. It works well in the MSYS shell or using sh, as I've put node.exe and the serve script in my ~/bin (which is on my PATH), and typing just "serve" works because of it's Shebang (#!) directive which tells the shell to run it with node.
However, Windows Command Prompt doesn't seem to support normal files without a *.bat or *.exe extension, nor the shebang directive. Are there any registry keys or other hacks that I can get to force this behavior out of the built-in cmd.exe?
I know I could just write up a simple batch file to run it with node, but I was wondering if it could be done in a built-in fasion so I don't have to write a script for every script like this?
Update: Actually, I was thinking, is it possible to write a default handler for all 'files not found' etc. that I could automatically try executing within sh -c?
Thanks.
Yes, this is possible using the PATHEXT environment variable. Which is e.g. also used to register .vbs or .wsh scripts to be run "directly".
First you need to extend the PATHEXT variable to contain the extension of that serve script (in the following I assume that extension is .foo as I don't know Node.js)
The default values are something like this:
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
You need to change it (through the Control Panel) to look like this:
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.FOO
Using the control panel (Control Panel -> System -> Advanced System Settings -> Environment Variables is necessary to persist the value of the PATHEXT variable.
Then you need to register the correct "interpreter" with that extension using the commands FTYPE and ASSOC:
ASSOC .foo=FooScript
FTYPE FooScript=foorunner.exe %1 %*
(The above example is shamelessly taken from the help provided by ftype /?.)
ASSOC and FTYPE will write directly into the registry, so you will need an administrative account to run them.
Command prompt does not support shebang , however there are a lot hybrid techniques for different languages that you allow to combine batch and other languages syntax in one file.As your question concerns node.js here's a batch-node.js hybrid (save it with .bat or .cmd extension):
0</* :{
#echo off
node %~f0 %*
exit /b %errorlevel%
:} */0;
console.log(" ---Self called node.js script--- ");
console.log('Press any key to exit');
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on('data', process.exit.bind(process, 0));
It is possible to be done with many other languages like Ruby,Perl,Python,PHP and etc.
Here is a simple way to force windows to support shebang however it has a caveat regarding the file naming. Copy the following text in to a batch file and follow general idea in REM comments.
#echo off
REM This batch file adds a cheesy shebang support for windows
REM Caveat is that you must use a specific extension for your script files and associate that extension in Windows with this batch program.
REM Suggested extension is .wss (Windows Shebang Script)
REM One method to still easily determine script type visually is to use double extensions. e.g. script.pl.wss
setlocal enableextensions disabledelayedexpansion
if [%1] == [] goto usage
for /f "usebackq delims=" %%a IN (%1) do (
set shebang=%%a
goto decode_shebang
)
:decode_shebang
set parser=%shebang:~2%
if NOT "#!%parser%" == "%shebang%" goto not_shebang
:execute_script
"%parser%" %*
set exit_stat=%errorlevel%
echo script return status: %exit_stat%
goto finale
:not_shebang
echo ERROR script first line %shebang% is not a valid shebang
echo maybe %1 is not a shebanged script
goto finale
:usage
echo usage: %0 'script with #! shebang' [scripts args]+
echo This batch file will inspect the shebang and extract the
echo script parser/interpreter which it will call to run the script
:finale
pause
exit /B %exit_stat%
No, there's no way to "force" the command prompt to do this.
Windows simply wasn't designed like Unix/Linux.
Is there a shell extension that does something similar?
Not that I've heard of, but that should be asked on Super User, not here.
There's no way to execute random file, unless it is an actual executable binary file. Windows CreateProcess() function just not designed for it. The only files it can execute are those with MZ magic or with extensions from %PATHEXT% list.
However, CMD itself has a limited support for custom interpreters through EXTPROC clause. The limitation is that interpreter should also support and omit this clause in its execution.
#npocmaka Thanks for the hint! After some trial and error I found the equivalent for a batch/php hybrid is as follows:
<?/** :
#echo off
C:\tools\php81\php.exe -d short_open_tag=On %~f0 %*
exit /b
*/ ?>
<?php
header('Location: example.com/');
print("<body><h1>Hello PHP!<h1></body>");
?>
I've run into a weird error with a Qt program running on Windows. The program uses QProcess to spawn a child process wit two arguments. The program and arguments passed to the QProcess::start() method are of the form:
"batchfile.bat" "--option1=some_value" "--option2=some_other_value\with_a\path"
For some reason by the time those options get to the batchfile for processing the equals signs have been converted to spaces and it now looks like:
"batchfile.bat" "--option1 some_value" "--option2 some_other_value\with_a\path"
because of this, the processing fails. Any ideas what could be causing the equal signs to be replaced by spaces? I'm using the mingw build of the QT 4.6.3 framework found on the Qt download page.
EDIT:
Here's the actual code. I didn't write it (I'm a complete Qt noob) but I've got to try to get it working. It's part of an automated build system that runs on two versions of RHEL (4 and 5), OS X, and Windows. And it works fine everywhere but Windows.
QProcess sconsProcess;
sconsProcess.setWorkingDirectory(build.getBuildLocation());
sconsProcess.setProcessChannelMode(QProcess::MergedChannels);
qDebug()<<"Starting scons process:"<<build.getSconsLocation()<<QString("--variant=%1-%2").arg(build.getOs()).arg(build.getVariant())<<
QString("--source-release=%1").arg(build.getSettings().getSetting("sourceReleaseLocation", QStringList()<<"BUILDLOCATION"<<"VERSION",
QStringList()<<build.getBuildLocation()<<build.getBuildPackage().getVersion()).toString());
sconsProcess.start(build.getSconsLocation(), QStringList()<<QString("--variant=%1-%2").arg(build.getOs()).arg(build.getVariant())<<
QString("--source-release=%1").arg(build.getSettings().getSetting("sourceReleaseLocation", QStringList()"BUILDLOCATION"<<"VERSION",
QStringList()<<build.getBuildLocation()<<build.getBuildPackage().getVersion()).toString()));
qDebug()<<"Source release build process started";
The actaul values that translates into in Windows (the bit that gets printed out in the first qDebug() print call) is:
DEBUG: Starting scons process: "V:\Glast_Software\Toaster\tools\Python2.5\Scripts\scons-1.3.0.bat" "--variant=Windows-i386-32bit-vc71-Debug" "--source-release=V:\Glast_Software\Toaster\ReleaseManagerBuild\Windows-i386-32bit-vc71\Debug\ScienceTools\LATEST-1-3163\ScienceTools-LATEST-1-3163-source.zip"
However inside the scons-1.3.0.bat (I had it echo all the commands executed) the passed parameters look like:
"--variant Windows-i386-32bit-vc71-Debug" "--source-release V:\Glast_Software\Toaster\ReleaseManagerBuild\Windows-i386-32bit-vc71\Debug\ScienceTools\LATEST-1-3163\ScienceTools-LATEST-1-3163-source.zip"
with the equal signs missing.
EDIT (6/29/10):
I should add that this system is designed to run on a small Windows batch farm using the LSF batch queuing system. It only fails when the process is running as a batch job. When I run this program from the command line on one of the batch machines, it works perfectly and does exactly what it is supposed to do. So maybe it is an environment problem.
There's a good chance that this is because the quotes aren't making it through (they may need to be escaped, see the docs for QProcess::start()).
cmd.exe treats equals signs in command line options that aren't quoted as a separator between arguments similar to a space or tab. Just one of very many bits of oddness in Windows cmd scripting:
C:\test>type c:\util\cmdechoargs.cmd
#echo off
setlocal
set /a i=0
echo args[*]: %*
:loop
if {%1} == {} goto :eof
echo argv[%i%]: %1
set /a i=%i% + 1
shift
goto :loop
C:\test>cmdechoargs testing=123
args[*]: testing=123
argv[0]: testing
argv[1]: 123
C:\test>cmdechoargs "testing=123"
args[*]: "testing=123"
argv[0]: "testing=123"
The best documentation I've come across for how to handle command line arguments in Windows cmd scripts is Tim Hill's "Windows NT Shell Scripting" - get one used for only a penny!
Based on the examples given in your update, I think you might want your options that have equals signs in them to have quotes embedded inside them:
"\"--variant=%1-%2\""
"\"--source-release=%1\""
Edit -- new material
The following script has a routine that will strip the quotes off of an argument passed to a cmd script. The routine returns the 'dequoted' argument in an environment variable named RET using an idiom/technique from Tim Hill's book I mentioned above. I stole some of the dequoting code from an example here: http://ss64.com/nt/syntax-esc.html, but made it a bit more robust to handle empty quotes.
#echo off
setlocal
set /a i=0
echo args[*]: %*
:loop
if {%1} == {} goto :eof
echo.
echo argv[%i%]: %1
call :dequote %1
set dequoted_arg=%RET%
echo argv[%i%] ^(dequoted^): %dequoted_arg%
set /a i=%i% + 1
shift
goto :loop
:dequote
setlocal
SET _string=###%1###
if {%_string%} == {######} goto :dequote_empty
if {%_string%} == {###""###} goto :dequote_empty
SET _string=%_string:"###=%
SET _string=%_string:###"=%
SET _string=%_string:###=%
goto :dequote_done
:dequote_empty
set _string=
:dequote_done
endlocal & (set RET=%_string%) & goto :eof
This kind of thing is why you want to avoid (in my opinion) cmd scripts except for the simplest of tasks. But, I hope this helps you pass unquoted arguments to your scons process through your batch file.
Have you tried escaping the = signs? Also, the paths in your example surely need escaping of the \ character.