these are my variable:users' and system's, and when i try to add a variable to system variable I find that I cant open the Path like that:
It only displays like this:
Forgive my Chinese verion and I think you can read it by your experience
I want my system variable(Path) be like the normal type whitch can add a lot of variables
Ok,I solved it by myself.
Just delete this "Path" and create a new one
and load the codes below then run it as bat:
chcp 65001&cls
REM 备份当前环境变量
echo 当前环境变量:
echo %Path%
echo 永久设置Hadoop、scala、Java、 go、spark、zookeeper环境变量
SETX /M GO_ROOT "C:\Go"
SETX /M JAVA_HOME "C:\Program Files\Java\jdk1.8.0_191"
SETX /M HADOOP_HOME "C:\hadoop"
SETX /M SCALA_HOME "C:\scala-2.11.8"
SETX /M SPARK_HOME "C:\spark-2.2.1-bin-hadoop2.7"
SETX /M ZOOKEEPER_HOME "C:\zookeeper-3.4.14"
SETX /M Path "%Path%;%ZOOKEEPER_HOME%\bin;%SPARK_HOME%\bin;%SPARK_HOME%\jars;%SCALA_HOME%\bin;%HADOOP_HOME%\bin;%JAVA_HOME%\bin;%GO_ROOT%\bin"
echo 修改完成, 即将重启文档管理器explorer
pause
REM 重启explorer.exe使环境变量立即生效
taskkill /im explorer.exe /f
echo ================================================
echo 开始重启“explorer.exe”进程
start explorer.exe
paus
Related
I'm trying to set up a script that will install python automatically, and I'm stuck on setting up the user path. I have only a vague clue about what I'm doing here so please excuse me if I'm using any terms incorrectly.
I'm trying to set the environment variables using setx path "%PATH%;%LOCALAPPDATA%\Programs\Python\Python310"\ but I've run into several issues.
I finally have this command not failing because of 'multiple default arguments' or something but now when trying to set PATH, I get duplicate entries.
If originally %PATH% gave me '\path1;\path2', and I run setx path "%PATH%;\path3", %PATH% outputs '\path1;\path2;\path1;\path2;\path3',
when I expected to have '\path1;\path2;\path3'
As per what I've been reading from other answers, I think %PATH% gives you the combined SYSTEM and USER paths, but setx path modifies the USER path only. So everytime I run setx path I'm adding the system variables again.
I just want to add my python.exe location to the user path variable in a .bat script without this duplicating issue. Does anyone have a working solution?
#ECHO OFF
SETLOCAL
SET "python=%%USERPROFILE%%\AppData\Local\Microsoft\WindowsApps"
ECHO %path%>u:\pp.txt
FOR /f "tokens=1,2,*" %%u IN ('reg query HKCU\Environment') DO IF "%%u"=="Path" (
FIND /v "%python%" "u:\pp.txt" >NUL
IF NOT ERRORLEVEL 1 (
ECHO CHANGE path
ECHO SETX PATH "%%w;%python%"
)
)
DEL u:\pp.txt
GOTO :EOF
I used a path that I have installed as python. Note that the % need to be doubled.
Write the current path to a tempfile (u:\pp.txt is simply on a RAMDRIVE for me)
Read the environment data from the registry, tokenise and select for the first item in %%u being Path. Its value will be in %%w.
See whether the "python path" is already in the path; if not, errorlevel will be 1 so execute the setx.
I merely echoed the setx as I'm not going to change the registry. If the command echoed appears correct, remove the echo to actually execute the setx.
It may be an idea to also set the path in the current environment, as setx changes the variable's value for future instances, not for the current one.
===== Revision ==== in the light of comments:
#ECHO Off
SETLOCAL
SET "python=%%USERPROFILE%%\AppData\Local\Microsoft\WindowsApps"
ECHO %path%>u:\pp.txt
FOR /f "tokens=1,2,*" %%u IN ('reg query HKCU\Environment') DO IF /i "%%u"=="Path" (
FIND /v "%python%" "u:\pp.txt" >NUL
IF NOT ERRORLEVEL 1 (
SETLOCAL ENABLEDELAYEDEXPANSION
SET "xpath=%%w;%python%"
SET "xpath=!xpath:~159!"
IF DEFINED xpath (ECHO PATH too long) ELSE (
ECHO CHANGE path
ECHO SETX %%u "%%w;%python%"
)
ENDLOCAL
)
)
DEL u:\pp*.txt
GOTO :EOF
Fixes:
Comparison in for ...%%u made case-insensitive.
Length of resultant user-path variable checked. I used a value of 159 for testing, 1022 for real-world.
variable name being setx'd will be identical to that retrieved from the registry. (For me, it's Path (W11 22H2) - My editor helpfully changes any batch keyword followed by a space to upper-case)
I used this command in a windows command line:
C:\Users\myuser\Desktop>C:\Windows\System32\ForFiles.exe /P C:\myfolder\mysubfolder /S /M *.* /D +09/15/2022 /C "cmd /C echo #FSIZE >> sizes.txt"
I wanted to echo all the sizes for files in folder modified in the last 5 days.
I didn't found the output file.
I then solved the problem by changing the command to:
C:\Users\myuser\Desktop>C:\Windows\System32\ForFiles.exe /P C:\myfolder\mysubfolder /S /M *.* /D +09/15/2022 /C "cmd /C echo #FSIZE" > sizes.txt
Anyway, I'd like to know if I created a sizes.txt file somewhere on my hard drive.
Searched in the folder, subfolder, desktop, home folder, C:, C:\Windows, C:\Windows\System32... nothing...
I finally found them, yes "them".
One in each directory containing recently edited file(s).
Seems that the command forfiles executes is actually run where the file is located.
It's strange because if you specify cmd /C echo %CD% as command it actually prints the directory you run from, in my case Desktop!
I have a Windows batch file whose purpose is to set some environment variables, e.g.
=== MyFile.cmd ===
SET MyEnvVariable=MyValue
Users can run this prior to doing work that needs the environment variable, e.g.:
C:\> MyFile.cmd
C:\> echo "%MyEnvVariable%" <-- outputs "MyValue"
C:\> ... do work that needs the environment variable
This is roughly equivalent to the "Developer command prompt" shortcuts installed by Visual Studio, which set environment variables needed to run VS utilities.
However if a user happens to have a Powershell prompt open, the environment variable is of course not propagated back to Powershell:
PS C:\> MyFile.cmd
PS C:\> Write-Output "${env:MyEnvVariable}" # Outputs an empty string
This can be confusing for users who switch between CMD and PowerShell.
Is there a way I can detect in my batch file MyFile.cmd that it was called from PowerShell, so that I can, for example, display a warning to the user? This needs to be done without any 3rd party utility.
Your own answer is robust and while it is generally slow due to needing to run a PowerShell process, it can be made significantly faster by optimizing the PowerShell command used to determine the calling shell:
#echo off
setlocal
CALL :GETPARENT PARENT
IF /I "%PARENT%" == "powershell" GOTO :ISPOWERSHELL
IF /I "%PARENT%" == "pwsh" GOTO :ISPOWERSHELL
endlocal
echo Not running from Powershell
SET MyEnvVariable=MyValue
GOTO :EOF
:GETPARENT
SET "PSCMD=$ppid=$pid;while($i++ -lt 3 -and ($ppid=(Get-CimInstance Win32_Process -Filter ('ProcessID='+$ppid)).ParentProcessId)) {}; (Get-Process -EA Ignore -ID $ppid).Name"
for /f "tokens=*" %%i in ('powershell -noprofile -command "%PSCMD%"') do SET %1=%%i
GOTO :EOF
:ISPOWERSHELL
echo. >&2
echo ERROR: This batch file may not be run from a PowerShell prompt >&2
echo. >&2
exit /b 1
On my machine, this runs about 3 - 4 times faster (YMMV) - but still takes almost 1 second.
Note that I've added a check for process name pwsh as well, so as to make the solution work with PowerShell Core too.
Much faster alternative - though less robust:
The solution below relies on the following assumption, which is true in a default installation:
Only a system environment variable named PSModulePath is persistently defined in the registry (not also a user-specific one).
The solution relies on detecting the presence of a user-specific path in PSModulePath, which PowerShell automatically adds when it starts.
#echo off
echo %PSModulePath% | findstr %USERPROFILE% >NUL
IF %ERRORLEVEL% EQU 0 goto :ISPOWERSHELL
echo Not running from Powershell
SET MyEnvVariable=MyValue
GOTO :EOF
:ISPOWERSHELL
echo. >&2
echo ERROR: This batch file may not be run from a PowerShell prompt >&2
echo. >&2
exit /b 1
Alternative approach for launching a new cmd.exe console window on demand:
Building on the previous approach, the following variant simply re-invokes the batch file in a new cmd.exe window on detecting that it is being run from PowerShell.
This is not only more convenient for the user, it also mitigates the problem of the solutions above yielding false positives: When run from an interactive cmd.exe session that was launched from PowerShell, the above solutions will refuse to run, even though they should, as PetSerAl points out.
While the solution below also doesn't detect this case per se, it still opens a useable - albeit new - window with the environment variables set.
#echo off
REM # Unless already being reinvoked via cmd.exe, see if the batch
REM # file is being run from PowerShell.
IF NOT %1.==_isNew. echo %PSModulePath% | findstr %USERPROFILE% >NUL
REM # If so, RE-INVOKE this batch file in a NEW cmd.exe console WINDOW.
IF NOT %1.==_isNew. IF %ERRORLEVEL% EQU 0 start "With Environment" "%~f0" _isNew & goto :EOF
echo Running from cmd.exe, setting environment variables...
REM # Set environment variables.
SET MyEnvVariable=MyValue
REM # If the batch file had to be reinvoked because it was run from PowerShell,
REM # but you want the user to retain the PowerShell experience,
REM # restart PowerShell now, after definining the env. variables.
IF %1.==_isNew. powershell.exe
GOTO :EOF
After setting all environment variables, note how the last IF statement, also re-invokes PowerShell, but in the same new window, based on the assumption that the calling user prefers working in PowerShell.
The new PowerShell session will then see newly defined environment variables, though note that you'll need two successive exit calls to close the window.
As Joe Cocker used to say "I get by with a little help from my friends".
In this case from Lieven Keersmaekers, whose comments led me to the following solution:
#echo off
setlocal
CALL :GETPARENT PARENT
IF /I "%PARENT%" == "powershell.exe" GOTO :ISPOWERSHELL
endlocal
echo Not running from Powershell
SET MyEnvVariable=MyValue
GOTO :EOF
:GETPARENT
SET CMD=$processes = gwmi win32_process; $me = $processes ^| where {$_.ProcessId -eq $pid}; $parent = $processes ^| where {$_.ProcessId -eq $me.ParentProcessId} ; $grandParent = $processes ^| where {$_.ProcessId -eq $parent.ParentProcessId}; $greatGrandParent = $processes ^| where {$_.ProcessId -eq $grandParent.ParentProcessId}; Write-Output $greatGrandParent.Name
for /f "tokens=*" %%i in ('powershell -command "%CMD%"') do SET %1=%%i
GOTO :EOF
:ISPOWERSHELL
echo.
echo ERROR: This batch file may not be run from a PowerShell prompt
echo.
cmd /c "exit 1"
GOTO :EOF
I did something like this for Chocolatey's RefreshEnv.cmd script: Make refreshenv.bat error if powershell.exe is being used.
My solution didn't end being used, for unrelated reasons, but it's available in this repo: beatcracker/detect-batch-subshell. Here is copy of it, just in case.
Script that will only run if called directly from interactive command processor session
Script will detect if it's run from non-interactive session (cmd.exe /c detect-batch-subshell.cmd) and show approriate error message.
Non-interactive shell includes PowerShell/PowerShell ISE, Explorer, etc... Basically anything that will try to execute script by running it in the separate cmd.exe instance.
Hovewer, dropping into the cmd.exe session from PowerShell/PowerShell ISE and executing script there will work.
Dependencies
wmic.exe - comes with Windows XP Professional and up.
Example:
Open cmd.exe
Type detect-batch-subshell.cmd
Output:
> detect-batch-subshell.cmd
Running interactively in cmd.exe session.
Example:
Open powershell.exe
Type detect-batch-subshell.cmd
Output:
PS > detect-batch-subshell.cmd
detect-batch-subshell.cmd only works if run directly from cmd.exe!
Code
detect-batch-subshell.cmd
#echo off
setlocal EnableDelayedExpansion
:: Dequote path to command processor and this script path
set ScriptPath=%~0
set CmdPath=%COMSPEC:"=%
:: Get command processor filename and filename with extension
for %%c in (!CmdPath!) do (
set CmdExeName=%%~nxc
set CmdName=%%~nc
)
:: Get this process' PID
:: Adapted from: http://www.dostips.com/forum/viewtopic.php?p=22675#p22675
set "uid="
for /l %%i in (1 1 128) do (
set /a "bit=!random!&1"
set "uid=!uid!!bit!"
)
for /f "tokens=2 delims==" %%i in (
'wmic Process WHERE "Name='!CmdExeName!' AND CommandLine LIKE '%%!uid!%%'" GET ParentProcessID /value'
) do (
rem Get commandline of parent
for /f "tokens=1,2,*" %%j in (
'wmic Process WHERE "Handle='%%i'" GET CommandLine /value'
) do (
rem Strip extra CR's from wmic output
rem http://www.dostips.com/forum/viewtopic.php?t=4266
for /f "delims=" %%x in ("%%l") do (
rem Dequote path to batch file, if any (3rd argument)
set ParentScriptPath=%%x
set ParentScriptPath=!ParentScriptPath:"=!
)
rem Get parent process path
for /f "tokens=2 delims==" %%y in ("%%j") do (
rem Dequote parent path
set ParentPath=%%y
set ParentPath=!ParentPath:"=!
rem Handle different invocations: C:\Windows\system32\cmd.exe , cmd.exe , cmd
for %%p in (!CmdPath! !CmdExeName! !CmdName!) do (
if !ParentPath!==%%p set IsCmdParent=1
)
rem Check if we're running in cmd.exe with /c switch and this script path as argument
if !IsCmdParent!==1 if %%k==/c if "!ParentScriptPath!"=="%ScriptPath%" set IsExternal=1
)
)
)
if !IsExternal!==1 (
echo %~nx0 only works if run directly from !CmdExeName!^^!
exit 1
) else (
echo Running interactively in !CmdExeName! session.
)
endlocal
Like the answer from beatcracker I think it would be better to not take assumptions about the external shell that can be used to launch the batch script, for instance, the issue can also arise when running the batch file through the bash shell.
Because it exclusively uses the native facilities of CMD and has no dependency on any external tool or the WMI, the execution time is very fast.
#echo off
call :IsInvokedInternally && (
echo Script is launched from an interactive CMD shell or from another batch script.
) || (
echo Script is invoked by an external App. [PowerShell, BASH, Explorer, CMD /C, ...]
)
exit /b
:IsInvokedInternally
setlocal EnableDelayedExpansion
:: Getting substrings from the special variable CMDCMDLINE,
:: will modify the actual Command Line value of the CMD Process!
:: So it should be saved in to another variable before applying substring operations.
:: Removing consecutive double quotes eg. %systemRoot%\system32\cmd.exe /c ""script.bat""
set "SavedCmdLine=!cmdcmdline!"
set "SavedCmdLine=!SavedCmdLine:""="!"
set /a "DoLoop=1, IsExternal=0"
set "IsCommand="
for %%A in (!SavedCmdLine!) do if defined DoLoop (
if not defined IsCommand (
REM Searching for /C switch, everything after that, is CMD commands
if /i "%%A"=="/C" (
set "IsCommand=1"
) else if /i "%%A"=="/K" (
REM Invoking the script with /K switch creates an interactive CMD session
REM So it will be considered an internal invocatoin
set "DoLoop="
)
) else (
REM Only check the first command token to see if it references this script
set "DoLoop="
REM Turning delayed expansion off to prevent corruption of file paths
REM which may contain the Exclamation Point (!)
REM It is safe to do a SETLOCAL here because the we have disabled the Loop,
REM and the routine will be terminated afterwards.
setlocal DisableDelayedExpansion
if /i "%%~fA"=="%~f0" (
set "IsExternal=1"
) else if /i "%%~fA"=="%~dpn0" (
set "IsExternal=1"
)
)
)
:: A non-zero ErrorLevel means the script is not launched from within CMD.
exit /b %IsExternal%
It checks the command line that used to launch the CMD shell to tell if script have been launched from within CMD or by an external app using the command line signature /C script.bat which is typically used by non CMD shells to launch batch scripts.
If for any reason the external launch detection needs to bypasses, for instance when manually launching the script with additional commands to take advantage the defined variables, it can done by prepending # to the path of the script in CMD command line:
cmd /c #MyScript.bat & AdditionalCommands
I ran into a problem in a bigger batch file I was making, and narrowed it down to a very particular problem. If I manually set the errorlevel like this: set errorlevel=5 , then the "choice" command can't set or override my errorlevel. How can I get past this from happening?
I made a batch file to test this out. Here it is:
#echo off
set errorlevel=5
choice /c 123
echo %errorlevel%
pause
And the output, if you were to press 2:
[1,2,3]?2
5
Press any key to continue . . .
I used to use a simple subroutine to set the errorlevel to any value:
#echo off
call :errorlevel=5
echo %errorlevel%
goto :EOF
:errorlevel
exit /B %1
use cmd /c exit /b 5 instead of set errorlevel=5
like this:
#echo off
cmd /c exit /b 5
choice /c 123
echo %errorlevel%
pause
System environment variables can be used by the batch file writer, but that is a really bad idea.
PATH TEMP WINDIR USERNAME USERPROFILE ERRORLEVEL TIME DATE are some of variable names you should avoid using. Type SET at a cmd prompt to see the usual ones that are in use, but it doesn't show them all.
Choice is operating normally, and other tools will fail to set an errorlevel too.
My Windows 7 system path looks something like this:
Path=C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;....
I am trying to set a new user variable based on whether its defined or not already:
IF NOT DEFINED DEFAULTP (
echo Setting DEFAULTP to backup the system PATH
SETX DEFAULTP=%PATH%
)
When I try to do this, I get an error that looks like this:
\NVIDIA was unexpected at this time.
Can anyone guess why?
Try this:
IF NOT DEFINED DEFAULTP (
echo Setting DEFAULTP to backup the system PATH
SETX DEFAULTP "%PATH%"
)
Edit: Running setx /? gives these examples:
Examples:
SETX MACHINE COMPAQ
SETX MACHINE "COMPAQ COMPUTER" /M
SETX MYPATH "%PATH%"
SETX MYPATH ~PATH~
I got the issue because there are braces in PATH and it got conflicted with the braces of the IF statement.
the solution is just try to remove braces from IF
#rem below will pass
SET PATH_1=C:\Program Files (x86)\NVIDIA Corporation;%PATH_1%
if "%1" == "1" set PATH_1=%NEW_PATH%;%PATH_1%
#rem below will fail
SET PATH_1=C:\Program Files (x86)\NVIDIA Corporation;%PATH_1%
if "%1" == "1" ( set PATH_1=%NEW_PATH%;%PATH_1% )
echo final PATH_1=%PATH_1%
echo over
My original demo code that has the error:
(for convenience just rename PATH to PATH_1 )
#setlocal
#echo off
set PATH_1=C:\windows
SET NEW_PATH=PATH\TO\SOME_WHRER
SET PATH_1=C:\Program Files (x86)\NVIDIA Corporation;%PATH_1%
if "%1" == "1" (
set PATH_1=%NEW_PATH%;%PATH_1%
)
echo final PATH_1=%PATH_1%
exit /b 1
That is happening because you work with a variable containing parentheses inside a code block (the if function in your case). You can indeed circumvent this by adding quotes around your variable (as RonK advises) or by removing the parentheses of the if function (as Michael Hou advises), but the easiest and most secure way is to change %PATH% into !PATH! inside the code block.
IF NOT DEFINED DEFAULTP (
echo Setting DEFAULTP to backup the system PATH
SETX DEFAULTP=!PATH!
)