PowerShell date format not behaving - string

I'm stumped. :)
My computer has PowerShell 5.1 installed. On another computer (same language) 5.0 it works as expected. (Check using Get-Culture; my locale is nb-NO (Norwegian) )
Consider this:
Get-Date
returns
tirsdag 23. mai 2017 13.13.18
So I do this
Get-Date -Format "H-m-s"
as expected it returns
13-13-18
But then I do this
Get-Date -Format "H:m:s"
You think it returns
13:13:18
right? (it does on PS5.0!) No! I get this:
13.13.18
Only if I do this, is the output what I want:
Get-Date -Format "H\:m\:s"
13:13:18
Can someone please explain why this is? I discovered it "by accident" when I wanted to format a datetime-compatible string for use in SQL Server.

That's because the underlying DateTime formatting function see's : and treats it as a culture-dependent "time separator".
In Norwegian (no-NO), the default time separator is .. You can inspect this with (assuming that no-NO is the current culture):
PS C:\> [CultureInfo]::CurrentCulture.DateTimeFormat.TimeSeparator
.
You can also override this, either by inserting a literal :, with the escape sequence \: as you've already found, or you can override it globally (for the lifetime of the current process/appdomain):
PS C:\> [CultureInfo]::CurrentCulture.DateTimeFormat.TimeSeparator = ":"
PS C:\> Get-Date -Format "H:m:s"
13:13:18

Related

Extract the last component from a path string in PowerShell

I have a Question about shortening a string in PowerShell. So for example we have this code here:
$path = "C:\Pow\temp\temp2"
Now, I wanted to shorten this String, even if I do not know it, but what I know is that it is a Path. So from this Path I want to get only the name of the last folder so from 'temp2'. Does someone know with what PowerShell Code you can do that? I would be happy for an answer, thank you.
In PowerShell 3.0 and newer, use Split-Path:
PS ~> $path = "C:\Pow\temp\temp2"
PS ~> Split-Path $path -Leaf
temp2
In PowerShell 2.0, use Path.GetFileName():
PS ~> [System.IO.Path]::GetFileName($path.TrimEnd('\'))
temp2

How to remove newlines from a text file with batch or PowerShell

Essentially, I want to read from file.txt with contents
apple
banana
carrot
and write to newfile.txt so that it will have contents
apple banana carrot
I need to do this on a Windows computer on which I do not have install permissions.
I tried
set row=
for /f %%x in (file.txt) do set row=%row% %%x
echo row > newfile.txt
and I tried using PowerShell statements (I cannot run PowerShell scripts) instead of the CMD-style for loop.
powershell -Command "(Gc file.txt) | Foreach-Object -Process {set row=%row% _$} | Out-File newFile.txt"
but both produce an empty file.
Is there a way to do this?
Get-Content returns the content of a file as an array of lines with the line breaks already removed, so all you need to do (in PowerShell) is to join the lines and write the result back to a file:
(Get-Content 'input.txt') -join ' ' | Set-Content 'output.txt'
Not recommended, but if you must do this in batch you need something like this:
#echo off
setlocal EnableDelayedExpansion
set row=
for /f %%x in (file.txt) do set "row=!row! %%x"
>newfile.txt echo %row%
Note that delayed expansion is required for this to work. Without it %row% in the loop body would be expanded at parse time (when the variable is still empty), so you'll end up with just the last line from the input file in the variable after the loop completes. With delayed expansion enabled (and using !row! instead of %row%) the variable is expanded at run time, i.e. during the loop iterations as one would normally expect.
For further information on delayed expansion see Raymond Chen's blog.
To complement Ansgar Wiechers' helpful answer:
Executing the following command from a batch file / a cmd.exe console window should do what you want:
powershell -command "\"$(Get-Content file.txt)\" > newFile.txt"
Note the escaping of embedded " as \", which PowerShell requires when called from the outside (by contrast, PowerShell-internally, ` is the escape character).
Enclosing the Get-Content file.txt call - which outputs an array of lines - in a double-quoted string, using subexpression operator $(...), means that the array elements are implicitly joined with a space each.
Note, however, that PowerShell's output-redirection operator, >, creates UTF16-LE ("Unicode") encoded files by default, as does Out-File (at least in Windows PowerShell; the cross-platform PowerShell Core defaults to (BOM-less) UTF-8).
To control the output encoding, use the -Encoding parameter, which you can apply to Out-File or, preferably - knowing that strings are being output - Set-Content.
In Windows PowerShell, note that Set-Content - in contrast with > / Out-File - defaults to the encoding implied by the legacy "ANSI" code page, typically Windows-1252.

Control M specific job status to a excel

I have configured control-m batches to execute complex job/workflows.
Is there a way to get the specific job/workflow status to a excel or csv ?
You could make use of the ctmlog listmsg Control-M utility.
This utility needs to be run on the server that hosts the control-m server.
I run the following powershell daily to extract all failures to a text file.
You could do the same for ended OK jobs - just change the message number from 5134 to 5133.
#Get Today's and Yesterday's date
$date = Get-Date -Format 'yyyyMMdd'
$m1Date = (get-date).AddDays(-1).ToString('yyyyMMdd')
#Generate and execute Control-M Utility command
$cmd = "ctmlog listmsg 5134 $m1Date 0000 $date 0000 C:\Temp\Todays_Failures.txt"
iex $cmd

Is it possible to write one script that runs in bash/shell and PowerShell?

I need to create ONE integrated script that sets some environment variables, downloads a file using wget and runs it.
The challenge is that it needs to be the SAME script that can run on both Windows PowerShell and also bash / shell.
This is the shell script:
#!/bin/bash
# download a script
wget http://www.example.org/my.script -O my.script
# set a couple of environment variables
export script_source=http://www.example.org
export some_value=floob
# now execute the downloaded script
bash ./my.script
This is the same thing in PowerShell:
wget http://www.example.org/my.script -O my.script.ps1
$env:script_source="http://www.example.org"
$env:some_value="floob"
PowerShell -File ./my.script.ps1
So I wonder if somehow these two scripts can be merged and run successfully on either platform?
I've been trying to find a way to put them in the same script and get bash and PowerShell.exe to ignore errors but have had no success doing so.
Any guesses?
It is possible; I don't know how compatible this is, but PowerShell treats strings as text and they end up on screen, Bash treats them as commands and tries to run them, and both support the same function definition syntax. So, put a function name in quotes and only Bash will run it, put "exit" in quotes and only Bash will exit. Then write PowerShell code after.
NB. this works because the syntax in both shells overlaps, and your script is simple - run commands and deal with variables. If you try to use more advanced script (if/then, for, switch, case, etc.) for either language, the other one will probably complain.
Save this as dual.ps1 so PowerShell is happy with it, chmod +x dual.ps1 so Bash will run it
#!/bin/bash
function DoBashThings {
wget http://www.example.org/my.script -O my.script
# set a couple of environment variables
export script_source=http://www.example.org
export some_value=floob
# now execute the downloaded script
bash ./my.script
}
"DoBashThings" # This runs the bash script, in PS it's just a string
"exit" # This quits the bash version, in PS it's just a string
# PowerShell code here
# --------------------
Invoke-WebRequest "http://www.example.org/my.script.ps1" -OutFile my.script.ps1
$env:script_source="http://www.example.org"
$env:some_value="floob"
PowerShell -File ./my.script.ps1
then
./dual.ps1
on either system.
Edit: You can include more complex code by commenting the code blocks with a distinct prefix, then having each language filter out its own code and eval it (usual security caveats apply with eval), e.g. with this approach (incorporating suggestion from Harry Johnston ):
#!/bin/bash
#posh $num = 200
#posh if (150 -lt $num) {
#posh write-host "PowerShell here"
#posh }
#bash thing="xyz"
#bash if [ "$thing" = "xyz" ]
#bash then
#bash echo "Bash here"
#bash fi
function RunBashStuff {
eval "$(grep '^#bash' $0 | sed -e 's/^#bash //')"
}
"RunBashStuff"
"exit"
((Get-Content $MyInvocation.MyCommand.Source) -match '^#posh' -replace '^#posh ') -join "`n" | Invoke-Expression
While the other answer is great (thank you TessellatingHeckler and Harry Johnston)
(and also thank you j-p-hutchins for fixing the error with true)
We can actually do way better
Work with more shells (e.g. work for Ubuntu's dash)
Less likely to break in future situations
No need to waste processing time re-reading/evaling the script
Waste less characters/lines on confusing syntax(we can get away with a mere 41 chars, and mere 3 lines)
Even Keep syntax highlighting functional
Copy Paste Code
Save this as your_thing.ps1 for it to run as powershell on Windows and run as shell on all other operating systems.
#!/usr/bin/env sh
echo --% >/dev/null;: ' | out-null
<#'
#
# sh part
#
echo "hello from bash/dash/zsh"
echo "do whatver you want just dont use #> directly"
echo "e.g. do #""> or something similar"
# end bash part
exit #>
#
# powershell part
#
echo "hello from powershell"
echo "you literally don't have to escape anything here"
How? (its actually simple)
We want to start a multi-line comment in powershell without causing an error in bash/shell.
Powershell has multi-line comments <# but as-is they would cause problems in bash/shell languages. We need to use a string like "<#" for bash, but we need it to NOT be a string in powershell.
Powershell has a stop-parsing arg --% lets write single quote without starting a string, e.g. echo --% ' blah ' will print out ' blah '. This is great because in shell/bash the single quotes do start a string, e.g. echo --% ' blah ' will print out blah
We need a command in order to use powershell's stop-parsing-args, lucky for us both powershell and bash have an echo command
So, in bash we can echo a string with <#, but powershell the same code finishes the echo command then starts a multi-line comment
Finally we add >/dev/null to bash so that it doesn't print out --% every time, and we add | out-null so that powershell doesn't print out >/dev/null;: ' every time.
The syntax highlighting tells the story more visually
Powershell Highlighting
All the green stuff is ignored by powershell (comments)
The gray --% is special
The | out-null is special
The white parts are just string-arguments without quotes
(even the single quote is equivlent to "'")
The <# is the start of a multi-line comment
Bash Highlighting
For bash its totally different.
Lime green + underline are the commands.
The --% isn't special, its just an argument
But the ; is special
The purple is output-redirection
Then : is just the standard "do nothing" shell command
Then the ' starts a string argument that ends on the next line
Caveats?
Almost almost none. Powershell legitimately has no downside. The Bash caveats are easy to fix, and are exceedingly rare
If you need #> in a bash string, you'll need to escape it somehow.
changing "#>" to "#"">"or from ' blah #> ' to ' blah #''> '.
If you have a comment #> and for some reason you CANNOT change that comment (this is what I mean by exceedingly rare), you can actually just use #>, you just have to add re-add those first two lines (eg true --% etc) right after your #> comment
One even more exceedingly rare case is where you are using the # to remove parts of a string (I bet most don't even know this is a bash feature). Example code below
https://man7.org/linux/man-pages/man1/bash.1.html#EXPANSION
var1=">blah"
echo ${var1#>}
# ^ removes the > from var1
To fix this one, well there are alternative ways of removeing chars from the begining of a string, use them instead.
Following up on Jeff Hykin's answer, I have found that the first line, while it is happy in bash, produces this output in PowerShell. Note that it is still fully functional, just noisy.
true : The term 'true' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\jp\scratch\envar.ps1:4 char:1
+ true --% ; : '
+ ~~~~
+ CategoryInfo : ObjectNotFound: (true:String) [], CommandNotFoundException
hello from powershell
I am experimenting with changing the first lines from:
true --% ; : '
<#'
to:
echo --% > /dev/null ; : ' | out-null
<#'
In very limited testing this seems to be working in bash and powershell. For reference, I am "sourcing" the scripts not "calling" them, e.g. . env.ps1 in bash and . ./env.ps1 in powershell.

PowerShell Start-Process loses precision on number passed as a string to a function

I have some code that edits the registry, so it needs to run as admin. To do this, I start up a new PowerShell process from my running PowerShell script, and pass in part of the registry key path, which happens to be a version number, e.g. "12.0". The function in the new PowerShell process receives the string as "12" though, not "12.0", and so I'm getting errors that it can't find the registry key.
I've created a little sample powershell script that reproduces the problem. Here's the snippet:
$ScriptBlock = {
function Test([string]$VisualStudioVersion)
{
$VisualStudioVersion # This always displays 12, instead of 12.0
$Host.UI.RawUI.ReadKey()
}
}
# Run the script block's function.
Start-Process -FilePath PowerShell -ArgumentList "-Command & {$ScriptBlock Test(""12.0"")}"
Here I've hardcoded "12.0", but in practice I want to pass in a variable.
Any ideas on what I'm doing wrong? Thanks in advance.
Ok, after some experimenting the following seems to work correctly:
Start-Process -FilePath PowerShell -ArgumentList "-Command & {$ScriptBlock Test('12.0')}"
and it even works when using a variable:
$version = "12.0"
Start-Process -FilePath PowerShell -ArgumentList "-Command & {$ScriptBlock Test('$version')}"
I'm still not sure why using double quotes causes it to lose the precision and single quotes keeps it, but at least I solved my problem.
Update
So it turns out I'm a dummy and the problem was that I was using C# syntax of Test(""$version"") to call the function, instead of the proper PowerShell syntax Test ""version"". With this change it now works as expected.

Resources