I am having a batch file where it has set variable. I would like replace each variable with value.
For ex: My batch file looks like follows
Set Sourcefolder=..\..\Release
Set Variable2=/y /R
Set DestFolder=..\..\Output
XCopy %SourceFolder%\Comp1 %DestFolder%\Comp1 %Variable2%
...
...
..
Likewise it has many lines. I would like to replace the variables with value. For example
instead of %SourceFolder% it should replace with value ( ....\release)
How to achieve this ? batch file or Powershell script are preferrale
This is how I would do it:
$script:variables=#{}
gc .\script.bat | %{ if($_ -match "^Set (.+?)=(.+)"){ $script:variables["$($matches[1])"] = $matches[2]}}
$evaluator ={
$group1 = $args[0].groups[1]
$script:variables["$group1"]
}
gc .\script.bat | %{ [Regex]::Replace($_,"%(.+?)%",$evaluator,[Text.RegularExpressions.RegexOptions]::none) } |
out-file .\script2.bat
Try this:
$bat = switch -regex -file c:\test.bat
{
'^set Sourcefolder=.+$' {'set Sourcefolder=....\release'}
'^set Variable2=.+$' {'set Sourcefolder=/y /R'}
'^set DestFolder=.+$' {'set Sourcefolder=..\..\Output'}
'%SourceFolder%' {'....\release'}
default {$_}
}
$bat | Out-File c:\test.bat
Related
Good day,
Could someone help me with setting some variables after filtering some records from a batchfile in powershell please?
My Batch file (config.bat) looks like this :
---------
set FILENAME=OIMSX4455
set USR=AJ34
set DOCTYPE=WORD
set YEAR=2019
---------
My Powershell code resembles the below although I'm unsure how to set the variables ie (getting the values "WORD" and "AJ34" into $doc and $user
$vars = "c:\temp\config.bat"
get-content $vars | select-string -pattern ("set DOCTYPE=","set USR=")
foreach-object {
$doc=<should equal WORD>
$user=<should equal AJ34>
}
As my config file changes and has several values, I'm only interested in the values what comes after = sign for set DOCTYPE= & set USR= i.e WORD and AJ34 in this example
On another day the config values will change which is why i need to read them again into my powershell variables. Apologies in advance for any mistakes etc
Thanks
The easiest way I think is by using regex on a line-by-line comparison with switch:
$user, $doc = $null
switch -Regex -File 'c:\temp\config.bat' {
'^set USR\s*=\s*(.*)' { $user = $matches[1] }
'^set DOCTYPE\s*=\s*(.*)' { $doc = $matches[1] }
}
Write-Host "user: $user`tdoc: $doc"
Result:
user: AJ34 doc: WORD
I have an application that generates 100's of text log files which are like
DaemonReruns=2|
Phase=|
Log=false|
DS=LOG_4|
Schema=LOLYY|
DBMS=mssql|
Host=abc.XYz.com|
IDs=xxxxx,xxxx
I need to select Host from these
I tried
GC C:\log_5.txt |
Select-String -Pattern 'Host=\"([^\"]*)\"'
Gives no results, any help ?
There aren't any quotes in your example input. Try this regex:
get-content C:\log_5.txt | foreach {
if ($_ -match 'Host=([^|]+)') {
$Matches.1
}
}
Note: This actually returns the host names, not just the line.
marsze's helpful answer fixes the problem with your regex and uses a ForEach-Object (foreach) call to extract and return matches via the -match operator and the automatic $Matches variable.
Here's a concise (and better-performing) alternative using the switch statement:
PS> switch -Regex -File C:\log_5.txt { 'Host=([^|]+)' { $Matches[1] } }
abc.XYz.com
Note that -File doesn't accept wildcard-based paths, however, so in order to process multiple file, you'll have to loop over them via Get-ChildItem or Convert-Path.
((Get-Content -Path .\log_5.txt) -match 'Host=') -replace 'Host=',''
returns all the lines starting with Host=
Just for fun ... the super-fast solution:
$regex = [Regex]::new('Host=([^|]+)', 'Compiled, IgnoreCase, CultureInvariant')
& {foreach ($line in [IO.File]::ReadLines("C:\log_5.txt")) {
$m = $regex.Match($line)
if ($m.Success) {
$m.Groups[1].Value
}
}}
If your logs are huge, it could be worth the overhead of Add-Type, and the rest would be much faster:
Add-Type '
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace PowerShell
{
public class Tools
{
static Regex regex = new Regex(#"Host=([^|]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
public static IEnumerable<string> GetHosts(string path)
{
foreach(var line in File.ReadLines(path))
{
var matches = regex.Match(line);
if (matches.Success)
{
yield return matches.Groups[1].Value;
}
}
}
}
}'
# call this for each log file (very fast)
[PowerShell.Tools]::GetHosts("C:\log_5.txt")
Other answers have the regex side covered well enough. Whenever I see little logs like this I always think about ConvertFrom-StringData which
converts a string that contains one or more key and value pairs into a hash table.
From: help ConvertFrom-StringData
In its basic form we just do something like this:
$pairs = Get-Content -Raw -File $pathtofile | ConvertFrom-StringData
[pscustomobject]$pairs
Which would give you a PowerShell object that you can interact with easily!
DS : LOG_4|
Schema : LOLYY|
IDs : xxxxx,xxxx
Log : false|
DBMS : mssql|
Host : abc.XYz.com|
Phase : |
DaemonReruns : 2|
Doubtful that you need the trailing pipes. You can remove those with some regex or simpler string methods.
[pscustomobject](Get-Content -File $pathToFile | ForEach-Object{$_.trimend("|")} | Out-string | ConvertFrom-StringData)
[pscustomobject]((Get-Content -Raw -File $pathToFile) -replace "(?m)\|$" | ConvertFrom-StringData)
In any case this gives you more options as to how you need to deal with your data.
I have a requirement, in which I need to do read line by line, and then do string/character replacement in a datafile having data in windows latin 1.
I've written this powershell (my first one) initially using out-file -encoding option. However the output file thus created was doing some character translation. Then I searched and came across WriteAllLines, but I'm unable to use it in my code.
$encoding =[Text.Encoding]::GetEncoding('iso-8859-1')
$pdsname="ABCD.XYZ.PQRST"
$datafile="ABCD.SCHEMA.TABLE.DAT"
Get-Content ABCD.SCHEMA.TABLE.DAT | ForEach-Object {
$matches = [regex]::Match($_,'ABCD')
$string_to_be_replaced=$_.substring($matches.Index,$pdsname.Length+10)
$string_to_be_replaced="`"$string_to_be_replaced`""
$member = [regex]::match($_,"`"$pdsname\(([^\)]+)\)`"").Groups[1].Value
$_ -replace $([regex]::Escape($string_to_be_replaced)),$member
} | [System.IO.File]::WriteAllLines("C:\Users\USer01", "ABCD.SCHEMA.TABLE.NEW.DAT", $encoding)
With the help of an answer from #Gzeh Niert, I updated my above script. However, when I execute the script the output file being generated by the script has just the last record, as it was unable to append, and it did an overwrite, I tried using System.IO.File]::AppendAllText, but this strangely creates a larger file, and has only the last record. In short its likely that empty lines are being written.
param(
[String]$datafile
)
$pdsname="ABCD.XYZ.PQRST"
$encoding =[Text.Encoding]::GetEncoding('iso-8859-1')
$datafile = "ABCD.SCHEMA.TABLE.DAT"
$datafile2="ABCD.SCHEMA.TABLE.NEW.DAT"
Get-Content $datafile | ForEach-Object {
$matches = [regex]::Match($_,'ABCD')
if($matches.Success) {
$string_to_be_replaced=$_.substring($matches.Index,$pdsname.Length+10)
$string_to_be_replaced="`"$string_to_be_replaced`""
$member = [regex]::match($_,"`"$pdsname\(([^\)]+)\)`"").Groups[1].Value
$replacedContent = $_ -replace $([regex]::Escape($string_to_be_replaced)),$member
[System.IO.File]::AppendAllText($datafile2, $replacedContent, $encoding)
}
else {
[System.IO.File]::AppendAllText($datafile2, $_, $encoding)
}
#[System.IO.File]::WriteAllLines($datafile2, $replacedContent, $encoding)
}
Please help me figure out where I am going wrong.
System.IO.File.WriteAllLines is getting either an array of strings or an IEnumerable of strings as second parameter and cannot be piped to a command because it is not a CmdLet handling pipeline input but a .NET Framework method.
You should try storing your replaced content into a string[]to use it as parameter when saving the file.
param(
[String]$file
)
$encoding =[Text.Encoding]::GetEncoding('iso-8859-1')
$replacedContent = [string[]]#(Get-Content $file | ForEach-Object {
# Do stuff
})
[System.IO.File]::WriteAllLines($file, $replacedContent, $encoding)
I've notice I'm not manage to format string in-place when calling a function in PowerShell. I need to format it because I want to output a number as hex and not decimal.
This didn't work like so:
WriteLog "Running Step | Retry=$RetryCount | EnabledDevices={0:X}" -f $EnabledDevices
It only works if I store the result in variable and then use it like so:
$Log = "Running Step | Retry=$RetryCount | EnabledDevices={0:X}" -f $EnabledDevices
WriteLog $Log
If there a way to do it in one statement instead of two?
Just put it in parentheses:
WriteLog ("Running Step | Retry=$RetryCount | EnabledDevices={0:X}" -f $EnabledDevices)
I was wondering if this was possible. I am trying to make a script we will refer to as a master script. This script queries a DB to get a list of servers we will call $svrs. Simple stuff.
The thing I don't know how to do or if it is possible is to run a series of subscripts from the master script using the $srvrs.Name variable as a parameter on those scripts.
$svrs = "get list sql stuff"
$scrpath = 'D:\test'
$scripts = Get-ChildItem $scrpath
$scripts.Name | ForEach-Object {
Invoke-Expression $_ {I have no idea how to get server name variable here}
}
Based on the comments, you do need a nested loop which won't be too complicated.
$Scripts | Select-object Name | % {$curScript = $_
$Servers | % {.\$_ $CurScript}
}
I ended up resolving this myself with #JNK 's assistance...
Here is how I got the result I needed.
$allServers | ForEach-Object {
$currentServer = $_
$scripts.Name | ForEach-Object {
Invoke-Expression ".\$_ $currentServer"
}
}