When I run the backtick (`) command on this snippet:
Get-WmiObject win32_service | Where-Object { $_.pathname -notlike "C:\windows\*" -and $_.startmode -eq "auto" -and $_.startname -eq "localsystem"} | Select-Object displayname, `
pathname, startmode, startname | Format-List | Out-Host
I get some errors. What are those errors?
First, by pressing F8 just on the first line, I get this:
Incomplete string token.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : IncompleteString
Objective: Running the snippet and assuming the PC will jump onto the next line automatically, since it has the ` character.
Second, when I highlight just the first line, by clicking to the left of the line numbers, I now get this:
At line:1 char:170
... to" -and $_.startname -eq "localsystem"}|Select-Object displayname, `
~ Missing expression after ',' > in pipeline element.
CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : MissingExpression
Objective: Running the snippet and assuming the PC will jump onto the next line automatically, since it has the ` character.
However, when I press F5 it works like a charm. Please forgive my ignorance on PowerShell, but what am I doing wrong here?
Additional info:
This is my powershell information:
Name: Windows PowerShell ISE Host
Version: 5.1
Your errors are coming from features of PowerShell ISE.
First you're trying to use F8 which is "Run Selection", on the first line, with no selection made. That will implicitly select all the characters of the first line, then try to run that.
When you do so, you get the incomplete string token error, and that's because the parser has encountered the lone backtick ` (the escape character), with no character following it. That's because the newline at the end of the line, which the backtick is usually escaping (that's how it works as a line continuation character), is missing, since the single line selection didn't include it.
Second, when I highlight just the first line, by clicking to the left of the line numbers
Now in this case you'll notice that your selection has placed the cursor at the beginning of the next line. That means this selection does include the newline, and so you now have a complete string token.
Your error is that you've now ended your command with a comma, as though you're going to pass more parameters, and then nothing comes after (the rest of the parameters were not included in your selection).
The root of the issue is that the commands in ISE that deal with running a selection, are doing exactly that, so if you want them to include things that are on the next line, you must include them in the selection too.
As a side note, I might recommend that you look for code elements which let you naturally use line breaks, such as the pipe | character, operators, and scriptblock braces {}.
Get-WmiObject win32_service |
Where-Object {
$_.pathname -notlike "C:\windows\*" -and
$_.startmode -eq "auto" -and
$_.startname -eq "localsystem"
} |
Select-Object displayname, pathname, startmode, startname |
Format-List |
Out-Host
This doesn't solve your selection problem, but it's nicer to read in my opinion, and doesn't require the awkward backtick.
Following up on #Santiagos link comment:
This is referred to as Statement Termination. When using Powershell, there are 2 statement terminator charcters.
Semicolon - ;
Newline (sometimes)
The rule for the newline is only sometimes due to the nature of how some users will use commands. Basically meaning that if the previous text is syntactically a complete statement, a new line is considered to be a statement termination. So, if it isn't complete, the newline is treated as whitespace.
Quick example:
PS C:\Users\Abraham> 3 +
>> 4
7
When I added the + operator, it was expecting another argument which is why it didn't error out.
In your case, I will assume the error came from powershells Tokenizer (lexical analyzer) when parsing through your command, as it read your backtick as an escape character for your comma (assuming it was complete - hence the statement terminator - reading the next line as a newline and not whitespace); in my opinion ( and someone correct me if im wrong ), I think it was a bug which was a False-Positive.
The Backtik (`) also known as the Escape Character can extend a line that isn't extensible. If the last character in the line is a backtick, then the newline will be treated as whitespace and not a "newline".
So you don't need that backtick due to your comma (,) telling powershell that theres more that come after it, which we reference back to the 2 paragraph for being syntactically incomplete.
In summary: it was a mistake on powershells end(:
PS - Sorry about the long post, since the first part isn't really needed to answer your question; just some info that can help you understand how it comes together.
EDIT:
Didn't see the F8 being the culprit here lol thanks to #briantist for pointing it out. Error wasn't on Powershells end. Disregard this post (:
Related
I'm attempting to check the RelayForAuth setting for my Windows SMTP Server using the below commands. Powershell appears to display the correct result 'False' but when running the same command via command prompt, it generates an error:
Powershell Example:
([ADSI]"IIS://localhost/smtpsvc/1".RelayForAuth -like "*0*")
Output:
False
Command Prompt Example:
powershell -command "([ADSI]"IIS://localhost/smtpsvc/1".RelayForAuth -like "*0*")"
Output:
At line:1 char:8
+ ([ADSI]IIS://localhost/smtpsvc/1.RelayForAuth -like *0*)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'IIS://localhost/smtpsvc/1.RelayForAuth' in expression or
statement.
At line:1 char:8
+ ([ADSI]IIS://localhost/smtpsvc/1.RelayForAuth -like *0*)
+ ~
Missing closing ')' in expression.
At line:1 char:56
+ ([ADSI]IIS://localhost/smtpsvc/1.RelayForAuth -like *0*)
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Since you're nesting (embedding) " chars. - to be passed verbatim to PowerShell - inside the syntactic outer double-quoting ("..."), you must escape those nested " chars.
Even though PowerShell-internally ` serves as the escape character, calling the PowerShell CLI (powershell.exe / pwsh) from the outside (cmd.exe) requires \-escaping of ":
# Embedded " chars. must be \-escaped
powershell -command "([ADSI]\"IIS://localhost/smtpsvc/1\").RelayForAuth -like \"*0*\""
Note that you can avoid the need for this escaping if you single-quote the strings inside the overall "..." string.
While this works fine in your case, given that your strings have only verbatim content, note that this is generally only an option if no string interpolation is required:
# Embedded strings use '...' -> no escaping needed.
powershell -command "([ADSI]'IIS://localhost/smtpsvc/1').RelayForAuth -like '*0*'"
Caveat: Using single-quoting to enclose the overall command ('...') does not work as expected from cmd.exe: the latter doesn't recognize these as quoting, and PowerShell simply interprets the string as using its syntax for a verbatim string, and therefore simply prints the contents of the string.
For more information, see this answer.
I'm trying to better understand how backticks work in PowerShell. This works and executes the ipconfig command:
$a = "ipc"
$b = "onf`ig"
iex $a$b
However, if the backtick is moved one character to the left, before the "f", the command breaks...
$a = "ipc"
$b = "on`fig"
iex $a$b
Another example of this:
who`ami
If the backtick is anywhere else, the whoami command will work just fine. With a backtick in the middle, it breaks.
What's happening here? Why does the placement of the backtick's matter so much?
These are becuase some special characters in powershell.
In powershell there are some special characters which are not in standard character set. They start with back tick to show special meaning. They are:
`0 Null
`a Alert
`b Backspace
`e Escape
`f Form feed
`n New line
`r Carriage return
`t Horizontal tab
`u{x} Unicode escape sequence
`v Vertical tab
Here when you escape "a" with backtick
means alert powershell (whoami) and when you escape "f" with backtick means form feed (ipconfig), so both commands break.
And when you escape the other character, commands don't break becuase then characters not render the special meaning.
Though I don't agree with all the author of this article says. Most of the is valid when it comes to use of the graveyard accents\bact tick.
It does have its use cases, but not for what you are showing.
Bye Bye Backtick: Natural Line Continuations in PowerShell]
See also:
about_Special_Characters - PowerShell | Microsoft Docs
PowerShell - Special Characters And Tokens
Grave_accent
Use in programming Programmers use the grave accent symbol as a
separate character (i.e., not combined with any letter) for a number
of tasks. In this role, it is known as a backquote, or backtick.
Many of the Unix shells and the programming languages Perl, PHP, and
Ruby use pairs of this character to indicate command substitution,
that is, substitution of the standard output from one command into a
line of text defining another command. For example, using $ as the
symbol representing a terminal prompt, the code line...
How-to: Escape characters, Delimiters and Quotes
I have a log file, and I want to get rid of the third column that start with "external", this column is not always in the third place so I need to find the word "external" and then delete it with the string that follows the colon.
I was thinking in using -replace for that, but does "-replace" accept some regex to delete the rest of the string (after the semicolons) that is always changing?
or maybe there is a better way to do this?
02/02/2020 name:VAL_NATURE external:af2045b2-5992-432e-b790-c1ad4743038 status:good
cat mylog.log | %{$_ -replace "external???",""}
With any delimited file, the first thought I have is to break it at the delimiters (in your case, the white space) and treat it like an object. Deleting a column is trivial if you do that, and it lets you have easy access to the data for other purposes.
If, however, your only task is to remove that column with 'external' + colon + all text up to the next bit of white space, that is an easy thing to do with a regex replace.
$line = '02/02/2020 name:VAL_NATURE external:af2045b2-5992-432e-b790-c1ad4743038 status:good'
$line -replace 'external:.*\s',''
EDIT: Tested the code above, and got this output:
02/02/2020 name:VAL_NATURE status:good
The . is any character, and .* says "any character zero or more times" it continues matching until it gets to whitespace, which is represented by the \s. So this regex matches the word 'external' followed by a ':' followed by zero or more other characters followed by whitespace (space/tab/etc).
I am trying to extract each line from a CSV that has over 1million (1,000,000) lines, where the first character is a 1.
The 1 in this case, refers to the 1st line of a log. There are several different logs in this file, and I need the first line from all of them. Problem is (as you could understand) 1 is not unique, and can appear in any of the 12 'columns' of data I have in this CSV
Essentially, I would like to extract them all to a new CSV file as well, for further break down.
I know it sounds simple enough, but I cannot seem to get the information I need.
I have searched StackOverflow, Microsoft, Google and my own Tech Team.
PS: Get-Content 'C:\Users\myfiles\Desktop\massivelogs.csv' | Select-String "1" | Out-File "extractedlogs.csv"
The immediate answer is that you must use Select-String '^1 in order to restrict matching to the start (^) of each input line.
However, a much faster solution is to use the switch statement with the -File` option:
$inFile = 'C:\Users\myfiles\Desktop\massivelogs.csv'
$outFile = 'extractedlogs.csv'
& { switch -File $inFile -Wildcard { '1*' { $_ } } } | Set-Content $outFile
Note, however, that the output file won't be a true CSV file, because it will lack a header row.
Also, note that Set-Content applies an edition-specific default character encoding (the active ANSI code page in Windows PowerShell, BOM-less UTF-8 in PowerShell Core); use -Encoding as needed.
Using -Wildcard with a wildcard pattern (1*) speeds things up slightly, compared to -Regex with ^1.
I have a unix script (korn to be exact) that is working well and I need to convert it windows batch script. So far I have tried inserting a powershell command line on my code, but it doesn't work. Please help, I am just new to both unix scripting and windows scripting so any help will do.
This is the line of code that I need to convert:
#create new file to parse ; exclude past instances of timestamp
parsefile=/tmp/$$.parse
sed -e "1,/$TIMESTAMP/d" -e "/$TIMESTAMP/d" $DSTLOGFILE > $parsefile
So far I have tried a powershell command line to be called on my script but it didn't work:
:set_parse_file
#powershell -Command "Get-Content $SCHLOGFILE | Foreach-Object {$_ -replace('1,/"$TIMESTAMP"/d' '/"$TIMESTAMP"/d'} | Set-Content $PARSEFILE"
Any suggestions please?
PowerShell has no sed-like constructs for processing ranges of lines (e.g., sed interprets 1,/foo/ as referring to the range of consecutive lines from line 1 through a subsequent line that matches regex foo)
Emulating this feature with line-by-line processing would be much more verbose, but a comparatively more concise version is possible if the input file is processed as a whole - which is only an option with files small enough to fit into memory as a whole, however (PSv5+ syntax).
Here's the pure PowerShell code:
$escapedTimeStamp = [regex]::Escape($TIMESTAMP)
(Get-Content -Raw $SCHLOGFILE) -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') `
-replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') |
Set-Content -NoNewline $PARSEFILE
Note that [regex]::Escape() is used to make sure that the value of $TIMESTAMP is treated as a literal, even if it happens to contain regex metacharacters (chars. with special meaning to the regex engine).
Your ksh code doesn't do that (and it's nontrivial to do in ksh), so if - conversely - $TIMESTAMP should be interpreted as a regex, simply omit that step and use $TIMESTAMP directly.
The -replace operator is regex-based and uses the .NET regular-expression engine.
It is the use of Get-Content's -Raw switch that requires PSv3+ and the use of Set-Content's -NoNewline switch that requires PSv5+. You can make this command work in earlier versions, but it requires more effort.
Calling the above from cmd.exe (a batch file) gets quite unwieldy - and you always have to be wary of quoting issues - but it should work:
#powershell.exe -noprofile -command "$escapedTimeStamp = [regex]::Escape('%TIMESTAMP%'); (Get-Content -Raw '%SCHLOGFILE%') -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') -replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') | Set-Content -NoNewline '%PARSEFILE%'"
Note how the -command argument is passed as a single "..." string, which is ultimately the safest and conceptually cleanest way to pass code to PowerShell.
Also note the need to embed batch variables as %varname% in the command, and since they are enclosed in embedded '...' above, the assumption is that their values contain no ' chars.
Therefore, consider implementing your entire script in Powershell - you'll have a much more powerful scripting language at your disposal, and you'll avoid the quoting headaches that come from bridging two disparate worlds.