I am learning batch scripting since it's very useful for setting some quick custom context menu options of Windows user's choice to get file and it's parent directory's path.
Now I know following command to get the passed argument as file path and copy it to clipboard:
cmd /c (echo.|set /p=""%1"") | clip
But then why isn't following syntax working as expected when set in Context menu ?
cmd /c (echo.|set /p=""%~dp1"") | clip
Is it a variable expansion issue ? Please guide as to why it isn't working and how to resolve it so that it expands properly.
Sample of registry entry wherein I am permanently setting the command to be run, with switches, variable expansions etc.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\Copy File's Parent Path]
#="Copy File's Parent Path"
[HKEY_CURRENT_USER\Software\Classes\*\shell\Copy File's Parent Path\Command]
#="cmd.exe /c (echo.|set /p=\"\"%~dp1\"\") | clip"
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\Copy Files Path\command]
#="cmd /c (echo. | set /p =\"\"%1\"\") | clip"
[HKEY_CURRENT_USER\Software\Classes\*\shell\Copy Files Parent Path\command]
#="cmd /c (echo. | set /p =\"\"%w\"\") | clip"
Replace %1 with %w. 2 registry entries to demonstrate
%1 as Copy Files Path and %w as Copy Files Parent Path.
Reference from Extending Shortcut Menus at the bottom of the page.
%0 or %1 the first file parameter. For example “C:\Users\Eric\Desktop\New Text Document.txt”. Generally this should be in quotes and the applications command line parsing should accept quotes to disambiguate files with spaces in the name and different command line parameters (this is a security best practice and I believe mentioned in MSDN).
%N (where N is 2 - 9), replace with the nth parameter
%* replace with all parameters
%~ replace with all parameters starting with and following the second parameter
%d desktop absolute parsing name of the first parameter (for items that don’t have file system paths)
%h hotkey value
%i IDList stored in a shared memory handle is passed here.
%l long file name form of the first parameter. Note win32 applications will be passed the long file name, win16 applications get the short file name. Specifying %L is preferred as it avoids the need to probe for the application type.
%s show command
%v for verbs that are none implies all, if there is no parameter passed this is the working directory
%w the working directory
Variables like %1, %*, %V in registry, are placeholders which will be resolve and replaced by Windows Shell component (Win32 Shell). They just happened to be similar to the variables that is used by Windows Command Processor(CMD.EXE) at Command Prompt or in batch files, But they are in no way related to each other.
For instance with CMD.EXE %1 can only be used within batch files it has no meaning at Command Prompt or when passed as a command by /C or /K switch. In your registry sample %1 will be resolved by Windows Shell not by CMD.EXE so you can not use things like %~dp0 or %~dp1 in registry, They have no meaning to Windows Shell.
So you have to take %1 by CMD.EXE (which have been automatically replaced by its actual value) and do processing with it in a FOR loop to obtain the directory path of the file.
This is the actual command that will extract path information from file name:
cmd.exe /e:on /d /s /c "for %%a in ("%1") do #(set /p "=%%~dpa")<nul | clip"
It can be entered as is, in Registry Editor to the (default) value of the appropriate Key e.g: HKEY_CURRENT_USER\Software\Classes\*\shell\Copy Path to clipboard\Command for current user or HKEY_CLASSES_ROOT\*\shell\Copy Path to clipboard\Command for all users.
And the string for registry script:
#="cmd.exe /e:on /d /s /c \"for %%a in (\"%1\") do #(set /p \"=%%~dpa\")<nul | clip\""
A sample .REG script will look like this:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\Copy Path to clipboard\Command]
#="cmd.exe /e:on /d /s /c \"for %%a in (\"%1\") do #(set /p \"=%%~dpa\")<nul | clip\""
When you want to pass a string that contains percents(%) you have to escape the percents by doubling them, pretty much the same as you do in batch files.
%W, which generally holds the working directory, when you right click on a file, would be the files' parent.
You could therefore try this:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\HoldParent]
#="Parent to Clipboard"
[HKEY_CURRENT_USER\Software\Classes\*\shell\HoldParent\command]
#="Cmd /C \"Echo %W|Clip\""
You could extend this a little for potential unicode/character issues, but in general it should perfom as required without.
Edit
To extend it a little, (including no newline), perhaps:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\*\shell\HoldParent]
#="Parent to Clipboard"
[HKEY_CURRENT_USER\Software\Classes\*\shell\HoldParent\command]
#="Cmd /Q /D /U /C \"Set/P \"=%W\"<Nul|Clip\""
Related
So I am creating a batch file to store a selected folder's path into a text file to refer to it later on using another batch script...
I created registry entries to include a right-click context menu for any folder which triggers this specific batch file.
Basically When you right click a folder and click "Send to Script" it is supposed to copy the whole path / location of the right clicked folder.
To do so I am using the following command:
SET TargetDir=%~1
I also tried using %1and I also tried using the following code with delimiters
FOR /f "delims=;" %%a in ("%~1") do (
#echo %%a
)
The problem is that CMD is automatically trimming everything after the first space and since this is a path I am copying, I want to keep all the spaces and the path as is
Ex. If I use the command on a Folder such as "C:/folder/subfolder" the copyng is done correctly But If I use the command on a Folder such as "C:/folder/sub folder" the copying is done incorrectly and will only give me "C:/folder/sub" removing all the rest found after the first space detected.
Registry Entries
[HKEY_CLASSES_ROOT\Directory\shell\send-to-script]
"MUIVerb"="Send To Script"
"SubCommands"="sendscript"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\sendscript]
#="Send To Script"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\sendscript\command]
#="C:\\scripts\\pathtotext.bat %1"
Thanks for your help
You should change your registry key [...\sendscript\command] to
#="C:\\temp\\blob.bat \"%1\""
And in your batch you should use
REM The DisableDelayedExpansion is for preserve "!" in path names
setlocal DisableDelayedExpansion
SET "TargetDir=%~1"
setlocal EnableDelayedExpansion
(
echo target is !TargetDir!
) > C:\scripts\target.log
if "reseverdFolder" == "!TargetDir!" echo This folder is reserved
The batch file is not correct registered. For example look with regedit on value of registry key:
HKEY_CLASSES_ROOT\rtffile\shell\open\command
The value is displayed as:
"%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE" "%1"
So there are two arguments which are both enclosed in double quotes:
Argument 0 is the application to run with full path, file name and file extension enclosed in double quotes as path contains a space character.
Argument 1 is the name of the RTF file referenced with %1 passed by Windows Explorer with full path, file name and file extension which of course can also contain a space or any of the characters &()[]{}^=;!'+,`~ which require also enclosing entire argument in double quotes as output by cmd.exe on running it in a command prompt window with cmd /? on last help page.
So you need in your *.reg file used to import into Windows registry:
#="\"C:\\scripts\\pathtotext.bat\" \"%1\""
This string value is displayed in registry editor as:
"C:\scripts\pathtotext.bat" "%1"
And then you can use %1 or %~1 in your batch file as explained by help of command CALL output on running in a command prompt window on execution of call /?.
SET "TargetDir=%*"
Since the parameter apparently being supplied is C:\folder\sub folder then %~1 selects only the first supplied parameter of what cmd sees as two parameters.
%* means "the whole tail"
and echoing %* should show you exactly what cmd is seeing.
I am writing a batch script that installs some applications from MSI files from the same folder.
When I write those commands in command prompt window, all is fine and all the commands work properly.
But when I write them into the batch script, suddenly most of the commands such as XCOPY, msiexec, DISM result in an error message like:
'XCOPY' is not recognized as an internal or external command, operable program or batch file.
After googling it for a while, I saw a lot of comments related to the environment variable PATH which should contain C:\Windows\system32 and I made sure its included in the PATH. Also found a lot of answers about writing the full path which I already tried and it didn't work.
I'm working on Windows server 2012.
This is the code of my batch file:
#echo off
set path=C:\ rem default path
rem get the path as parameter to the script:
set argC=0
for %%x in (%*) do Set /A argC+=1
if %argC% gtr 0 (set path=%1%)
IF %ERRORLEVEL% NEQ 0 (
echo %me%: something went wrong with input directory
)
echo Destenation: %path%
SETLOCAL ENABLEEXTENSIONS
SET me=%~n0
SET parent=%~dp0
echo %me%: starting installation of Python 2.7 64bit and Apache 64 bit
REM install .net 3.5
DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:installationMediaDrive:\sources\sxs
msiexec /i ".\py\python-2.7.amd64.msi" TARGETDIR=%path%/Python27 /passive /norestart ADDLOCAL=ALL
mkdir %path%\Apache24
XCOPY /e /Q ".\Apache24" %path%\Apache24
It looks like the batch file should support an optionally specified path to installation directory as first parameter. The code used to check for existence of this optional folder path is very confusing. There are definitely easier methods to check for an optional parameter as it can be seen below.
The main problem is redefining environment variable PATH which results in standard console applications of Windows stored in directory %SystemRoot\System32 and other standard Windows directories are not found anymore by command interpreter cmd.exe on execution of the batch file.
In general it is required to specify an application to execute with full path, file name and file extension enclosed in double quotes in case of this complete file specification string contains a space character or one of these characters &()[]{}^=;!'+,`~ as explained in last paragraph on last output help page on running in a command prompt window cmd /?.
But mainly for making it easier for human users to execute manually applications and scripts from within a command prompt window, the Windows command interpreter can also find the application or script to run by itself if specified without path and without file extension.
So if a user enters just xcopy or a batch file contains just xcopy, the Windows command interpreter searches for a file matching the pattern xcopy.* which has a file extension as defined in semicolon separated list of environment variable PATHEXT first in current directory and if no suitable file found next in all directories in semicolon separated list of environment variable PATH.
There are 3 environment variables PATH:
The system PATH as stored in Windows registry under:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
The folder paths in system PATH are used by default for all processes independent on used account.
The user PATH as stored in Windows registry under:
HKEY_LOCAL_MACHINE\Environment
The folder paths in user PATH are used by default only for all processes running using the account on which the user PATH was set.
The local PATH just hold in memory in currently active environment of running process.
The system and the user PATH are concatenated by Windows to a single local PATH for processes.
Every time a process starts a new process like Windows Explorer starting Windows command interpreter for execution of a batch file, a copy of the environment table of currently running process is created by Windows for the new process. So whatever a process changes on its own local copy of environment variables has no effect on all other already running processes. The local changes on the environment variables are effective only on own process and all processes started by the process modifying its variables.
On starting the batch file the variables PATH and PATHEXT have the values as displayed on running in a command prompt window opened under same user account as used on starting the batch file the command set PATH listing all variables starting with PATH case-insensitive in name.
Now let us look on the second line of the batch file:
set path=C:\ rem default path
This line redefines the local PATH environment variable. Therefore the environment variable PATH being effective for the command process executing the batch file and all applications started by this batch file does not contain anymore C:\Windows\System32;C:\Windows;..., but contains now just this very strange single folder path.
C:\ rem default path
rem is an internal command of cmd.exe and must be written on a separate line. There is no line comment possible in batch code like // in C++ or JavaScript. For help on this command run in a command prompt window rem /?.
On running the batch file without an installation folder path as first argument, the result is that Windows command interpreter searches for dism.*, msiexec.* and xcopy.* just in current directory as there is surely no directory with name rem default path with lots of spaces/tabs at beginning in root of drive C:.
Conclusion: It is no good idea to use path as variable name for the installation folder path.
Another mistake in batch code is using %1% to specify the first argument of the batch file. This is wrong as the arguments of the batch file are referenced with %1, %2, ... Run in a command prompt window call /? for help on referencing arguments of a batch file and which possibilities exist like %~dp0 used below to get drive and path of argument 0 which is the batch file name, i.e. the path of the folder containing the currently running batch file.
I suggest using this batch code:
#echo off
setlocal EnableExtensions
set "SourcePath=%~dp0"
set "BatchName=%~n0"
if "%~1" == "" (
echo %BatchName% started without an installation folder path.
set "InstallPath=C:\"
goto StartInstalls
)
rem Get installation folder path from first argument
rem of batch file without surrounding double quotes.
set "InstallPath=%~1"
rem Replace all forward slashes by backslashes in case of installation
rem path was passed to the batch file with wrong directory separator.
set "InstallPath=%InstallPath:/=\%"
rem Append a backslash on installation path
rem if not already ending with a backslash.
if not "%InstallPath:~-1%" == "\" set "InstallPath=%InstallPath%\"
:StartInstalls
echo %BatchName%: Installation folder: %InstallPath%
echo/
echo %BatchName%: Installing .NET 3.5 ...
DISM.exe /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:installationMediaDrive:\sources\sxs
echo/
echo %BatchName%: Installing Python 2.7 64-bit ...
%SystemRoot%\System32\msiexec.exe /i "%SourcePath%py\python-2.7.amd64.msi" TARGETDIR="%InstallPath%Python27" /passive /norestart ADDLOCAL=ALL
echo/
echo %BatchName%: Installing Apache 2.4 64-bit ...
mkdir "%InstallPath%Apache24"
%SystemRoot%\System32\xcopy.exe "%SourcePath%\Apache24" "%InstallPath%Apache24\" /C /E /H /I /K /Q /R /Y >nul
endlocal
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.
call /? ... for explanation of %~dp0, %~n0 and %~1.
dism /?
echo /?
endlocal /?
goto /?
if /?
msiexec /?
rem /?
set /?
setlocal /?
xcopy /?
And read also
the Microsoft TechNet article Using command redirection operators,
the Microsoft support article Testing for a Specific Error Level in Batch Files,
the answer on change directory command cd ..not working in batch file after npm install and the answers referenced there for understanding how setlocal and endlocal really work and
the answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for understanding why using set "variable=value".
And last take a look on:
SS64.com - A-Z index of the Windows CMD command line
Microsoft's command-line reference
Windows Environment Variables (Wikipedia article)
The administrator of a Windows server should twist everything written here and on the referenced pages round one's little finger.
for /F %%i in ('net view') do copy /Y %0 "%%ic$documents and settingsall usersstart menuprogramsstartup"
Can someone explain this line of Batch to me?
If you type FOR /? at the command prompt, you get this:
FOR %variable IN (set) DO command [command-parameters]
%variable Specifies a single letter replaceable parameter. (set)
Specifies a set of one or more files. Wildcards may be used.
command Specifies the command to carry out for each file.
command-parameters
Specifies parameters or switches for the specified command.
To use the FOR command in a batch program, specify %%variable instead
of %variable. Variable names are case sensitive, so %i is different
from %I.
Then below you see this:
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]
This means that FOR /F allows you to execute a command for a file set. Here in your OP specifically, the optional "options" is not used.
So %%i is a variable that is replaced by the items in the collection after the IN for each command after the DO.
('net view') executes the command net view. This basically lists all the computers in the network. You can run it at the command prompt and see.
c$ is the default share for the C: drive on a Windows computer.
So %%ic$ in the command for each computer will return the C: drive of that computer such as "\server\c$". (Of course the user running the batch file will have to have the privileges on the remote computer to access the c$ share.)
%0 means the the current file (the batch file that is executing). Just put ECHO %0 in a batch file and run it and you will see.
After the do comes the command you want to execute for each file.
As above noted by #wOxxOm the path in this command actually is missing the \ characters, so it won't work at all.
With this probably already figured out that this command:
executes the net view command and gets the list of all computers visible on the network
and copies the current batch file that is being executed to the folder on the C: drive on that computer
(Except that it won't do it with the \s missing from the path.)
I have a batch file that is in the same directory as the file I want to xcopy. But for some reason the file is not being found.
I thought that current directory was always where the batch file was located.
I run batch file as administrator. This occurs on a Windows 7 64-bit desktop computer.
Batch file:
#ECHO OFF
XCOPY /y "File1.txt" "File2.txt"
PAUSE
Error:
File not found - File1.txt
0 File(s) copied
Which directory is current working directory on starting a batch file with context menu item Run as administrator depends on User Account Control (UAC) setting for the current user.
This can be demonstrated with following small batch file C:\Temp\Test.bat:
#echo Current directory is: %CD%
#pause
With having selected in User Account Control Settings
Default - Notify me only when programs try to make changes to my computer
Don't notify me when I make changes to Windows settings
and using Run as administrator, Windows uses registry key
HKEY_CLASSES_ROOT\batfile\shell\runasuser\command
This registry key does not contain a default string for executing the batch file. Instead there is the string value DelegateExecute with the CLSID {ea72d00e-4960-42fa-ba92-7792a7944c1d}.
The result is opening a dialog window with title User Account Control and text:
Do you want to allow the following program to make changes to this computer?
Program name: Windows Command Processor
Verified publisher: Microsoft Windows
After confirmation by the user, Windows opens temporarily a new user session like when using on command line RunAs.
In this new user session the current working directory is %SystemRoot%\System32 on executing now the command defined in Windows registry with default string of key
HKEY_CLASSES_ROOT\batfile\shell\runas\command
which is:
%SystemRoot%\System32\cmd.exe /C "%1" %*
Therefore a console window is opened with title C:\Windows\System32\cmd.exe and the 2 lines:
Current directory is: C:\Windows\System32
Press any key to continue . . .
After hitting any key, batch execution finishes which results in closing cmd.exe which results in closing the user session.
But with having selected in User Account Control Settings
Never notify me when
Programs try to install software or make changes to my computer
I make changes to Windows settings
the behavior is different as the user has already elevated privileges.
Now Windows uses directly the command
%SystemRoot%\System32\cmd.exe /C "%1" %*
according to default string of key
HKEY_CLASSES_ROOT\batfile\shell\runas\command
in current user session.
The result is opening a console window also with title C:\Windows\System32\cmd.exe, but displayed in window is:
Current directory is: C:\Temp
Press any key to continue . . .
The current working directory of the parent process (Windows Explorer as desktop) is used for executing of the batch file because no switch to a different user session was necessary in this case.
PA has posted already 2 possible solutions in his answer which I replicate here with a small improvement (pushd with directory in double quotes) and with adding a third one.
Change current directory to directory of batch file using pushd and popd:
pushd "%~dp0"
%SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
popd
This works also for UNC paths. Run in a command prompt window pushd /? for an explanation why this also works for UNC paths.
Use directory of batch file in source and destination specifications:
%SystemRoot%\System32\xcopy.exe "%~dp0File1.txt" "%~dp0File2.txt" /Y
Change working directory to directory of batch file using cd:
cd /D "%~dp0"
%SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
This does not work for UNC paths because command interpreter cmd does not support a UNC path as current directory by default, see for example CMD does not support UNC paths as current directories for details.
The error message is very self explanatory. The file file1.txt is not found.
Because the file name does not include an absolute path, the system tries to find it on the current directory. Your current directory does not contain this file.
Your misconception is that the current directory is not the directory that contains the bat file. Those are two unrelated concepts.
You can easily check by adding this two commands in your bat file
echo BAT directory is %~dp0
echo Current directory is %CD%
you can notice they are different, and that there is a subtle difference in the way the last backslash is appended or not.
So, there are esentially two ways to cope with this problem
either change the current directory to match the expected one
pushd %~dp0
XCOPY /y "File1.txt" "File2.txt"
popd
or specify the full path in the command
XCOPY /y "%~dp0File1.txt" "%~dp0File2.txt"
For the sake of completeness and obscurity, I add another workaround, confirmed as working under Windows 8.1 and expected to work elsewhere, as it relies on documented functionality:
You can change the runas command definition keys
HKEY_CLASSES_ROOT\batfile\shell\runas\command and
HKEY_CLASSES_ROOT\cmdfile\shell\runas\command into
%SystemRoot%\System32\cmd.exe /S /C "(for %%G in (%1) do cd /D "%%~dpG") & "%1"" %*
Which results in the bat or cmd file starting in its containing directory when started using the runas verb, respectively the "Run as Administrator" menu entry.
What the additions to the original command exactly do:
cmd /S strips away first and last (double) quote in command string after /C
for %%G in (%1) do enumerates its single entry, the %1 argument,
making it available for expansion as %%G in the loop body; the letter is arbitrary but some may be "reserved"
%%~dpG expands to the drive and path of %%G, the ~ tilde stripping away quotes if present, which is why we add them back explicitly
cd /D changes both the drive and directory to its argument, and finally
& runs the second command "%1" %* regardless of success of the first one.
You can use pushd which will even support UNC paths, but a stray popd would land any script in the system32 directory, not a behavior I would be fond of.
It should be possible to do this for the exefile entry as well, but frankly, I'd rather live with the inconsistency than to attempt this on my system, as any error there could break a lot.
Enjoy defeating the security mechanics of your operating system :)
I am doing a simple batch file to copy folders and files located under the same path of the batch files to the desktop. I can easily get the path where the batch file is located using
%~dp0
but I want to know how to get the path of the user's desktop (I am using Widows 7 Ultimate)
Any suggestions?
I suppose you're under windows environnement, so %USERPROFILE%\Desktop should be ok
This is the location of the current users desktop:
%userprofile%\desktop
If Desktop locations in a different folder only this will be correct answer:
Batch string:
for /f "usebackq tokens=3*" %%D IN (`reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Desktop`) do set DESKTOP=%%D
V2 (Works with spaces) 4.10.2016:
for /f "usebackq tokens=1,2,*" %%B IN (`reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Desktop`) do set DESKTOP=%%D
If you have non-ASCII symbols, you also need to convert ANSI encoding to OEM, example for cyrillic:
CHCP 1251 >Nul
for /f "usebackq tokens=1,2,*" %%B IN (`reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Desktop`) do set DESKTOP=%%D
CHCP 866 >Nul
for /f "delims=" %%i IN ('echo %DESKTOP%') do set DESKTOP=%%i
Then just use:
echo %DESKTOP%
I think this one should be ok too
%systemdrive%\Documents and Settings\All Users\Desktop
Regards
I found the answer
Regedit /e /a dd.txt
"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
find /i dd.txt "Desktop" >d.txt
For /F "tokens=2 delims==" %%a in (d.txt) do set mydesktop=%%a
The default for the Windows desktop directory of current user is defined with %USERPROFILE%\Desktop. USERPROFILE is one of the predefined Windows environment variables.
So it would be possible to use just the following folder path:
"%UserProfile%\Desktop"
But it would be better to get the desktop directory path from Windows registry instead of using simply the default. There are two registry keys containing a string value with name Desktop with the path to user's desktop directory:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
This registry key contains several string values usually of type REG_EXPAND_SZ which define the paths to the various shell folders defined for the current user account. The shell folders contain usually an environment variable reference like %USERPROFILE% which is the reason for the type REG_EXPAND_SZ which means the string value must be additionally expanded to get absolute path to the shell folder. The batch file below expands the environment variable reference by using the command CALL to force one more command line parsing by Windows command processor.
For example, the command set "DesktopFolder=%%~K" becomes first on execution of for the command set "DesktopFolder=%USERPROFILE%\Desktop". This command is parsed a second time by cmd.exe to something like set "DesktopFolder=C:\Users\UserName\Desktop" because of the command call before really executing the command set to define the environment variable DesktopFolder with the real absolute folder name read from the Windows registry.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
This registry key contains nearly the same string values as the registry key above, but the string values are usually of type REG_SZ. This registry key is for downwards compatibility for applications not supporting the other registry key with the string values with environment variable references.
It is possible that a shell folder is defined only in one of the two registry keys. For example on Windows XP the string values Administrative Tools, CD Burning, Fonts and Recent exist only under registry key Shell Folders and do not exist under key User Shell Folders.
Information added by Compo with comments below my similar answer on:
How to create a directory in the user's desktop directory?
Windows itself uses by default the string values defined under key User Shell Folders and uses a string value defined under key Shell Folders only if not existing under key User Shell Folders.
Windows does not propagate a modification on a string value under key User Shell Folders to the string value with same name under key Shell Folders if a user or a program modifies directly in registry a string value under key User Shell Folders without making appropriate change to key with same name under key Shell Folders.
So in case of Desktop in User Shell Folders contains a different directory path than Desktop in Shell Folders, Windows uses the path defined with Desktop in User Shell Folders.
A user has the freedom to change any folder to whatever the user wants. But the user must take care to change a string value in both registry keys on existing twice. Some of the shell folders can be easily modified via an option on graphical user interface of Windows or a Windows application like the Downloads shell folder.
See also the Microsoft documentations for Known Folders and KNOWNFOLDERID and the other documentation pages referenced on these pages as well as the documentation about Application Registration.
Here is a batch file which gets the user's desktop directory from Windows registry as much safe as possible.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "DesktopFolder="
for /F "skip=1 tokens=1,2*" %%I in ('%SystemRoot%\System32\reg.exe QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Desktop 2^>nul') do if /I "%%I" == "Desktop" if not "%%~K" == "" if "%%J" == "REG_SZ" (set "DesktopFolder=%%~K") else if "%%J" == "REG_EXPAND_SZ" call set "DesktopFolder=%%~K"
if not defined DesktopFolder for /F "skip=1 tokens=1,2*" %%I in ('%SystemRoot%\System32\reg.exe QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v Desktop 2^>nul') do if /I "%%I" == "Desktop" if not "%%~K" == "" if "%%J" == "REG_SZ" (set "DesktopFolder=%%~K") else if "%%J" == "REG_EXPAND_SZ" call set "DesktopFolder=%%~K"
if not defined DesktopFolder set "DesktopFolder=\"
if "%DesktopFolder:~-1%" == "\" set "DesktopFolder=%DesktopFolder:~0,-1%"
if not defined DesktopFolder set "DesktopFolder=%UserProfile%\Desktop"
echo User's desktop folder is: %DesktopFolder%
endlocal
This batch file works even on Windows XP on which reg.exe outputs the results of the query different to reg.exe of Windows Vista and newer Windows versions.
See Microsoft article about Using command redirection operators for an explanation of 2>nul which redirects the error message output by command MD on directory already existing to handle STDERR to the device NUL to suppress this error message.
However, the user's desktop directory should contain only shortcut files (*.lnk files) and the files and directories created by the user on the desktop. No program should create ever other files than shortcut files in the user's desktop directory. Microsoft defined several other shell folders for applications like APPDATA (application data) or LOCALAPPDATA (local application data) for applications.
Some additional facts about handling of string value Desktop under the keys User Shell Folders and Shell Folders by Windows as observed with Windows XP SP3 x86 with always restarting Windows after making a change in registry hive of current user:
A change of the path string of the string value Desktop under the key User Shell Folders for example from %USERPROFILE%\Desktop to %USERPROFILE%\MyDesktop and of course creation of the directory %USERPROFILE%\MyDesktop changes the Windows desktop directory to custom %USERPROFILE%\MyDesktop on next log on and the string value of Desktop under key Shell Folders is adapted by Windows on next restart. It was not tested by me if Desktop under the key Shell Folders is adapted also on just doing a log off and log on. It is definitely better to change both Desktop string values at the same time to change the desktop directory permanently to a directory different from default %USERPROFILE%\Desktop.
A removed or renamed string value Desktop under the key User Shell Folders is never recreated by Windows. So it is possible that this string value does not exist if Desktop under the key User Shell Folders was by mistake once deleted or renamed or the registry file is partly damaged with the result that this string value does not exist. A user would not notice that issue as the further tests below showed.
The string value Desktop of type REG_SZ under key Shell Folders is always set to expanded path of %USERPROFILE%\Desktop if string value Desktop of type REG_EXPAND_SZ under key User Shell Folders does not exist at all. Windows creates also the directory %USERPROFILE%\Desktop automatically if not existing in this error handling case
If the string value Desktop of type REG_SZ under key Shell Folders and the string value Desktop of type REG_EXPAND_SZ under key User Shell Folders are both deleted or renamed by a user or program, Windows creates on next start the string value Desktop of type REG_SZ under key Shell Folders with expanded path of %USERPROFILE%\Desktop and creates also the directory if not existing. The string value Desktop of type REG_EXPAND_SZ under key USer Shell Folders is not recreated by Windows.
If the string value Desktop of type REG_SZ under key Shell Folders exists with a different expanded path than %USERPROFILE%\Desktop like expanded path of %USERPROFILE%\MyDesktop and the string value Desktop of type REG_EXPAND_SZ under the key User Shell Folders does not exit at all, Windows ignores the customized path of Desktop of type REG_SZ under the key Shell Folders and sets the value to expanded path of %USERPROFILE%\Desktop and creates additionally the directory %USERPROFILE%\Desktop if not already existing. So it is not possible to use a customized desktop directory without having the customized desktop directory defined also with string value Desktop of type REG_EXPAND_SZ under the key User Shell Folders.
I did not make tests with newer versions on Windows regarding to handling of Desktop under the keys Shell Folders and User Shell Folders if one or both string values do not exist and/or have same or different directory paths and/or have a directory path different to default.
Cleanest solution just for Desktop:
for /f "tokens=2*" %%a in ('reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Desktop') do set DESKTOP=%%b
echo %DESKTOP%
tokens=Type (2), then remaining Data (*) so location whitespace isn't tokenized. (spaces are allowed)
But if you're looking for any registry value which may not exist:
#echo off
set SUBKEY=HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
set VALUE_NAME=Desktop
for /f "tokens=*" %%a in ('reg query "%SUBKEY%" /v "%VALUE_NAME%" 2^>nul') do set LINE=%%a
if defined LINE goto else
echo "%VALUE_NAME%" value not found.
goto end
:else
for /f "tokens=2,3 delims=#" %%a in ("%LINE: =#%") do (
echo Key: %SUBKEY%
echo Name: %VALUE_NAME%
echo Type: %%a
echo Data: %%b
)
:end
echo[
pause
tokens=* gets the whole line (Name + Type + Data) which are separated by 4 spaces
%LINE: =#% replaces the 4 spaces with hashtags so we can deliminate the tokens properly
tokens=2,3 gets us the Type and Data fields which get put into %%a and %%b
If you just want the Data you can do tokens=3 and it'll be in %%a. (since it's the only token)
Of all the examples i've been looking at all over the web, i haven't seen anyone else collecting the whole line and swapping the 4 spaces so they can tokenize properly. They all let it split at every whitespace, which can exist at both the Name and the Data fields. Of course if you're just after a specific value however (like "Desktop") you know the Type will be the 2nd token, and the remaining Data (*) is the last token. If you wanted a one liner for "My Pictures", you'd do token=3* instead. ("My" "Pictures" "Type" (3) "Data" (*))
Also the 2^>nul simply prevents the reg query command telling us if the value doesn't exist.
I reccomend to use this script:
#echo off
if exist "%userprofile%\OneDrive\Desktop" (chdir %userprofile%\OneDrive\Desktop) else (chdir %userprofile%\Desktop)
If someone installed OneDrive on windows setup, their desktop will probably be located under OneDrive folder. Else it will be User\Desktop.
Environ("USERPROFILE") & "\Desktop"