I have a script I am running in Powershell, and I want to be able to put a line in my resulting text file output between the ccript name and the script content itself.
Currently, from the below, the line $str_msg = $file,[System.IO.File]::ReadAllText($file.FullName) is what I need, but I need a line to separate $file and the result of the next expression. How can I do this?
foreach ($file in [System.IO.Directory]::GetFiles($sqldir,"*.sql",
[System.IO.SearchOption]::AllDirectories))
{
$file = [System.IO.FileInfo]::new($file);
$Log.SetLogDir("");
$str_msg = $file,[System.IO.File]::ReadAllText($file.FullName);
$Log.AddMsg($str_msg);
Write-Output $str_msg;
# ...
}
$str_msg = $file,[System.IO.File]::ReadAllText($file.FullName) doesn't create a string, it creates a 2-element array ([object[]]), composed of the $file [System.IO.FileInfo] instance, and the string with the contents of that file.
Presumably, the .AddMsg() method expects a single string, so PowerShell stringifies the array in order to convert it to a single string; PowerShell stringifies an array by concatenating the elements with a single space as the separator by default; e.g.:
[string] (1, 2) yields '1 2'.
Therefore, it's best to compose $str_msg as a string to begin with, with an explicit newline as the separator, e.g.:
$strMsg = "$file`r`n$([System.IO.File]::ReadAllText($file.FullName))"
Note the use of escape sequence "`r`n" to produce a CRLF, the Windows-specific newline sequence; on Unix-like platforms, you'd use just "`n" (LF).
.NET offers a cross-platform abstraction, [Environment]::NewLine, which returns the platform-appropriate newline sequence (which you could alternatively embed as $([Environment]::NewLine) inside "...").
An alternative to string interpolation is to use -f, the string-formatting operator, which is based on the .NET String.Format() method:
$strMsg = '{0}{1}{2}' -f $file,
[Environment]::NewLine,
[System.IO.File]::ReadAllText($file.FullName)
Backtick-r+backtick-n will do a carriage return with a new line in PS. You could do a Get-Content of your $file variable as a new array variable, and insert the carriage return at a particular index:
Example file: test123.txt
If the file contents were this:
line1
line2
line3
Store the contents in an array variable so you have indices
[Array]$fileContent = Get-Content C:\path\to\test123.txt
To add a carriage return between line2 and line3:
$fileContent2 = $fileContent[0..1] + "`r`n" + $fileContent[2]
Then output a new file:
$fileContent2 | Out-File -FilePath C:\path\to\newfile.txt
You need to use the carriage return powershell special character, which is "`r".
Use it like this to add a carriage return in your line :
$str_msg = $file,"`r",[System.IO.File]::ReadAllText($file.FullName);
Check this documentation to have more details on Poewershell special characters.
Related
I have a file prototype as follows:
// <some stuff>
#define KEYWORD release01-11
// <more stuff>
How can I delete the last two characters in the same line as KEYWORD and replace them with two different characters (12 in this case), in order to end up with:
// <some stuff>
#define KEYWORD release01-12
// <more stuff>
I'm trying to use Clear-Content and Add-Content but I cannot get it to do what I need. The rest of the file needs to remain unchanged after these symbols have been replaced. Is there a better alternative?
Use the -replace regex operator to identify the relevant statements and replace/remove the trailing numbers:
# read file into a variable
$code = Get-Content myfile.c
# replace the trailing -XX with 12 in all lines starting with `#define KEYWORD`, with
$code = $code -replace '(?<=#define KEYWORD .+-)\d{2}\s*$','12'
# write the contents back to the file
$code |Set-Content myfile.c
The regex construct (?<=...) is a positive lookbehind - it ensures that the following expression will only match at a position where text right behind it is #define KEYWORD followed by some characters and a -.
If you want to always increment the current value (as opposed to just replacing it with 12), we'll need some way to inspect and evaluate the current value before doing the substitution.
The [Regex]::Replace() method allows for just that:
# read file into a variable
$code = Get-Content myfile.c
$code = $code |ForEach-Object {
# Same as before, but now we can hook into the regex engine's substitution routine
[regex]::Replace($_, '(?<=#define KEYWORD .+-)\d{2}\s*$',{
param($m)
# extract the trailing numbers, convert to a numerical type
$value = $m.Value -as [int]
# increment the value
$value++
# return the new value
return $value
})
}
# write the contents back to the file
$code |Set-Content myfile.c
In PowerShell 6.1 and up, the -replace operator natively supports scriptblock substitutions:
$code = $code |ForEach-Object {
# Same as before, but now we can hook into the regex engine's substitution routine
$_ -replace '(?<=#define KEYWORD .+-)\d{2}\s*$',{
# extract the trailing numbers, convert to a numerical type
$value = $_.Value -as [int]
# increment the value
$value++
# return the new value
return $value
}
}
I have a file
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*LM*KY
EF*12*Code1*TX*1234*RJ
I need to replace the 5th element in the CD segment alone from LM to ET in each of the file in the folder. Element delimiter is * as mentioned in the above sample file content. I am new to PowerShell and tried a code as below but unfortunately it is not giving desired results. Can any of you please provide some help?
foreach($xfile in $inputfolder)
{
If ($_ match "^CD\*")
{
[System.IO.File]::ReadAllText($xfile).replace(($_.split("*")[5],"ET") | Set-Content $xfile
}
[System.IO.File]::WriteAllText($xfile),((Get-Content $xfile -join("~")))
}
here's a slightly different way to get there ... [grin] what it does ...
fakes reading in a test file
when ready to do this for real, remove the entire #region/#endregion block and use Get-Content.
sets the constants
iterates thru the imported text file lines
checks for a line that starts with the target pattern
if found ...
== escapes the old value with [regex]::Escape() to deal with the asterisks
== replaces the escaped old value with the new value
== outputs the new version of that line
if NOT found, outputs the line as-is
stores all the lines into the $OutStuff var
displays that on screen
the code ...
#region >>> fake reading in a plain text file
# in real life, use Get-Content
$InStuff = #'
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*LM*KY
EF*12*Code1*TX*1234*RJ
'# -split [System.Environment]::NewLine
#endregion >>> fake reading in a plain text file
$TargetLineStart = 'CD*'
$OldValue = '*LM*'
$NewValue = '*ET*'
$OutStuff = foreach ($IS_Item in $InStuff)
{
if ($IS_Item.StartsWith($TargetLineStart))
{
$IS_Item -replace [regex]::Escape($OldValue), $NewValue
}
else
{
$IS_Item
}
}
$OutStuff
output ...
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*ET*KY
EF*12*Code1*TX*1234*RJ
i will leave saving that to a new file [or overwriting the old one] to the user. [grin]
You could capture all that comes before the match in group 1, and match LM.
In the replacement use $1ET
^(CD*(?:[^*\r\n]+\*){5})LM\b
Regex demo
If you don't want to match LM literally, you could also match any other char than * or a newline.
^(CD*(?:[^*\r\n]+\*){5})[^*\r\n]+\b
Replace example
$allText = Get-Content -Raw file.txt
$allText -replace '(?m)^(CD*(?:[^*\r\n]+\*){5})LM\b','$1ET'
Output
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*ET*KY
EF*12*Code1*TX*1234*RJ
This is an automation of a command to SQLPlus 12c on Linux from Windows 18_3 version on PowerShell 5.1 with Microsoft modules loaded.
I need to clean out the whitespace of the string to input wildcard data on an automation Select script (the final script will find a missing TIFF image and reinsert it).
I am UNABLE to remove the white space before the tee.
The latest attempts are in the post but I have tried Trim, Split, Replace, Remove, Substring, >>, Write-Host -NoNewline,... I am SO close.
When I Write-Host -NoNewline I succeeded in removing the CRLF but not so as I can Tee, Write-Out, or Out-File the content that way.
#Add-Type -AssemblyName System.Data.OracleClient
$filefolder = "C:\EMSCadre\iGateway\clint\Input_Images\"
$Files = Get-ChildItem $FileFolder -Name -File
$longname = $Files.Get(2)
$shortname = $longname.Replace("_tiff","").Replace("cns","").Substring(9).Split('".tif"')
echo "select LD_CASE_NUMBER FROM LOG_data where ld_message_3 like %$shortname%" |
tee -Verbose c:\scripts\input\lockedout_test.sql
type c:\scripts\input\lockedout_test.sql
#Failed attempts
#echo "select LD_CASE_NUMBER FROM LOG_data where ld_message_3 like %($shortname1.TrimEnd('_',"")%" |
# tee -Verbose c:\scripts\input\lockedout_test.sql
Latest Results showing Whitespaces before last %:
select LD_CASE_NUMBER FROM LOG_data where ld_message_3 like %100838953_180130001 %
select LD_CASE_NUMBER FROM LOG_data where ld_message_3 like %100838953_180130001 %
Details to help troubleshoot:
PS C:\scripts> $Files
2823910000.tif
2823910002.tif
cns20180827_100838953_180130001_tiff.tif
exposureworks-dynamic-range-test-f16-graded-TIFF-RGB-parade.jpg
PS C:\scripts> $shortname
100838953_180130001
Looks to me like the last step (Split()) of the statement
$longname.Replace("_tiff","").Replace("cns","").Substring(9).Split('".tif"')
is supposed to remove the extension from the file name. That is not how Split() works. The method interprets the string ".tif" as a character array and splits the given string at any of those characters (", ., f, i, t). Splitting the string 100838953_180130001.tif that way gives you an array with 5 elements, the last 4 of which are empty strings:
[ '100838953_180130001', '', '', '', '' ]
Putting the variable with that array into a string mangles the array into a string by concatenating its elements using the output field separator ($OFS), which by default is a single space, thus producing the trailing spaces you observed.
To remove the prefix cns..._ and the substring _tiff as well as the extension .tif from the file name use the following:
$shortname = $longname -replace '^cns\d*_|_tiff|\.tif$'
That regular expression replacement will remove the substring "cns" followed by any number of digits and an underscore from the beginning of a string (^), the substring "_tiff" from anywhere in a string, and the substring ".tif" from the end of a string ($).
My powershell command below
$BUILD_SOURCEVERSIONMESSAGE= (Get-Item Env:\BUILD_SOURCEVERSIONMESSAGE)
returns output in this format
2018-10-26T01:08:44.7409834Z BUILD_SOURCEVERSIONMESSAGE Merge 569594f057e2c4bd0320159855e81e14216ca66f into 41107d0f0db5ef2986831db2182280e0c...
I am trying to parse the string 569594f057e2c4bd0320159855e81e14216ca66f from the output above.
I tried converting the output to a string, splitting it on whitespace, and accessing the second element of the array as follows. However, I get empty string. How can I access the required string?
echo $BUILD_SOURCEVERSIONMESSAGE
$out = $BUILD_SOURCEVERSIONMESSAGE | Out-String
$out1 = $out.split()
echo $out1[1]
The concise equivalent of command Get-Item Env:\BUILD_SOURCEVERSIONMESSAGE - i.e., retrieving the value of environment variable BUILD_SOURCEVERSIONMESSAGE - is the expression $env:BUILD_SOURCEVERSIONMESSAGE.
Using the unary form of Powershell's -split operator, which splits the input by any nonempty run of whitespace (while stripping leading and trailing whitespace), you can get the desired output as follows:
PS> (-split $env:BUILD_SOURCEVERSIONMESSAGE)[3]
569594f057e2c4bd0320159855e81e14216ca66f
Index 3 extracts the 4th token resulting from the tokenization via -split.
If you want to use string interpolation with the result:
$prefix = 'before<'; $postfix = '>after'
$val = (-split $env:BUILD_SOURCEVERSIONMESSAGE)[3]
# Output a synthesized string that applies a pre- and postfix, using
# {...} to enclose variable names to avoid ambiguity.
"${prefix}${val}${postfix}"
The above yields:
before<569594f057e2c4bd0320159855e81e14216ca66f>after
Say I have a text file 123.txt
one,two,three
four,five,six
My goal is to capitalize the first character of each line by using Get-Culture. This is my attempt:
$str = gc C:\Users\Administrator\Desktop\123.txt
#Split each line into an array
$array = $str.split("`n")
for($i=0; $i -lt $array.Count; $i++) {
#Returns O and F:
$text = (Get-Culture).TextInfo.ToTitleCase($array[$i].Substring(0,1))
#Supposed to replace the first letter of each array with $text
$array[$i].Replace($array[$i].Substring(0,1), $text) >> .\Desktop\finish.txt
}
Result:
One,twO,three
Four,Five,six
I understand that .Replace() is replaces every occurrence of the current array, which is why I made sure that it's replacing ONLY the first character of the array with $array[$i].Substring(0,1), but this doesn't work.
Try the following:
Get-Content C:\Users\Administrator\Desktop\123.txt | ForEach-Object {
if ($_) {
$_.Substring(0, 1).ToUpper() + $_.Substring(1)
} else {
$_
}
} > .\Desktop\finish.txt
Get-Content reads the input file line by line and sends each line - stripped of its line terminator - through the pipeline.
ForEach-Object processes each line in the associated script block, in which $_ represents the line at hand:
if ($_) tests if the line is nonempty, i.e. if there's at least 1 character; if not, the else block simply passes the empty line through.
$_.Substring(0, 1).ToUpper() converts the line's 1st character to uppercase, implicitly using the current culture (with a single character, this is equivalent to applying Get-Culture).TextInfo.ToTitleCase()).
+ $_.Substring(1) appends the rest of the line.
Only > rater than >> is needed to write to the output file, because the entire pipeline's output is written at once.
The reason this is not working is because you are replacing the character...
$array[$i].Substring(0,1)
... but you are using the Replace method on the entire array element
$array[$i].Replace(...
Here the array element is a string, equal to a line of the input. So it will replace every occurrence of that character.
Get-Content (unless you use the -Raw parameter) by default returns the text as an array of strings. So you should be able to use this regex replace (I have used ToString().ToUpper() - nothing wrong with the Get-Culture method)
$str = gc C:\Users\Administrator\Desktop\123.txt
foreach($line in $str){
$line -replace '^\w', $line[0].ToString().ToUpper() >> .\Desktop\finish.txt
}
Regex explanation:
^ is an anchor. It specifies "the beginning of the string"
\w matches a word character - usually a-z, A-Z, 0-9
See mklement0's comments here for the more focused ^\p{Ll} and here for further explanation