How to replace similar code in VS 2017. - powershell

I am replacing our logging functionality and it is taking a long time to manually go through all of the code and replace it.
Here is the current code:
Error Messages:
cLogger.LogMessage(ComponentID.ClientID, CLASS_NAME, "AddContextMenuItem", MessageType.mtErrorMessage, "Null MenuItem provided. MenuItem's status not changed");
cLogger.LogMessage(ComponentID.ClientID, CLASS_NAME, "enableDisableToolbarItem", MessageType.mtErrorMessage, "Invalid toolbaritem provided.");
Exceptions:
cLogger.LogMessage(ComponentID.ClientID, CLASS_NAME, "enableDisableContextMenuItem", MessageType.mtException, ex);
cLogger.LogMessage(ComponentID.ClientID, CLASS_NAME, "AddToolbarItem", MessageType.mtException, exc);
Is there a simple way to create a macro (never used a macro before) or power shell or notepad++ script or something else to find and replace all of these different instances so that they look like the following:
New Error Messages:
logger.Log(LogLevel.Error, CLASS_NAME + " AddContextMenuItem - Null MenuItem provided. MenuItem's status not changed");
logger.Log(LogLevel.Error, CLASS_NAME + " enableDisableToolbarItem - Invalid toolbaritem provided.");
and
New Exceptions:
logger.Log(LogLevel.Exception, CLASS_NAME + " enableDisableContextMenuItem - " + ex);
logger.Log(LogLevel.Exception, CLASS_NAME + " AddToolbarItem - " + exc);
I am replacing the code in the entire project and it will just simply take way too long to go through and manually change all of the logging code manually. Any help is greatly appreciated.

There are a few options:
Regex Search & Replace in Visual Studio:
search for the exception example
\w+logger.LogMessage\([^,]+,([^,]+),([^,]+),[^,]+,([^\",]+)\);
replace
logger.Log(LogLevel.Exception, $1 + $2 + $3);
Use Resharper structural Search & Replace
Build a CodeFix for Roslyn

Yes, you can likely do this with a Regular Expression, easier in PowerShell perhaps than in Notepad++ or perhaps VSCode.
It's difficult to tell from your examples precisely what you are changing in each item, but the basic concept is to do the following:
Match the static text that establishes the type of item to change
Also match the variable text with wildcards (.* etc) enclosed in CAPTURING parentheses
Replace with new static text and 'rearranged' variable text using the $1, $2, etc backreferences to the capture groups (or $Matches[1] etc.)
If #3 is more complicated, you'll need to further alter the variable text before replacing -- this is where a script language has an advantage over a pure search and replace.
Here is a simplified example (PowerShell but similar in other langauges or editors that support Regexes) for statically replacing the "FunctionOldName" while swapping the order of Param1 and Param2 and altering the names based on the original names for these params:
"Function_OldName Param1 Param2" -replace 'Function_OldName\s+(\w+)\s+(\w+)',
'NewFunctionName New$2Parm New$1Parm'
The $1 and $2 are backreferences to the "1st parens" and "2nd parens" respectively in the match string.
If you can write out clear examples showing which parts of your changed text must be matched, simply altered, rearranged, or rebuilt then it might be possible to show you some more relevant examples....
You can do this across many files with either PowerShell or the editors, but generally doing it to many files is again a bit easier in a Programming language (e.g., PowerShell.)
Get-ChildItem *.PS1 -recurse | ForEach-Object {
'Function_OldName\s+(\w+)\s+(\w+)', # your match goes here
'NewFunctionName New$2Parm New$1Parm' # your replacement goes here
}

Related

What am I allowed to name a function in Powershell?

PS > function ]{1}
PS > ]
1
PS >
PS
Why does this work?
What else can I name a function? All I've found so far that works is * and ].
You can name it almost anything. You can even include newlines and emoji* in the name.
function Weird`nFunctionの名前😀 { Write-Host hey }
$c = gcm Weird*
$c.Name
& $c
Escaping helps with lots of things like that:
function `{ { Write-Host cool }
`{
function `0 { Write-Host null }
gci function:\?
I'll add that this is true for variables too, and there's a syntax that removes the need to do most escaping in the variable name: ${varname} (as opposed to $varname).
With that, you could easily do:
${My variable has a first name,
it's
V
A
something
R,
whatever I dunno
🤷} = Get-Process
You'll note that if you then start typing like $MyTAB it will tab complete in a usable way.
To (somewhat) answer why this should work, consider that the variable names themselves are just stored in .Net strings. With that in mind, why should there be a limit on the name?
There will be limits on how some of these names can be used in certain contexts, because the parser will not understand what to do with it if the names don't have certain characters escaped, but literal parsing of PowerShell scripts are not the only way to use functions or variables or other language constructs, as I've shown some examples of.
Being less limiting also means being able to support other languages and cultures by having wide support for character sets.
To this end, here's one more thing that might surprise you: there are many different characters to represent the same or similar things that we take for granted in code, like quotation marks for example.
Some (human) languages or cultures just don't use the same quote characters we do in English, don't even have them on the keyboard. How annoying would it be to type code if you have to keep switching your keyboard layout or use ALT codes to quote strings?
So what I'm getting at here is that PowerShell actually does support many quote characters, for instance, what do you think this might do:
'Hello’
Pretty obvious it's not the "right" set of quotes on the right side. But surprisingly, this works just fine, even though they aren't the same character.
This does have important implications if you're ever generating code from user input and want to avoid sneaky injection attacks.
Imaging you did something like this:
Invoke-Expression "echo '$($userMsg -replace "'","''")'"
Looks like you took care of business, but now imagine if $userMsg contained this:
Hi’; gci c: -recurse|ri -force -whatif;'
For what it's worth, the CodeGeneration class is aware of this stuff ;)
Invoke-Expression "echo '$([System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($userMsg))'"
* PowerShell Console doesn't have good support for Unicode, even though the language does. Use ISE to better see the characters.

How to escape quotation marks in Enterprise Architect Code Generation Template

I want to give enumeration values a further description. Therefore I'm adding a custom Tagged Value to the attributes of the enumeration called Description, if a description shall be provided. The goal is, to add a custom C# attribute to the tagged enumeration attribute during code generation, but only if such a Tagged Value exists. Therefore I need to edit the code generation template for Attribute Declaration. Currently it works using:
$hasDescription = %attTag:"Description" ? "true" : "false"%
%if $hasDescription == "true"%
[Description(%qt%%attTag:"Description"%%qt%)]
%endIf%
which gives me the desired output. But if there are quotation marks in the value, it breaks the output code file. It won't compile. Therefore I need to replace/escape all quotation marks in the value field of the Tagged Value. I tried the following (in various combinations):
%REPLACE(attTag:"Description", "\"", "\\\"")%
%REPLACE(attTag:"Description", """", "\\""")%
%REPLACE(attTag:"Description", "%qt%", "%sl%%qt%")%
%REPLACE(attTag:"Description", %qt%, %sl%%qt%)%
Note: %qt% is used to insert ", %sl% is used to insert \ (reference)
None of them works. Either the string as it is will be inserted into the generated code file or nothing happens to the quotation marks in the value of Tagged Value.
So is there a way to escape those characters to be able to replace them in a string within a Code Template?
Using Enterprise Architect 13.5.1351
Question asked first on SE Software Engineering
I looked through the other templates provided and finally found the solution after some more fiddling. The macro take either some text in quotation marks or variables as parameters. Since using the escape sequences directly in the REPLACE macro didn't work, I tried assigning them to variables beforehand:
$qt = %qt%
$escape = %sl% + %qt%
$description = %REPLACE(attTag:"Description", $qt, $escape)%
That's it. Finally works. It is important to add the + between %sl% and %qt% on the second line, even though the documentation on Code Template Syntax > Literal Text states it otherwise. $escape = %sl%%qt% does not work, as it yields me just a \ without the ".
The variable $description is not necessary, but added for readability.

VFP macro expansion in USE statement

A table is being opened in a folder whose name is being provided by the user.
lFolder = Getfile()
lFilename = lFolder + “mytable.dbf”
USE &lFilename IN 0 ALIAS . . .
This usually works fine. However, if the folder whose name is supplied by the user has an embedded space, so ‘My folder’, the USE instruction fails. But this instruction works successfully :
USE (lFilename) IN 0 . . .
Are there any rules which say when one should use the Ampersand (&) construct and when one should use the bracket construct? And is this only applicable to the USE statement?
Thanks. Andrew
The proper way to write that code is:
local lFolder, lFilename
lFolder = Getdir()
lFilename = addbs(m.lFolder) + 'mytable.dbf'
* or a single GetFile() to select the dbf directly
USE (m.lFilename) IN 0 ALIAS . . .
There are more than one point in this code:
1) Declare your variables as local. Without that declaration, it would work and VFP would implicitly declare them as private. It is a good practice to declare local and also would help with intellisense, if you use tools like ISX.
2) Using addbs() ensures a backslash. It is just coding safe.
3) Use m. (aka mdot) for memory variables. Using mdot, you are telling VFP explicitly that it is a memory variable. Using mdot there is no harm, but if you don't you might get into hard to catch bugs (and also in tight loops, it is proven to be much faster using mdot).
4) Finally, your original question. A Filename is a "name" so do not use a macro expansion (&) operator but "name expression" anywhere there is a Name. A "name expression" is simply a set of parentheses. If something is a "name", then use "name expression" (a fieldName, fileName, folderName, variableName ...).
Apart from rules, unfortunately many VFP developers abuse the & and use it too often. In reality, probably it has too few places where using makes sense and that is SQL clauses. Not something like:
lcSQL = "select * from ..." + ... + "..."
&lcSQL
(which often you may see this pattern as well) but this one where parts of SQL use macro expansion. ie:
select &fieldList ;
from (m.tableName) ;
where &lcWhere ;
order by &lcOrder
Note that m.tableName is a "name" and thus used with "name expression". FieldList variable might be holding a single fieldName or a series of fieldNames (ie: "CustomerId" or "CustomerId, CompanyName, ContactName") and cannot be used as a "name expression", needs to be macro expanded.

What does the period '.' operator do in powershell?

This is a weird one. Normally when I execute an external command from powershell I use the & operator like this:
& somecommand.exe -p somearguments
However, today I came across the . operator used like this:
.$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd install notepadplusplus
What purpose does the period serve in this scenario? I don't get it.
The "." dot sourcing operator will send AND receive variables from other scripts you have called. The "&" call operator will ONLY send variables.
For instance, considering the following:
Script 1 (call-operator.ps1):
clear
$funny = "laughing"
$scriptpath = split-path -parent $MyInvocation.MyCommand.Definition
$filename = "laughing.ps1"
"Example 1:" # Call another script. Variables are passed only forward.
& $scriptpath\$filename
"Example 2:" # Call another script. Variables are passed backwards and forwards.
. $scriptpath\$filename
$variableDefinedInOtherScript
Script 2 (laughing.ps1):
# This is to test the passing of variables from call-operator.ps1
"I am $funny so hard. Passing variables is so hilarious."
$variableDefinedInOtherScript = "Hello World!"
Create both scripts and ONLY run the first one. You'll see that the "." dot sourcing operator sends and receives variables.
Both have their uses, so be creative. For instance, the "&" call operator would be useful if you wanted to modify the value(s) of variables in another script while preserving the original value(s) in the your current script. Kinda a safeguard. ;)
The Short:
It is a Special Operator used to achieve what regular operators cannot achieve. This particular operator . actually has two distinctively different Special Operator use cases.
The Long:
As with any other language, scripting or otherwise, PowerShell script also supports many different types of Operators to help manipulate values. These regular operators include:
Arithmetic
Assignment
Comparison
Logical
Redirection
List item
Split and Join
Type
Unary
However, PowerShell also supports whats known as Special Operators which are used to perform tasks that cannot be performed by the other types of operators.
These Special Operators Include:
#() Array subexpression operator
& Call operator
[ ] Cast operator
, Comma operator
. Dot sourcing operator
-f Format operator
[ ] Index operator
| Pipeline operator
. Property dereference operator
.. Range operator
:: Static member operator
$( ) Subexpression operator
. Dot sourcing operator: is used in this context to allow a script to run in the current scope essentially allowing any functions, aliases, and variables which has been created by the script to be added to the current script.
Example:
. c:\scripts.sample.ps1
NoteThat this application of the . Special Operator is followed by a space to distinguish it from the (.) symbol that represents the current directory
Example:
. .\sample.ps1
. Property dereference operator: Allows access to the properties and methods of of an object which follows the . by indicating that the expression on the left side of the . character is an object and the expression on the right side of the is an object member (a property or method).
Example:
$myProcess.peakWorkingSet
(get-process PowerShell).kill()
Disclaimer & Sources:
I had the same question while looking at a PowerShell script that I was trying to expand on its feature sets and landed here when doing my research for the answer. However I managed to find my answer using this magnificent write up on the Microsoft Development Network supplemented with this further expansion of the same ideas from IT Pro.
Cheers.
The dot is a call operator:
$a = "Get-ChildItem"
. $a # (executes Get-ChildItem in the current scope)
In your case, however, I don't see what it does.
.Period or .full stop for an objects properties; like
$CompSys.TotalPhysicalMemory
See here: http://www.computerperformance.co.uk/powershell/powershell_syntax.htm#Operators_
This answer is to expand slightly upon those already provided by David Brabant and his commenters. While those remarks are all true and pertinent, there is something that has been missed.
The OPs use of & when invoking external commands is unnecessary. Omitting the & would have no effect (on the example of his usage). The purpose of & is to allow the invocation of commands whose names are the values of a (string) expression. By using the & above, powershell then (essentially) treats the subsequent arguments as strings, the first of which is the command name that & duly invokes. If the & were omitted, powershell would take the first item on the line as the command to execute.
However, the . in the second example is necessary (although, as noted by others, & would work just as well in this case). Without it, the command line would begin with a variable access ($env:systemdrive) and so powershell would be expecting an expression of some form. However, immediately following the variable reference is a bare file path which is not a valid expression and will generate an error. By using the . (or &) at the beginning of the line, it is now treated as a command (because the beginning doesn't look like a valid expression) and the arguments are processed as expandable strings (" "). Thus, the command line is treated as
. "$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd" "install" "notepadplusplus"
The first argument has $env:systemdrive substituted into it and then . invokes the program thus named.
Note: the full description of how powershell processes command line arguments is way more complicated than that given here. This version was cut down to just the essential bits needed to answer the question. Take a look at about_Parsing for a comprehensive description. It is not complete but should cover most normal usage. There are other posts on stackoverflow and github (where powershell now resides) that cover some of the seemingly quirky behaviour not listed in the official documentation. Another useful resource is about_Operators though again this isn't quite complete. An example being the equivalence of . and & when invoking something other than a powershell script/cmdlet getting no mention at all.

Can I affect quoting in a Powershell tab expansion function?

Powershell tab expansion functions take 2 parameters, the line so far, and the "current word". The function should return a replacement for the current word.
From experiment, it seems to me that the current word is passed to the function without any quotes, and the returned word is inserted into the line with the same quoting as the original. So, for example, if I type
PS> foo "bar"<TAB>
I will get the string bar passed to my tab expansion function (without quotes), and my returned value will be placed back on the line in double quotes.
This behaviour causes problems in certain cases. For example, partial completion of file names, where I might type C:\Pro<TAB> to get "C:\Program Files", but I then need to delete the final quote to expand further (say, by typing \Micro and then hitting TAB again.
Also, returning an expanded value containing quotes can be very messy:
PS> function TabExpansion($line, $lastword) {
PS> "looks like '" + $lastword + "' when quoted"
PS> }
PS>
PS> Silly 'example'<TAB>
This results in unbalanced quotes.
Is there any way of avoiding or working around this behaviour?
Paul.
First off, this is not true:
This behaviour causes problems in
certain cases. For example, partial
completion of file names, where I
might type C:\Pro to get
"C:\Program Files", but I then need to
delete the final quote to expand
further (say, by typing \Micro and
then hitting TAB again.
You can continue typing the \Micro after the quote and it will take care of it for you.
If you really need to return a value containing quotes, you can inject the escape character (`) into your string. Note that you will need to escape the escape character itself so it doesn't get eaten:
function TabExpansion($line, $lastword){
"looks like ``'" + $lastword + "``' when quoted"
}
After Tab expansion, your example will look like:
Silly "looks like `'example`' when quoted"
and the parser should have no problem with it.