Move File only if the file does not contain only a zero - powershell

I have the below powershell command which, if the file only contains a zero and no spaces after, works ok.
However, my file has multiple spaces after the 0, followed by a newline.
How can I only move the file if the only character in the file is a 0, no matter on whether there are additional spaces and lines?
$file = Get-Content "Test\A28AP.txt" -raw
if ($file -notmatch '^0$') {
Move-Item "D:\Test\A28AP.txt" -Destination "D:\Test\History"
}

As Jeroen Mostert suggests, using .Trim() on the file's content before comparing to string '0' is the simplest solution in your case:
$filePath = 'Test\A28AP.txt'
if ('0' -ne (Get-Content $filePath -Raw).Trim()) {
Move-Item $filePath -Destination D:\Test\History -WhatIf
}
.Trim() (System.String.Trim) removes all leading and trailing whitespace, and is complemented by .TrimStart() and .TrimEnd() variants. All these methods optionally allow you to control the set of characters to be trimmed.
To look on the first line only, irrespective of whether there are additional, non-empty ones, replace -Raw with -First 1
For the sake of completeness, here's the equivalent (first-line-only) regex solution - even though it is overkill in your case:
$filePath = 'Test\A28AP.txt'
if ((Get-Content $filePath -First 1) -notmatch '^0 *') {
Move-Item $filePath -Destination D:\Test\History -WhatIf
}
' *' matches zero or more spaces after the 0 at the start of the string (^).
With a given multi-line input string (which is what -Raw returns for a multi-line file), limiting matching to the first line requires more effort:
By default, ^ and $ in .NET regexes match only the very beginning and end of the entire input; inline option (?m) can be used to make them match on each line, in which case \A and \Z / \z must be used to match the very beginning and end of the string - see the .NET regex quick reference.
Therefore, use regex '(?m)\A0 *$' to look for a 0 with optional trailing spaces only on the first line of the multi-line string:
$filePath = 'Test\A28AP.txt'
if ((Get-Content $filePath -Raw) -notmatch '(?m)\A0 *$') {
Move-Item $filePath -Destination D:\Test\History -WhatIf
}

Related

how to replace text ($) with quotation marks in a text file by using powershell

I am trying to replace the text ($) with quotation marks in a text file by using PowerShell
Here is the code I am trying, But not giving expected results:
$FilePath = "C:\PT\Test\"
Get-ChildItem $FilePath -Filter *.TXT | ForEach-Object {
(Get-Content $_.FullName) | Foreach-Object {
$_ -replace ',"$"', ',"$",'
} | Set-Content $_.FullName
}
As pointed out by Abraham in his comment, the $ is a reserved character in regex known as Anchor, if you want to match a literal $ using -replace you would need to escape it: \$:
Get-ChildItem $FilePath -Filter *.TXT | ForEach-Object {
(Get-Content $_.FullName -Raw) -replace ',"\$"',',"$",' |
Set-Content $_.FullName
}
The other alternative is to use the .Replace(..) string method which will match literal characters.
Get-ChildItem $FilePath -Filter *.TXT | ForEach-Object {
(Get-Content $_.FullName -Raw).Replace(',"$"',',"$",') |
Set-Content $_.FullName
}
By looking at your code seems like you want to replace ,"$" with ,"$",, if this was not the case let me know.
The -replace operator:
uses a regex as the search-pattern operand, and therefore requires a verbatim $ to be escaped as \$ in order to be interpreted as such (an unescaped $ is a regex metacharacter representing the end of the input string)
also uses $ as a metacharacter in the substitution operand, namely to refer to the text that the regex captured ($&) or parts thereof (e.g. $1 to refer to what the first capture group ((...)) captured). In this case, escape it as $$.
Note: Situationally, even an unescaped $ may work, namely if, in combination with the subsequent characters, if any, it cannot be interpreted as a placeholder such as $& or $1. However, in the interest of robustness it is better to escape.
Therefore, use:
PS> ',"$"20' -replace ',"\$"', ',"$$",'
,"$",20
Taking a step back:
As Abraham Zinala suggests, for verbatim string replacements, the .Replace() string method is sufficient, whose use doesn't necessitate escaping, and which is faster to boot:
PS> ',"$"20'.Replace(',"$"', ',"$",')
,"$",20
Caveat: Unlike -replace, .Replace() is case-sensitive - invariably in Windows PowerShell and by default in PowerShell (Core) 7+.
See this answer for a detailed juxtaposition of -replace and .Replace(), including how to perform escaping for -replace programmatically.
To put it all together, along with an optimization that reads each file in full, with Get-Content -Raw, for much faster processing (to avoid appending a trailing newline, -NoNewLine is used with Set-Content):
$FilePath = "C:\PT\Test"
Get-ChildItem $FilePath -Filter *.TXT | ForEach-Object {
(Get-Content -Raw $_.FullName).Replace(',"$"', ',"$",') |
Set-Content -NoNewLine $_.FullName
}

Select-String successfully finds a string, replacing that found string with something else fails

uniquefile1.txt is a file that has no carriage returns (if it matters) and is very long. I am trying to match a variety of patterns.
The text file is not open when the program runs.
I visually have checked that the file has my pattern exactly as
written, when using Select-String it confirms that the pattern
exists.
When I go to replace, if I do Out-Host and search my
output in my ide it does not show that it has changed. It is not
replacing this string, and I do not know what I am doing wrong.
There are no errors of any kind when running my code.
I have tried:
$file = Get-Content C:\Uniqueline1
$file.Replace($variableforpattern1, $varForReplacement) | Set-Content Uniquefile1.txt
As well as the above except with the actual strings in place of the variables.
$file = Get-Content C:\uniquefile1.txt -Raw
$SEL = Select-String -Path "C:\uniquefile1.txt" -Pattern ">>>11^A"
if ($SEL = $true)
{
write "true"
}
else
{
write "not true"
}
$file -replace ">>>11^A", ">>>0111^A" | Set-Content "C:\uniquefile1.txt"
AdminOfThings was correct, please see their comment. This character ^ has special meaning in Regex and must be escaped.
-replace uses regex for the search pattern and therefore must have regex-special characters escaped if they are to be matched literally. The \ is used for escaping.
$file = Get-Content C:\uniquefile1.txt -Raw
$file -replace '>>>11\^A', '>>>0111^A' | Set-Content C:\uniquefile1.txt
Select-String without the -SimpleMatch switch uses regex as well. I don't know how that ever matched for you if the -replace operation failed.

Need to batch add characters to filenames using Powershell

I have a series of files all named something like:
PRT14_WD_14220000_1.jpg
I need to add two zeroes after the last underscore and before the number so it looks like PRT14_WD_14220000_001.jpg
I've tried"
(dir) | rename-Item -new { $_.name -replace '*_*_*_','*_*_*_00' }
Appreciate any help.
The closest thing to what you attempted would be this. In regex, the wildcard is .*. And the parentheses do grouping to refer to later with the dollar sign numbers.
dir *.jpg | rename-Item -new { $_.name -replace '(.*)_(.*)_(.*)_','$1_$2_$3_00' } -whatif
What if: Performing the operation "Rename File" on target "Item: C:\users\admin\foo\PRT14_WD_14220000_1.jpg Destination: C:\users\admin\foo\PRT14_WD_14220000_001.jpg".
Ok, here's my take when you want the number with max two zeroes padding. $num has to be an integer for the .tostring() method I want.
dir *.jpg | rename-item -newname { $a,$b,$c,$num = $_.basename -split '_'
$num = [int]$num
$a + '_' + $b + "_" + $c + '_' + $num.tostring('000') + '.jpg'
} -whatif
the following presumes your last part of the .BaseName will always need two zeros added to it. what it does ...
fakes getting the fileinfo object that you get from Get-Item/Get-ChildItem
replace that with the appropriate cmdlet. [grin]
splits the .BaseName into parts using the _ as the split target
adds two zeros to the final part from the above split
merges the parts into a $NewBaseName
gets the .FullName and replaces the original BaseName with the $newBaseName
displays that new file name
you will still need to do your rename, but that is pretty direct. [grin]
here's the code ...
# fake getting a file info object
# in real life, use Get-Item or Get-ChildItem
$FileInfo = [System.IO.FileInfo]'PRT14_WD_14220000_1.jpg'
$BNParts = $FileInfo.BaseName.Split('_')
$BNParts[-1] = '00{0}' -f $BNParts[-1]
$NewBasename = $BNParts -join '_'
$NewFileName = $FileInfo.FullName.Replace($FileInfo.BaseName, $NewBaseName)
$NewFileName
output = D:\Data\Scripts\PRT14_WD_14220000_001.jpg
The -replace operator operates on regexes (regular expressions), not wildcard expressons such as * (by itself), which is what you're trying to use.
A conceptually more direct approach is to focus the replacement on the end of the string:
Get-ChildItem | # `dir` is a built-in alias for Get-ChildItem`
Rename-Item -NewName { $_.Name -replace '(?<=_)[^_]+(?=\.)', '00$&' } -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
(?<=_)[^_]+(?=\.) matches a nonempty run (+) of non-_ chars. ([^_]) preceded by _ ((?<=_) and followed by a literal . ((?=\.)), excluding both the preceding _ and the following . from what is captured by the match ((?<=...) and (?=...) are non-capturing look-around assertions).
In short: This matches and captures the characters after the last _ and before the start of the filename extension.
00$& replaces what was matched with 00, followed by what the match captured ($&).
In a follow-up comment you mention wanting to not just blindly insert 00, but to 0-left-pad the number after the last _ to 3 digits, whatever the number may be.
In PowerShell [Core] 6.1+, this can be achieved as follows:
Get-ChildItem |
Rename-Item -NewName {
$_.Name -replace '(?<=_)[^_]+(?=\.)', { $_.Value.PadLeft(3, '0') }
} -WhatIf
The script block ({ ... }) as the replacement operand receives each match as a Match instance stored in automatic variable $_, whose .Value property contains the captured text.
Calling .PadLeft(3, '0') on that captured text 0-left-pads it to 3 digits and outputs the result, which replaces the regex match at hand.
A quick demonstration:
PS> 'PRT14_WD_14220000_1.jpg' -replace '(?<=_)[^_]+(?=\.)', { $_.Value.PadLeft(3, '0') }
PRT14_WD_14220000_001.jpg # Note how '_1.jpg' was replaced with '_001.jpg'
In earlier PowerShell versions, you must make direct use of the .NET [regex] type's .Replace() method, which fundamentally works the same:
Get-ChildItem |
Rename-Item -NewName {
[regex]::Replace($_.Name, '(?<=_)[^_]+(?=\.)', { param($m) $m.Value.PadLeft(3, '0') })
} -WhatIf

Replace a non-unique line of text under a unique line of text in a text file using powershell

I have the following txt file.
[AppRemover]
Enable=0
[CleanWipe]
Enable=0
[RerunSetup]
Enable=0
How do I change the Enable=0 to Enable=1 under [CleanWipe] only?
Below is how I plan on using the code with my file.
$Path = C:\temp\file.txt
$File = Get-Content -Path $Path
# Code to update file
$File | Out-File $Path
You can use -replace to update the value if it is 0.
$Path = C:\temp\file.txt
(Get-Content $Path -Raw) -replace "(?<text>\[CleanWipe\]\r?\nEnable=)0",'${text}1' |
Set-Content $Path
Using a module that parses INI files will be the best solution though. I'd recommend trying PsIni.
Explanation:
The -Raw switch reads the file contents as a single string. This makes it easier to work with newline characters.
-replace performs a regex match and then replace. Below is the regex match breakdown.
(?<text>) is a named capture group. Anything matched within that capture group can be recalled in the replace string as '${text}'.
\[CleanWipe\] is a literal match of [CleanWipe] while escaping the [] characters with \.
\r? is optional carriage return
\n is the newline character
Enable= is a literal match
0 is a literal match
The replace string is the capture group contents and 1 when a match exists. Technically, a capture group is not needed if you want to use a positive lookbehind instead. The positive lookbehind assertion is (?<=). That solution would look like the following:
$Path = C:\temp\file.txt
(Get-Content $Path -Raw) -replace "(?<=\[CleanWipe\]\r?\nEnable=)0",'1' |
Set-Content $Path
The problem with the -replace solutions as they written is they will update the file regardless of a change actually being made to the contents. You would need to add an extra comparison to prevent that. Other issues could be extra white space on any of these lines. You can account for that by adding \s* where you think those possibilities may exist.
Alternative With More Steps:
$file = Get-Content $Path
$TargetIndex = $file.IndexOf('[CleanWipe]') + 1
if ($file[$TargetIndex] -match 'Enable=0') {
$file[$TargetIndex] = 'Enable=1'
$file | Set-Content $Path
}
This solution will only update the file if it meets the match condition. It uses the array method IndexOf() to determine where [CleanWipe] is. Then assumes the line you want to change is in the next index.
IndexOf() is not the only way to find an index. The method requires that your line match the string exactly. You can use Select-String (case-insensitive by default) to return a line number. Since it will be a line number and not an index (indexes start at 0 while line numbers start at 1), it will invariably be the index number you want.
$file = Get-Content $Path
$TargetIndex = ($file | Select-String -Pattern '[CleanWipe]' -SimpleMatch).LineNumber
if ($file[$TargetIndex] -match 'Enable=0') {
$file[$TargetIndex] = 'Enable=1'
$file | Set-Content $Path
}

Find and Replace character only in certain column positions in each line

I'm trying to write a script to find all the periods in the first 11 characters or last 147 characters of each line (lines are fixed width of 193, so I'm attempting to ignore characters 12 through 45).
First I want a script that will just find all the periods from the first or last part of each line, but then if I find them I would like to replace all periods with 0's, but ignore periods on the 12th through 45th line and leaving those in place. It would scan all the *.dat files in the directory and create period free copies in a subfolder. So far I have:
$data = get-content "*.dat"
foreach($line in $data)
{
$line.substring(0,12)
$line.substring(46,147)
}
Then I run this with > Output.txt then do a select-string Output.txt -pattern ".". As you can see I'm a long ways from my goal as presently my program is mashing all the files together, and I haven't figured out how to do any replacement yet.
Get-Item *.dat |
ForEach-Object {
$file = $_
$_ |
Get-Content |
ForEach-Object {
$beginning = $_.Substring(0,12) -replace '\.','0'
$middle = $_.Substring(12,44)
$end = $_.Substring(45,147) -replace '\.','0'
'{0}{1}{2}' -f $beginning,$middle,$end
} |
Set-Content -Path (Join-Path $OutputDir $file.Name)
}
You can use the powershell -replace operator to replace the "." with "0". Then use substring as you do to build up the three portions of the string you're interested in to get the updated string. This will output an updated line for each line of your input.
$data = get-content "*.dat"
foreach($line in $data)
{
($line.SubString(0,12) -replace "\.","0") + $line.SubString(13,34) + ($line.substring(46,147) -replace "\.","0")
}
Note that the -replace operator performs a regular expression match and the "." is a special regular expression character so you need to escape it with a "\".