I was wondering if there was a simpler way to change the case of just the highlighted colored programming text (For In While Do Set etc) in one go by color using notepad++ or sublime text. So for example change the case of all the blue text in a batch file test.bat:
SETLOCAL DisableDelayedExpansion
FOR /F "delims=" %%A IN ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c
ECHO(0x09"') DO SET "TAB=%%A"
ECHO This is a %TAB%
The syntax would be changed to title case like:
Setlocal DisableDelayedExpansion
For /F "delims=" %%A In ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c
Echo(0x09"') Do Set "TAB=%%A"
Echo This is a %TAB%
I currently do this by Right Clicking and then selecting Plugin Commands> Copy Text with Syntax Highlighting from the context menu, copying this text to Microsoft word and saving it as an html file, opening the html file in notepad++ for editing and then pasting text-transform: uppercase; under the line color:blue; and then opening it again in word (not in edit mode) and finally copying it to notepad++ but would like to know if there is a simpler way without looking up each individual word but instead just batch formatting words of a similar color.
Sublime has an internal command called title_case to perform this manipulation, which is available from the menu as Edit > Convert Case > Title Case or from the command palette as Convert Case: Title Case.
So if all of the keywords in the document were selected, you could use that command to perform the operation. You would probably have to do it in batches (pun mildly intended), such as putting the cursor in SETLOCAL and repeatedly pressing Ctrl+D to select all of the instances, then cycle back and do the next keyword.
To automate the process a little better, a simple plugin can be created that selects all of the keywords and runs the command, so that you can perform the bulk of the operation in one simple step.
An example of that is the following, which you can use by selecting Tools > Developer > New Plugin... from the menu, replacing the stub code with the code below, and then saving in the location Sublime will default to as something like dos_batch_case_fix.py or something similar (only the location and extension matter):
import sublime
import sublime_plugin
# A list of extra words to change the title case for that aren't considered
# keywords by the dos batchfile syntax.
_extra_words = ["do", "in"]
class BatchTitleCaseCommand(sublime_plugin.TextCommand):
"""
For a dos batch file, convert the case of all keywords and all found
instances of the words in _extra_words to title case.
"""
def run(self, edit):
# Save the current selection, then clear it
saved_sel = list(self.view.sel())
self.view.sel().clear()
# Find everything that the syntax thinks is a keyword and add it to
# the selection
for region in self.view.find_by_selector("keyword"):
self.view.sel().add(region)
# Convert the list of extra words to a regular expression and add all
# whole word matches to the selection.
regex = r"\b({0})\b".format("|".join(_extra_words))
for region in self.view.find_all(regex, sublime.IGNORECASE):
self.view.sel().add(region)
# Convert the selection to title case.
self.view.run_command("title_case")
# Restore the selection to what it was on entry.
self.view.sel().clear()
for region in saved_sel:
self.view.sel().add(region)
def is_enabled(self):
return self.view.match_selector(0, "source.dosbatch")
This implements a new command named batch_title_case that is only active in batch files. It saves the current selection, then selects all of the keywords (as determined by the syntax currently in use), runs the command to change the case, then puts the original selection back. You can bind this command to a key the same as you would for any other internal command.
Since this uses the syntax of the current file to detect what a keyword is, it doesn't catch things like IN and DO because (at least currently) the Sublime syntax for Batch files doesn't think those are keywords.
For that reason, this also shows how you could handle those sorts of words. The code here does a case insensitive whole word search for a list of words (represented in _extra_words) and selects those as well as the selected text.
This is semi-dangerous in that unlike the keyword search by syntax scope, the regex search will find those words anywhere, including inside of strings where they might not represent keywords but just regular words instead.
As such it's probably a good idea to use this on a copy of the file (or be able to undo) and verify that it hasn't done something that you didn't otherwise expect.
I would imagine that a visual inspection would be much less effort than the solution you're currently using.
Potential changes
If desired, the plugin above could be modified to remove the portions that save and restore the selection along with executing the title_case command; in that case the command would only alter the selection in the file to the words that it thinks that it needs to title case and allow yo to take the action manually.
Note that if you work with a really large file that contains a lot of keywords, having that many simultaneous selections may slow things down a bit.
Invoking the command
The plugin above creates a command named batch_title_case. There are a variety of ways to execute the command, depending on how you want to proceed. Where the User package is mentioned below, you can use the Preferences > Browse Packages command from the menu to locate it. The User package is the location where the plugin above is stored, since Sublime defaults to that location when you use Developer > New Plugin.
Via a key binding
Using Preferences > Key Bindings, you can add a custom binding to the right hand side of the window that references the command:
{
"keys": ["ctrl+alt+s"],
"command": "batch_title_case",
},
Via the Command Palette
The command can be added to the command palette by adding a file of type sublime-commands to your User package with the following contents (e.g. MyCustomCommands.sublime-commands). The caption will specify how the command appears:
[
{ "command": "batch_title_case", "caption": "Command Caption Here" },
]
Note: As written above, the command is only enabled for a batch file, and the Command Palette only shows you available commands, so in non-batch files the command will not appear in the command palette.
Via the context menu
The command can be added to the right click context menu by creating a file named Context.sublime-menu in your User package; if such a file already exists, add only the { ... } line to the appropriate place in the existing file. The caption will specify how the command appears:
[
{ "command": "batch_title_case", "caption": "Command Caption Here" },
]
Note: As written above, the command is only enabled for a batch file, so in non-batch files the command in the menu will appear grayed out. To hide the context menu item in files that it doesn't apply to, add the following lines to the plugin code above under is_enabled():
def is_visible(self):
return self.is_enabled()
Related
I have 2 projects for which I am trying to create a generic Post-Build event batch file.
Here is the command in Visual Studio:
Post-Build event
if $(ConfigurationName) == Release ("$(ProjectDir)PostBuildRelease.bat" "$(TargetDir)" #(VersionNumber) "$(TargetFileName)" "$(TargetName)")
So I am calling the file PostBuildRelease.bat with 4 parameters:
Bin\Release Directory
Project Version
File Name With Extension
File Name Without Extension
Project 1
This works perfectly with this batch script:
CMD
SET parameter=%1 REM Full path to new bin\release\
SET parameter=%2 REM Full Version Number
SET parameter=%3 REM File name + extension
SET parameter=%4 REM File name - extension
SET "productionpath=Z:\Unused\Apps\LyncVdiChecker\"
MOVE %productionpath%%3 %productionpath%"_archive\"%4"."%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%"-"%2
XCOPY %3 %productionpath%
Where the assembly is copied to Z:\Unused\Apps\LyncVdiChecker\ and the existing version copied to _archive in the same folder. The archived version also has the date and version number replace the file extension.
Project 2
This batch script also works perfectly (it does the same thing but in a different folder and for a different project):
CMD
SET parameter=%1 REM Full path to new bin\release\
SET parameter=%2 REM Full Version Number
SET parameter=%3 REM File name + extension
SET parameter=%4 REM File name - extension
SET "productionpath=Z:\Unused\Apps\IT Support App\"
MOVE "Z:\Unused\Apps\IT Support App\"%3 "Z:\Unused\Apps\IT Support App\_archive\"%4"."%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%"-"%2
XCOPY %3 "Z:\Unused\Apps\IT Support App"
However, if I try using the same script from Project1 (the more generic version) in Project2, I get errors, even though the 2 scripts are equivalent:
Errors
The command "if Release == Release ("C:\Users\Seb.Kotze\Source\Repos\Applications\ITSelfHelp\ITHelp\PostBuildRelease.bat" "C:\Users\Seb.Kotze\Source\Repos\Applications\ITSelfHelp\ITHelp\bin\Release\" 2.0.6100.20905 "IT Self Help.exe" "IT Self Help")" exited with code 4.
Output Window:
The syntax of the command is incorrect.
Invalid number of parameters
This error is rather unhelpful, so I tried commenting out the 2 lines MOVE and XCOPY and build again:
Removed MOVE
Same error as above.
Output window:
Invalid number of parameters
Remove XCOPY
No Visual Studio Error, but this appears in the output window:
The syntax of the command is incorrect.
Parameter Output
When I echo out the parameters being used in Project2, everything seems to be in order:
"Path\to\Bin\Release"
2.0.6100.21082
"IT Self Help.exe"
"IT Self Help"
Z:\Unused\Apps\IT Support App\
How can I debug this issue? How is it possible that my script runs fine without any issues, but when run against a different project none of the commands are recognised? Any help with this is much appreciated!
You should normalize all your arguments, so they don't contain outer quotes.
Then you can use them in a reliable way.
The syntax set "variable=%~1" avoids outer quotes in the variable itself.
set "TargetDir=%~1"
set "VersionNumber=%~2"
set "TargetFileName=%~3"
set "TargetName=%~4"
SET "productionpath=Z:\IT Support App\"
set "dateStamp=%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%"
MOVE "Z:\IT App\%TargetFileName%" "Z:\IT App\_archive\%TargetName%.%dateStamp%-%VersionNumber%"
XCOPY "%TargetFileName%" "Z:\IT App"
The problem is that the script is messing with the double quotes resulting in invalid paths and invalid number of arguments passed. When dealing with paths built dynamically, it's best to strip any existing " from the parts, and after the path is complete, surround it in ".
Dealing with batch arguments is explained on MSDN. Same thing for variables can be found on SS64.
I've played a bit with the file, and I was able to run it (from command line). The changes you should make in your (Project1) file:
SET productionpath="Z:\Unused\Apps\LyncVdiChecker\"
MOVE "%productionpath:"=%%~3" "%productionpath:"=%_archive\%~4.%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%-%~2"
XCOPY "%~3" "%productionpath:"=%"
I moved the " from the productionpath line to the beginning of its contents. That way will work with paths that contain SPACE s.
In the MOVE and XCOPY lines, I did what I explained above: even if the syntax is not that clear, it's more robust (the last "%productionpath:"=%" could be simple written as %productionpath%, but I left it in the the 1st form for consistency).
Note: You could remove the CMD command at the beginning of your batch, since it starts a new cmd instance(process) that doesn't end.
I found a solution to this, but I am still not sure what the cause was.
I suspect it has something to do with either one of:
Spaces in productionpath causing the command parameter declaration to escape
Quotes around one or more of the parameters creating a non-existent file path
After trying out a few changes to the script, I found that changing the productionpath declaration to SET productionpath="Z:\Unused\Apps\IT Support App\" solved the issue:
CMD
SET parameter=%1 REM Full path to new bin\release\
SET parameter=%2 REM Full Version Number
SET parameter=%3 REM File name + extension
SET parameter=%4 REM File name - extension
SET productionpath="Z:\Unused\Apps\IT Support App\"
MOVE "Z:\Unused\Apps\IT Support App\"%3 "Z:\Unused\Apps\IT Support App\_archive\"%4"."%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%"-"%2
XCOPY %3 "Z:\Unused\Apps\IT Support App"
Making the same change to the Project1 script did not cause that to break either, so this seems safe.
Update
After reading some of the other answers, I amended the script once again to the following:
CMD
SET "TargetDir=%~1"
SET "VersionNumber=%~2"
SET "TargetFileName=%~3"
SET "TargetName=%~4"
SET "ProductionPath=Z:\Unused\Apps\IT Support App\"
SET "ArchivePath=%ProductionPath%_archive\"
SET "DateStamp=%DATE:~0,2%%DATE:~3,2%%DATE:~6,4%"
MOVE "%ProductionPath%%TargetFileName%" "%ArchivePath%%TargetName%.%DateStamp%-%VersionNumber%"
XCOPY "%TargetFileName%" "%ProductionPath%"
Notice the "normalisation" of the paramaters - this removes all quotation marks from their values.
Also now using named parameters.
I know that using ctrl+shift+f we can find the text in folder we want and simple ctrl+f will find the text in a opened file or we can right folder and click on a option Find in Folder... to search the text
I am looking for, how can I find the file in a Folder/Project.
You can use the Goto Anything feature (Ctrl+P on Windows and Linux, Cmd+P on macOS) and type the name of the file you're looking for. If there are multiple hits, you can select the appropriate file using cursor keys. It also supports powerful operators, that let you jump to specific parts inside a file.
Examples:
file.js opens that file
:100 jumps to line 100 in current file
file.js:100 jumps to line 100 in file.js
#loadFile lists all files with classes/functions named loadFile (shortcut: Ctrl+R, Cmd+R on macOS)
file.js#loadFile jumps to a loadFile() in file.js
This can be done using:
Windows: Ctrl + P
Mac: Cmd + P
It works much like ItelliJ's Shift - 2 times, with a faster and accurate prediction.
I am looking for a solution i have been trying to research for a little while.
I am making a .bat program to use at my work for templates to be placed in a certification document. Trying to speed up the process of copying and pasting .
Currently i am using notepad, highlighting and CRTL+C / V to copy the template.
I have built a .bat program that will do it in a selection window (enter 1-8 ect ect)
The only problem i am having is i was wondering if i can place the template inside the scripting of the .bat, and it will copy it to the clipboard.
example of template (must have spaces and breaks):
~~~~~~~~~~~~~~~~~~~~~~~
Processed OK:
Surface Hardness Tested HR:
Name
03/21/2015
Company
~~~~~~~~~~~~~~~~~~~~~~~~~~
i know of the "clip" command, but i can only find ways to copy stuff from a .txt. document, and i would like to avoid having 8+ .txt documents for every template. (e.g: clip > temp1.txt)
So is there a way to copy a prefab'ed template from within the .bat
example:
if %TYPE%==1 goto temp1
:temp1
clip ???????
Any help would be great!
Thanks!
Sure you can. You can create arbitrarily complex streams to be fed into the clip program, such as:
#echo off
(
echo hello
echo goodbye
) | clip
If you run a cmd file containing those commands, then open up a notepad document and use CTRL-V, it will paste the text:
hello
goodbye
Just replace the two echo commands with whatever code you need to generate the template.
I tried the instructions - I am using Firefox on Lubuntu (Openbox). But I get the error
"Firefox doesn't know how to open this address, because the protocol (org-protocol) isn't associated with any program".
How should I fix this?
The following steps for setting up org-protocol work with Ubuntu 16.04 (Xenial Xerus) and presumably later versions. Org-mode is assumed to have already been set-up (and installed using apt-get install org-mode or via the ELPA repository).
Set-up
Add .desktop file
Create and save a file called org-protocol.desktop to ~/.local/share/applications containing:
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
Then run:
$ update-desktop-database ~/.local/share/applications/
This step makes Firefox aware that "org-protocol" is a valid scheme-handler or protocol (by updating ~/.local/share/applications/mimeinfo.cache), and causes Firefox to prompt for a program to use when opening these kinds of links.
Add config settings to ~/.emacs.d/init.el (or ~/.emacs) file
Have the following settings in your Emacs configuration file:
(server-start)
(require 'org-protocol)
Also add some template definitions to the configuration file, for example:
(setq org-protocol-default-template-key "l")
(setq org-capture-templates
'(("t" "Todo" entry (file+headline "/path/to/notes.org" "Tasks")
"* TODO %?\n %i\n %a")
("l" "Link" entry (file+olp "/path/to/notes.org" "Web Links")
"* %a\n %?\n %i")
("j" "Journal" entry (file+datetree "/path/to/journal.org")
"* %?\nEntered on %U\n %i\n %a")))
Now run Emacs.
Create your notes.org file
Assuming you use the capture templates defined in step 2, you will need to prepare a notes.org file at the location you specified in step 2. You must create this file -- if it is not created along with the headlines specified in step 2, org-mode will just give a warning when you try to capture web-pages. So, given the capture templates from step 2, notes.org should contain the following:
* Tasks
* Web Links
Add bookmarklet(s) to Firefox
Save bookmark to toolbar containing something like the following as the location:
javascript:location.href='org-protocol://capture?template=l&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection())
If you are using an older version of org-mode, you may need to use the following instead:
javascript:location.href='org-protocol://capture://l/'+encodeURIComponent(location.href)+'/'+encodeURIComponent(document.title)+'/'+encodeURIComponent(window.getSelection())
Notice the 'l' (lowercase L) in the above URL -- this is what chooses the capture template (automatically) -- it is the key one would normally have to press when capturing with org-mode via C-c c.
When you click on this bookmarklet, Firefox will ask what program to use to handle the "org-protocol" protocol. You can simply choose the default program that appears ("org-protocol").
Using it
(Optionally) select some text on a webpage you're viewing in Firefox. When you click on the bookmarklet, the link and selected text will be placed in the Emacs capture buffer. Go to Emacs, modify the capture buffer as desired, and press C-c C-c to save it.
Add protocol handler
Create file ~/.local/share/applications/org-protocol.desktop containing:
[Desktop Entry]
Name=org-protocol
Exec=emacsclient %u
Type=Application
Terminal=false
Categories=System;
MimeType=x-scheme-handler/org-protocol;
Note: Each line's key must be capitalized exactly as displayed, or it will be an invalid .desktop file.
Then update ~/.local/share/applications/mimeinfo.cache by running:
On GNOME:
update-desktop-database ~/.local/share/applications/
On KDE:
kbuildsycoca4
Configure Emacs
Init file
Add to your Emacs init file:
(server-start)
(require 'org-protocol)
Capture template
You'll probably want to add a capture template something like this:
("w" "Web site"
entry
(file+olp "/path/to/inbox.org" "Web")
"* %c :website:\n%U %?%:initial")
Note: Using %:initial instead of %i seems to handle multi-line content better.
This will result in a capture like this:
\* [[http://orgmode.org/worg/org-contrib/org-protocol.html][org-protocol.el – Intercept calls from emacsclient to trigger custom actions]] :website:
[2015-09-29 Tue 11:09] About org-protocol.el
org-protocol.el is based on code and ideas from org-annotation-helper.el and org-browser-url.el.
Configure Firefox
Expose protocol-handler
On some versions of Firefox, it may be necessary to add this setting. You may skip this step and come back to it if you get an error saying that Firefox doesn't know how to handle org-protocol links.
Open about:config and create a new boolean value named network.protocol-handler.expose.org-protocol and set it to true.
Note: If you do skip this step, and you do encounter the error, Firefox may replace all open tabs in the window with the error message, making it difficult or impossible to recover those tabs. It's best to use a new window with a throwaway tab to test this setup until you know it's working.
Make bookmarklet
Make a bookmarklet with the location:
javascript:location.href='org-protocol://capture://w/'+encodeURIComponent(location.href)+'/'+encodeURIComponent(document.title)+'/'+encodeURIComponent(window.getSelection())
Note: The w in the URL chooses the corresponding capture template. You can leave it out if you want to be prompted for the template.
When you click on this bookmarklet for the first time, Firefox will ask what program to use to handle the org-protocol protocol. If you are using Ubuntu 12.04 (Precise Pangolin), you must add the /usr/bin/emacsclient program, and choose it. With Ubuntu 12.10 (Quantal Quetzal) or later, you can simply choose the default program that appears (org-protocol).
You can select text in the page when you capture and it will be copied into the template, or you can just capture the page title and URL.
Tridactyl
If you're using Tridactyl, you can map key sequences something like this:
bind cc js location.href='org-protocol://capture://w/'+encodeURIComponent(content.location.href)+'/'+encodeURIComponent(content.document.title)+'/'+encodeURIComponent(content.document.getSelection())
You might also want to add one for the `store-link` sub-protocol, like:
bind cl js location.href='org-protocol://store-link://'+encodeURIComponent(content.location.href)+'/'+encodeURIComponent(content.document.title)
Capture script
You may want to use this script to capture input from a terminal, either as an argument or piped in:
#!/bin/bash
if [[ $# ]]
then
data="$#"
else
data=$(cat)
fi
if [[ -z $data ]]
then
exit 1
fi
encoded=$(python -c "import sys, urllib; print urllib.quote(' '.join(sys.argv[1:]), safe='')" "${data[#]}")
# "link" and "title" are not used, but seem to be necessary to get
# $encoded to be captured
emacsclient "org-protocol://capture://link/title/$encoded"
Then you can capture input from the shell like this:
tail /var/log/syslog | org-capture
org-capture "I can capture from a terminal!"
These instructions are more up-to-date than the ones in Mark's answer.
I found it is hard to keep my environment variables sync on different machines. I just want to export the settings from one computer and import to other ones.
I think it should be possible, but don't know how to do it. Can anyone help me? Thanks.
You can use RegEdit to export the following two keys:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
HKEY_CURRENT_USER\Environment
The first set are system/global environment variables; the second set are user-level variables. Edit as needed and then import the .reg files on the new machine.
I would use the SET command from the command prompt to export all the variables, rather than just PATH as recommended above.
C:\> SET >> allvariables.txt
To import the variablies, one can use a simple loop:
C:\> for /F %A in (allvariables.txt) do SET %A
To export user variables, open a command prompt and use regedit with /e
Example :
regedit /e "%userprofile%\Desktop\my_user_env_variables.reg" "HKEY_CURRENT_USER\Environment"
Combine #vincsilver and #jdigital's answers with some modifications,
export .reg to current directory
add date mark
code:
set TODAY=%DATE:~0,4%-%DATE:~5,2%-%DATE:~8,2%
regedit /e "%CD%\user_env_variables[%TODAY%].reg" "HKEY_CURRENT_USER\Environment"
regedit /e "%CD%\global_env_variables[%TODAY%].reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Output would like:
global_env_variables[2017-02-14].reg
user_env_variables[2017-02-14].reg
You can get access to the environment variables in either the command line or in the registry.
Command Line
If you want a specific environment variable, then just type the name of it (e.g. PATH), followed by a >, and the filename to write to. The following will dump the PATH environment variable to a file named path.txt.
C:\> PATH > path.txt
Registry Method
The Windows Registry holds all the environment variables, in different places depending on which set you are after. You can use the registry Import/Export commands to shift them into the other PC.
For System Variables:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
For User Variables:
HKEY_CURRENT_USER\Environment
My favorite method for doing this is to write it out as a batch script to combine both user variables and system variables into a single backup file like so, create an environment-backup.bat file and put in it:
#echo off
:: RegEdit can only export into a single file at a time, so create two temporary files.
regedit /e "%CD%\environment-backup1.reg" "HKEY_CURRENT_USER\Environment"
regedit /e "%CD%\environment-backup2.reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
:: Concatenate into a single file and remove temporary files.
type "%CD%\environment-backup1.reg" "%CD%\environment-backup2.reg" > environment-backup.reg
del "%CD%\environment-backup1.reg"
del "%CD%\environment-backup2.reg"
This creates environment-backup.reg which you can use to re-import existing environment variables. This will add & override new variables, but not delete existing ones :)
Here is my PowerShell method
gci env:* | sort-object name | Where-Object {$_.Name -like "MyApp*"} | Foreach {"[System.Environment]::SetEnvironmentVariable('$($_.Name)', '$($_.Value)', 'Machine')"}
What it does
Scoops up all environment variables
Filters them
Emits the formatted PowerShell needed to recreate them on another machine (assumes all are set at machine level)
So after running this on the source machine, simply transfer output onto the target machine and execute (elevated prompt if setting at machine level)
A PowerShell script based on #Mithrl's answer
# export_env.ps1
$Date = Get-Date
$DateStr = '{0:dd-MM-yyyy}' -f $Date
mkdir -Force $PWD\env_exports | Out-Null
regedit /e "$PWD\env_exports\user_env_variables[$DateStr].reg" "HKEY_CURRENT_USER\Environment"
regedit /e "$PWD\env_exports\global_env_variables[$DateStr].reg" "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Not being satisfied with answers from 12 years ago I've approached this a little differently. This approach could work with Win OS flavors older than Win 8 by using SET instead of SETX which is when SETX began being used.
NOTE:
Be sure to tune the RegEx for your preferred editor to achieve desired
results. For RegEx specific questions please seek help from various
sources including tutorials available from here. I'm using Sublime Text 4 for search and replace RegEx examples.
WARNING:
I would like to point out that following this process
blindly with copy and paste will most likely clobber existing settings
with the source data extracted. It DOES NOT merge the two sets of
data. That is your responsibility and I take no responsibility for
any damage that may result. Additionally, you should take time to
remove settings from the extracted env variables that pose issues or
no value such as changed paths and different hardware metrics such as
CPU core counts.
This approach avoids mixing System env variables with User env variables which a handful of previous answers are plagued with.
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment">>SystemEnvVariablesSourceMachine.txt
reg query "HKEY_CURRENT_USER\Environment">>UserEnvVariablesSourceMachine.txt
Clean up the files that were just created! Import success depends on this!
Use a RegEx capable editor and use the following search and replace:
NOTE: Some RegEx engines/tools require use of the $ character to
represent backreference in the Replace Pattern. If your not getting
the expected results in search and replace give that a try.
Search Pattern:
(?:\A\r?\n|^HKEY_CURRENT_USER\\Environment\r?\n?|^HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\r?\n?|^\r?\n$|\r?\n\Z)
Replace Pattern (Literally Empty):
Literally Empty
and then
Search Pattern:
^\s+(.*?)\s{4}\w+\s{4}(.*?)$
Replace Pattern:
\1=\2
Its strongly advised you take a moment to do the same steps above on the destination machine using these file names:
SystemEnvVariablesDestinationMachine.txt
UserEnvVariablesDestinationMachine.txt
This also will serve as a backup for the upcoming import.
Once the DestinationMachine versions of the files are cleaned up its time to make a copy. Copy of each of the DestinationMachine files and name them something like:
SystemEnvVariablesFinalMerge.txt
UserEnvVariablesFinalMerge.txt
We're not done yet, that's just a version of the file you can feel safe to edit. Leave the DestinationMachine version of the files alone. Consider them a backup.
Next we will merge the SourceMachine files into the FinalMerge files. This provides a means to
manual review for cleanup of duplicates and bad data followed by a final output. There are plenty of ways
to do this, but the easiest way I've used is to prepare the data for comparison, then compare and merge, and then reassemble the data back
so that its importable.
Apply this search and replace RegEx pattern to each Source and FinalMerge file:
Search Pattern:
(^\w+=|.*?(?:;|$))
Replace Pattern:
\1\n
Then compare each Source to FinalMerge using a diff tool such as Beyond Compare 4, Meld, or Winmerge. My personal favorite is Beyond Compare 4. Keep in mind the data at this time may not be sorted
so you can take care at this time to sort the data taking care not to mix up variables from key to value structure. How to use those tools is out of scope here. Delete env variables that you do not wish to import at this time from the FinalMerge version of the file.
Once you're satisifed with the merge with cleanup applied save the changes in the FinalMerge files then restore the key to value mapping with the following RegEx pattern:
Search Pattern:
(.)$\r?\n
Replace Pattern:
\1
Then on the destination machine import the variables with powershell:
Get-Content .\UserEnvVariablesFinalMerge.txt | ForEach-Object {
$envVarDataSplit = $($_).split("=")
if($($envVarDataSplit).count -gt 0)
{
Write-Output "Key: $($envVarDataSplit[0]) ~ Value: $($envVarDataSplit[1])"
SETX $envVarDataSplit[0] "$($envVarDataSplit[1])"
}
}
NOTE:
Run powershell as administrator for this to succeed or you will
get an error.
Get-Content .\SystemEnvVariablesFinalMerge.txt | ForEach-Object {
$envVarDataSplit = $($_).split("=")
if($($envVarDataSplit).count -gt 0)
{
Write-Output "Key: $($envVarDataSplit[0]) ~ Value: $($envVarDataSplit[1])"
SETX $envVarDataSplit[0] "$($envVarDataSplit[1])" /M
}
}
NOTE:
If you encounter an error here its likely due to a need to
escape a character. You'll need to either manually enter that env
variable or figure out the proper escaped character sequence to get
around it.
If things have gone horribly wrong you should be able to revert to your DestinationMachine versions of the env variables using the previous command with the backup.