Good day Stackoverflow.
As the title says, I have an issue with Doxygen.
Description
A PowerShell script modify the PROJECT_NUMBER variable of my Doxyfile.
Then it runs Doxygen, but it generates the documentation in HTML and LaTeX like it's reading a Default generated Doxyfile.
If I manually modify the Doxyfile before running this script, via Notepad++, Doxygen works perfectly, but once the script is ran, the issue appears.
I would also mention that my Doxyfile has:
GENERATE_HTML = YES
GENERATE_LATEX = NO
GENERATE_MAN = YES
In practice Doxygen behave like this:
.\doxygen.exe -g
\doxygen.exe .\Doxyfile
The bizzarre behaviour begins now!
Let's call my actual Doxyfile CustomConfig and the default generated DefaultConfig.
If I generate a DefaultConfig through .\doxygen.exe -g and then I overwrite its content with the text of CustomConfig via Notepad++, doxygen accepts the Doxyfile, as it should, and generates a correct output!
So the problem is not the Doxyfile content but PowerShell that modifies the file.
I've verified this by doing a simple copy&paste of the entire content:
Copy&Paste through Notepad++: WORK
Copy&Paste through PowerShell: DOESN'T WORK
PowerShell Script
# Replace the old PROJECT_NUMBER with the new one
$DOXY_PATH = $env:FS_OS + "\doc"
$CONFIG_PATH = $DOXY_PATH + "\bin\Doxyfile"
$BIN_PATH = $DOXY_PATH + "\bin\doxygen.exe"
$GIT_PATH = $env:FS_OS
$GIT_BRANCH = "Development"
# Get git commit number on the specified branch
$GIT_HASH = git log $GIT_BRANCH -1 --pretty=format:%H
$PRJ_CONTENT = Get-Content $CONFIG_PATH
$PRJ_NUM = "PROJECT_NUMBER = " + $GIT_HASH
$PRJ_CONTENT = $PRJ_CONTENT -replace "PROJECT_NUMBER\s*=\s*[A-z0-9]{40}",$PRJ_NUM
$PRJ_CONTENT | Out-File -FilePath $CONFIG_PATH
Start-Process -FilePath $BIN_PATH -ArgumentList "$CONFIG_PATH" -WorkingDirectory ($DOXY_PATH + "\bin")
Copy&Paste Script
$var = Get-Content "./doc/bin/Doxyfile.bak"
$var | Out-File -FilePath "./doc/bin/Doxyfile"
Thanks to #BenH for the comment, I've found the solution.
It looks like PowerShell writes to files automatically with BOM.
I've found a solution with the Accepted Answer from this question:
Using PowerShell to write a file in UTF-8 without the BOM
Related
I was trying to use dir command to list recursively all files that end with .cpp in a given directory, I tried to follow various solutions but my powershell seems not to accept any options after '/' sign as seen on the picture bellow:
Example
The command I initially tried was 'dir sourcefolder "*.cpp"' but it only lists files in a given folder (because I cant provide any additional options as seen in microsoft doc), also any example command provided there does not work for me giving the same error as shown in example above.
here is how I will bring out all the files in .cpp.
Here is a small program in powershell :
$path = "C:\temp\"
$filter = "*.cpp"
$files = Get-ChildItem -Path $path -Filter $filter
Write-Host "here, all the .cpp files in '$path' :"
Write-Host $files -Separator "`r`n"
I prefer to use the cmdlet "Get-ChildItem" rather than "dir".
Here the content folder for my test
And, why so many / ?
How can I create a protocol handler for a powershell script and make the target powershell script receive command line arguments?
And what are the security concerns in doing so?
I thought I write up a decent guide on doing so since the information I found online was lacking some details.
https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa767914(v=vs.85)
First off the security concerns
Any program, website, script etc. that is running on your computer can set off the protocol. There are no authorization checks.
You should NOT create a universal protocol handler. That would be a massive security issue concern. I mean that would enable a program, website, script etc. to run any powershell script or command on your computer.
Creating the protocol handler in Windows registry
The protocol must be registered in Windows Registry. It's a simple task.
I'm calling my powershell protocol handler for pwsh
Step 1: Open the Registry Editor and navigate to
Computer\HKEY_CLASSES_ROOT
For inspiration you can look at Computer\HKEY_CLASSES_ROOT\http to look at how that protocol handler is made.
Step 2: Create the following hierarchy:
Create the key pwsh: [Computer\HKEY_CLASSES_ROOT\pwsh]
Edit the default value of (Default) to URL:pwsh. Remember I call my protocol handler for pwsh, write whatever your is called.
Add a string value with the name URL Protocol and empty data.
It should look like this now:
Create a new key under pwsh, DefaultIcon: Computer\HKEY_CLASSES_ROOT\pwsh\DefaultIcon.
Set the (Default) data field to a filepath that leads to an icon or image. I used the powershell icon for Powershell 7 C:\Program Files (x86)\PowerShell\7-preview\assets\ps_black_32x32.ico.
Then create the keys shell -> open -> command like shown on the image above.
In the key command change the (Default) data value to where powershell is installed and then the powershell script to be run.
When testing I do this: "C:\Program Files\PowerShell\6\pwsh.exe" -noexit -executionpolicy bypass -Command {Write-Host %1}
Note I am using powershell core 6 and your path to powershell is probably different.
You can test to check if it works by opening the run program in Windows(Windows+R).
Expected behavior is the powershell window to open with the text pwsh:Hello Stackoverflow printed.
Step 3: Create a powershell script to handle incoming actions on the protocol.
The production ready data value for the command key: "C:\Program Files\PowerShell\6\pwsh.exe" -noexit -File C:\handleActions.ps1 %1
Param($Argument="") # If the protocol is ran you always at least get the protocol name as an argument. (if using the %1)
[String]
$Argument
function Handle-Actions { # The cmdlet 'Handle-Actions' uses an unapproved verb.
[cmdletBinding()]
param(
[Parameter(Mandatory=$false, Position=0)]
[String]
$Argument
)
$Argumuments = $Argument.Split([Char]0x003F) # Splits by `?`
#Argumnets is now in an array, do whatever you need to next.
$Argumuments | %{
Write-Host $_ # Writes each argument that was seperated by ? to a line
}
}
Handle-Actions -Argument $Argument
Given the run command pwsh:?firstArgument?SecondArgument the script will output:
pwsh:
firstArgument
SecondArgument
To complement your helpful guide with sample code that automates creation of a custom protocol handler:
The following:
Creates a custom URI protocol custom: (rather than pwsh:, given that PowerShell is simply used to implement the protocol) to which an open-ended number of arguments can be passed.
Does so for the current user only (HKEY_CURRENT_USER\Software\Classes) by default; however, it's easy to tweak the code to implement the custom protocol for all users instead (HKEY_LOCAL_MACHINE\Software\Classes), though you'll need to run the code with elevation (as administrator) then.
A handler *.ps1 script is automatically created:
At $env:USERPROFILE\customUriHandler.ps1 in the current-user scenario.
At $env:ALLUSERPROFILE\customUriHandler.ps1 in the all-users scenario.
The handler script simply echoes the arguments passed to it, and it is invoked in a PowerShell script window that is kept open after script execution (-NoExit); tweak the PowerShell command as needed.
The protocol expects its arguments as if it were a shell command, i.e., as a space-separated list of arguments, with argument-individual "..." quoting, if necessary.
The sample command at the end uses Start-Process to invoke the following URI, which you could also submit from the Run dialog (WinKey-R), which passes arguments one, two & three, four:
URI: custom:one "two & three" four
Invocation via Start-Process: Start-Process 'custom:one "two & three" four'
Caveat: If you submit this URI via a web browser's address bar (note: doesn't seem to work with Microsoft Edge), it is URI-escaped, and a single one%20%22two%20&%20three%22%20four argument is passed instead, which would require custom parsing; similarly, submitting from File Explorer's address bar passes one%20two%20&%20three%20four, though note that the " chars. are - curiously - lost in the process.
# Determine the scope:
# Set to $false to install machine-wide (for all users)
# Note: Doing so then requires running with ELEVATION.
$currentUserOnly = $true
if (-not $currentUserOnly) {
net session *>$null
if ($LASTEXITCODE) { Throw "You must run this script as administrator (elevated)." }
}
$ErrorActionPreference = 'Stop'
# The name of the new protocol scheme
$schemeName = 'custom'
$pwshPathEscaped = (Get-Process -Id $PID).Path -replace '\\', '\\'
$handlerScript = ($env:ALLUSERSPROFILE, $env:USERPROFILE)[$currentUserOnly] + "\${schemeName}UriHandler.ps1"
$handlerScriptEscaped = $handlerScript -replace '\\', '\\'
# Create the protocol handler script.
#'
# Remove the protocol scheme name from the 1st argument.
$argArray = $args.Clone()
$argArray[0] = $argArray[0] -replace '^[^:]+:'
# If the 1st argument is now empty, remove it.
if ('' -eq $argArray[0]) { $argArray = $argArray[1..($argArray.Count-1)] }
"Received $($argArray.Count) argument(s)."
$i = 0
foreach ($arg in $argArray) {
"#$((++$i)): [$arg]"
}
'# > $handlerScript
# Construct a temp. *.reg file.
# Target the scope-appropriate root registrykey.
$rootKey = ('HKEY_LOCAL_MACHINE\Software\Classes', 'HKEY_CURRENT_USER\Software\Classes')[$currentUserOnly]
# Determine a temp. file path.
$tempFile = [IO.Path]::GetTempPath() + [IO.Path]::GetRandomFileName() + '.reg'
#"
Windows Registry Editor Version 5.00
[$rootKey\$schemeName]
#="URL:$schemeName"
"URL Protocol"=""
[$rootKey\$schemeName\DefaultIcon]
#="$pwshPathEscaped"
[$rootKey\$schemeName\shell]
#="open"
[$rootKey\$schemeName\shell\open\command]
; === Tweak the PowerShell command line here: ===
#="\"$pwshPathEscaped\" -ExecutionPolicy Bypass -NoProfile -NoExit -File \"$handlerScriptEscaped\" %1"
"# > $tempFile
# Import the *.reg file into the registry.
& {
$ErrorActionPreference = 'Continue'
reg.exe import $tempFile 2>$null
if ($LASTEXITCODE) { Throw "Importing with reg.exe failed: $tempFile"}
}
# Remove the temp. *.reg file.
Remove-Item -ErrorAction Ignore -LiteralPath $tempFile
# ---
# Sample invocation of the new protocol with 3 arguments:
$uri = "$schemeName`:one `"two & three`" four"
Write-Verbose -Verbose "Invoking the following URI: $uri"
Start-Process $uri
I have post build events set up already for copying files for me dependent on ConfigurationName and want to be able to set an "environment variable" in a config (js) file on client side of an angular application to allow debug info to be visible or not dependent upon environment running application in.
To this end I've created a powershell script (ReplaceText.ps1):
function replace-file-content([String] $path, [String] $replace, [String] $replaceWith)
{
(Get-Content $path) | Foreach-Object {$_ -replace $replace,$replaceWith} | Out-File $path
}
and added this line to the post build event of my web project.
if "$(ConfigurationName)"=="LIVE" (powershell -File "$(ProjectDir)Tools\ReplaceText.ps1" "$(ProjectDir)app\application.config.js" "DEBUG" "LIVE")
which I was hoping to change the word "DEBUG" to "LIVE" when built against LIVE build configuration in my application.config.js file which contains this line:
$provide.constant('currentEnv', 'DEBUG');
Build succeeds but no changes occur on my file. Can anyone identify where I'm going wrong?
I do know I could do this sort of stuff with Gulp or another task runner BTW, but was trying to do it without bringing in another dependency and just use VS post build events & PS. :)
Cheers
Your PS code only defines a function but there's nothing that invokes it.
Use param as the first statement in the script to convert command line into the script's parameters:
param([String] $path, [String] $replace, [String] $replaceWith)
(Get-Content -literalPath $path -raw) -replace $replace, $replaceWith |
Out-File $path -encoding UTF8
-literalPath - correctly handles paths with [] symbols otherwise interpreted as a wildcard;
-raw - reads the entire file as one string for speedup, available since PowerShell 3.
Yesterday I ran the following script on some batch files on our server to replace an individual's email address as she is no longer with the company. When examining the text it worked perfectly and the log file wrote correctly as well. However, now the batch file no longer executes when I double click on it. I see a quick flash of the console window but there does not appear to be any text in it. Adding PAUSE statements is not helpful as it does not seem to execute any of the text in the file.
I copied and pasted the text to a new batch file and it works fine. I noticed that the powershell-edited file is 6KB and the new copied-and-pasted file is 3KB so clearly the script has done something unexpected to the file. Copying and pasting each file obviously defeats the purpose of using a script to batch process things. Any ideas where I'm going wrong?
I've been running the script from my development machine and I have full administrator permissions to everything on our network.
If it matters we are running Windows Server 2008 R2 Enterprise on the server.
# Finds string in batch files recursively and replaces text with other text
#
#get command line arguments
param (
[string]$Text = $( Read-Host "Input the text to search for"),
[string]$Replacement = $( Read-Host "Input the replacement text")
)
$Today=get-date -format yyyymmdd
$Results = "{server name}\c$\Batch Jobs\Find Text In Batch Jobs\ReplaceTextInBatchJobs-" + $Text + "-" + $Today + ".txt"
$Path = "{server name}\c$\Batch Jobs"
# get all the files in $Path that end in ".bat".
Get-ChildItem $Path -Filter "*.bat" -Recurse |
Where-Object { $_.Attributes -ne "Directory"} |
ForEach-Object {
#Find whether there is a matching string
If (Get-Content $_.FullName | Select-String -Pattern $Text) {
#Replace the text in this file
(Get-Content $_.FullName) | Foreach-Object {$_ -replace $Text, $Replacement} |
Out-File $_.FullName #write the new results back to the file
#write the file name to a log file
$_.FullName >> $Results
}
}
Out-File defaults to Unicode encoding (which is why the file doubles in size; each character is 16 bits). You can either use Out-File -Encoding ASCII or Set-Content which defaults to ASCII.
I'm replacing parts of a .bat script with PowerShell. Configuration for the batch files is done via files that set appropriate environment variables. I'm looking for a way to load those variable values into the .ps1 script, without modifying the .bat files (as they are also used in other places.
An example .bat looks as follows:
set VAR_ONE=some_value
set VAR_TWO=/other-value
In a batch script, I'd just CALL the configuration file and the variables would be available. I've tried both dot-sourcing (. filename.bat) and calling (& filename.bat) the configuration files from PowerShell, neither of those makes the variables visible. Tried accessing them with both with $VAR_ONE and $env:VAR_ONE syntax.
What would be a good way to load such configuration file without modifying it's format on disk?
If you are using the PowerShell Community Extensions, it has a Invoke-BatchFile that does this. I use with the Visual Studio vcvarsall.bat file to configure my PowerShell session to use the Visual Studio tools.
I'd parse them (just skip all lines that don't start with set and split them with first = character. You can do it from o small C# cmdlet or directly with a small PowerShell script:
CMD /c "batchFile.bat && set" | .{process{
if ($_ -match '^([^=]+)=(.*)') {
Set-Variable $matches[1] $matches[2]
}
}}
I have this code and I'm sure it comes from somewhere but credits have been lost, I suppose it comes from Power Shell Community Extensions for an Invoke-Batch script.
The preferred option would be to change the configuration to a .ps1 file and change the variable definitions to PowerShell syntax:
$VAR_ONE = 'some_value'
$VAR_TWO = '/other-value'
Then you'll be able to dot-source the file:
. filename.ps1
If you want to stick with the format you currently have, you'll have to parse the values, e.g. like this:
Select-String '^set ([^=]*)=(.*)' .\filename.bat | ForEach-Object {
Set-Variable $_.Matches.Groups[1].Value $_.Matches.Groups[2].Value
}
Note: The above won't work in PowerShell versions prior to v3. A v2-compatible version would look like this:
Select-String '^set ([^=]*)=(.*)' .\filename.bat | ForEach-Object {
$_.Matches
} | ForEach-Object {
Set-Variable $_.Groups[1].Value $_.Groups[2].Value
}
You can do that via a Batch file that first call the configuration file and then execute the PowerShell script.
Assuming the .bat is called test.bat, define testps.ps1:
$lines = cat "test.bat"
$output = #{};
foreach ($line in $lines) {
$bits = $line.Split("=");
$name = $bits[0].Split(" ")[1];
$val = $bits[1];
$output[$name] = $val
}
return $output
Then the result is something like:
C:\temp> .\testps.ps1
Name Value
---- -----
VAR_TWO /other-value
VAR_ONE some_value
C:\temp> $x = .\testps.ps1
C:\temp> $x
Name Value
---- -----
VAR_TWO /other-value
VAR_ONE some_value
C:\temp> $x["VAR_ONE"]
some_value
There is probably a nicer way of doing the splits (will edit if I find it)