String variable acting strange when writing to text file - string

Below is the code I am having trouble with. It pulls from a config.txt file and sets information to childofRoot.properties file. for some reason the string when I set it is coming with extra added on.
$date = (Get-Date -Format "MM-dd-yyyy'_'HH-mm")
$backup = 'backuplocation'
$temp = 'replace'
$p = ($PSScriptRoot + 'childofRoot.properties')
$cuidConf = Get-Content -Path ($PSScriptRoot + 'config.txt') | Select-String "cuids_rootfolder=" | Out-String
$cuidConf = $cuidConf.replace("cuids_rootfolder=", "").replace("`n", "")
$cuids = $cuidConf.Split(";")
$backuppath = ((Get-Content -Path ($PSScriptRoot + 'config.txt'))) | Select-String "backuplocation=" | Out-String
$backuppath = $backuppath.replace("backuplocation=", "").replace("`n", "")
$backuppath = $backuppath + $date + "\"
$foldername, $cuid = ($cuids[0].Split(","))
$backup1 = ($backuppath + "Folder a.lcmbiar")
((Get-content -Path $p).replace($temp, $cuid)) | Set-Content -Path $p
((Get-content -Path $p).replace($backup, $backup1)) | Set-Content -Path $p
Contents of config.txt:
backuplocation=C:\backuplcm\backupfiles\
Content of childofRoot.properties:
exportLocation=backuplocation
Expected output written to childofRoot.properties:
exportLocation=C:\backuplcm\backupfiles\08-18-2020_17-59\Folder\a.lcmbiar
Output I'm seeing written to childofRoot.properties:
exportLocation=
C:\backuplcm\backupfiles\
08-18-2020_18-02\Folder a.lcmbiar
Yes I already tried removing new line characters with .replace("`n","")
I had others look at this script too and the variable looks fine just when i set it it comes out different in the text file.
I was able to replicate this on Powershell 4 and Powershell 5 on different machines. If someone would try this on their machine and see if they can fix the problem.

The extra line breaks are from doing | Select-String "backuplocation=" | Out-String, and the reason .Replace("`n", "") doesn't solve the problem is that Windows uses the sequence "`r`n" for line breaks.
I suggest simplifying your script with Where-Object instead of Select-String | Out-String:
$backuppath = Get-Content -Path (Join-Path $PSScriptRoot config.txt) | Where-Object {$_ -match "backuplocation="}
$backuppath = $backuppath -replace 'backuplocation='

Related

Powershell Separate csv data into separate rows

I have the following script and it gets me the info I need.
$Monitors = Get-WmiObject WmiMonitorID -Namespace root\wmi
$LogFile = "d:\monitors.csv"
"Manufacturer,Name,Serial" | Out-File $LogFile
ForEach ($Monitor in $Monitors)
{
$Manufacturer = ($Monitor.ManufacturerName|where {$_ -ne 0}|ForEach{[char]$_}) -join ""
$Name = ($Monitor.UserFriendlyName |where {$_ -ne 0}| ForEach{[char]$_}) -join ""
$Serial = ($Monitor.SerialNumberID |where {$_ -ne 0}| ForEach{[char]$_}) -join ""
"$Manufacturer,$Name,$Serial" | Out-File $LogFile -append
}
My problem is the data is exported to the excel spreadsheet like this..
Manufacturer,Name,Serial.
ACI,VE248,K8LMQS048382
HWP,HP P232,6CM8122DXL
HWP,HP P232,6CM7241DRB
I need it to be:
Manufacturer Name Serial
in the spreadsheet, and when I do the next pc, it adds to the next line and so on.
I have looked online and the examples just don't match.
Does anyone have any advice?
As others already commented, use Export-Csv to write out the wanted data in csv format, rather than constructing it manually.
That means your ForEach-Object loop should emit objects instead of lines of concatenated strings.
Also, nowadays, I would use the newer Get-CimInstance instead of Get-WmiObject (What's the difference)
Try
$LogFile = "d:\monitors.csv"
Get-CimInstance -ClassName WmiMonitorID -Namespace root\wmi | ForEach-Object {
[PsCustomObject]#{
Manufacturer = [string]::new($_.ManufacturerName, 0, $_.ManufacturerName.Length).Trim("`0")
Name = [string]::new($_.UserFriendlyName, 0, $_.UserFriendlyName.Length).Trim("`0")
Serial = [string]::new($_.SerialNumberID, 0, $_.SerialNumberID.Length).Trim("`0")
}
} | Export-Csv -Path $LogFile -NoTypeInformation -UseCulture
The -UseCulture switch makes sure the output csv file uses the same delimiter characters your locally installed Excel would expect, so you can simply double-click the file to open it in Excel

Replace n'th character - and only that character with another [duplicate]

This question already has answers here:
Replacing only the first occurrence of a word in a string
(2 answers)
Closed 2 years ago.
I would like to replace the n'th character in a file with another - so for example you have
Type:1BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2
Type:2BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2
Type:3BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2
Type:4BANKFROMBANK1TO2 -> Type:2BANKFROMBANK1TO2
I do not know what the value will be, but I know at what place it will be - so for this example the 6th character.
When attempting to do it like this:
$Replace = Get-ChildItem "C:\temp\" -Filter "xfil*"
Foreach ($file in $Replace) {
(Get-Content $File.fullname).replace((Get-Content $File.fullname)[5],'x') | Set-Content -Path $file.FullName
}
It replaces every occurence of the character which is expected but not exactly what I want- how do I go around that?
Your examples do not match the description of the question. However, from your comment, I gather you simply want to replace the 26th character with another.
To do that, read the file in as single string and do the replacement:
$charIndex = 26 # the character index. Remember this counts from 0
$replaceWith = 'x' # the character to put in the $charIndex place
Get-ChildItem "C:\Temp" -Filter "xfil*" -File | ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw
$content.Replace($content[$charIndex], $replaceWith) | Set-Content -Path $_.FullName
}
Alternative
Get-ChildItem "C:\Temp" -Filter "xfil*" -File | ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw
if ($content.Length -gt $charIndex) {
"{0}$replaceWith{1}" -f $content.Substring(0, $charIndex),
$content.Substring($charIndex + 1) |
Set-Content -Path $_.FullName
}
}
It can also be achieved with this one liner:
$Content = Get-Content -path $file.fullname
$Replace = $Content.Remove(22,1).Insert(22,$ReplaceNumber) | Set-content $file.fullname

How to read NTFS permissions in a list of shares from a text file

I am trying to get it easier to use on file servers. I would like to import a file called shares.txt. In that shares.txt there are for example lines, e.g. folder name → root folder and share name → subfolder.
\\10.10.15.240\folder name\share name
\\10.10.15.240\folder name\share2 name
\\10.10.15.240\folder name\share3 name
\\10.10.15.240\folder2 name\share name
\\10.10.15.240\folder2 name\share2 name
\\10.10.15.240\folder2 name\share3 name
and so on.
And I would like to create the output to:
Outputs the data to a text file on \\10.10.15.240\output\ using the folder name and share name as part of the file name
\\10.10.15.240\output\Company_name_$folder name_$share name.csv
\\10.10.15.240\output\Company_name_$folder name_$share2 name.csv
\\10.10.15.240\output\Company_name_$folder name_$share3 name.csv
\\10.10.15.240\output\Company_name_$folder2 name_$share name.csv
\\10.10.15.240\output\Company_name_$folder2 name_$share2 name.csv
\\10.10.15.240\output\Company_name_$folder2 name_$share3 name.csv
and continue.
Do you know how to import this in your script? I tried several things but they all comes up with errors.
Script:
I want to write the permissions list to a CSV file instead of writing it directly to Excel.
$ErrorActionPreference = "SilentlyContinue"
$a = New-Object -ComObject Excel.Application
$a.visible = $true
$b = $a.Workbooks.Add()
$intRow = 1
$c = $b.Worksheets.Item(1)
$c.Cells.Item($intRow,1) = "Folder"
$c.Cells.Item($intRow,2) = "Compte/groupe"
$c.Cells.Item($intRow,3) = "Type d'Acces"
$c.Cells.Item($intRow,4) = "Droits"
$d = $c.UsedRange
$d.EntireColumn.AutoFit()|Out-Null
$d.Interior.ColorIndex = 19
$d.Font.ColorIndex = 11
$d.Font.Bold = $true
Remove-Variable arrayOfPath
$depth = 2
$RootFolder = "\\MySRV\Folder"
for ($i=0; $i -le $depth; $i++) {
$arrayOfPath += ,$RootFolder
$RootFolder = $RootFolder + "\*"
}
$arrayOfPath | Get-ChildItem | %{
Get-Acl $_.FullName
} | %{
$intRow = $intRow + 1
$c.Cells.Item($intRow, 1) = $_.Path.ToString().Replace("Microsoft.PowerShell.Core\FileSystem::", "")
$droit = $_.Access
$droit | %{
$c.Cells.Item($intRow, 2) = $_.IdentityReference.ToString();
$c.Cells.Item($intRow, 3) = $_.AccessControlType.ToString();
$c.Cells.Item($intRow, 4) = $_.FileSystemRights.ToString();
$intRow = $intRow+1
}
}
$d.EntireColumn.AutoFit() | Out-Null
Last Update:
$arrayOfPaths= Get-Content "\\UNC PATH\myfile.txt"
Get-ChildItem $arrayOfPaths -Recurse | Where-Object {$_.mode -match "d"} | ForEach-Object {
$csv = $_.FullName -replace '^\\\\[^\\]+\\([^\\]+)\\(.*)', '\\UNC PATH\Company_name_${1}_${2}.csv' # <- construct CSV path here
$path = $_.FullName
Get-Acl $path | Select-Object -Expand Access |
Select-Object #{n='Path';e={$path}}, IdentityReference, AccessControlType,
FileSystemRights |
Export-Csv $csv -Append -NoType
}
Basically you'd do something like this:
loop over the paths
get each folder's ACL
expand the ACEs
select the ACE properties you want exported
add the path with a calculated property
export the selected properties to a CSV
Appending to the output CSV(s) inside the loop allows you to control to which file each ACL is exported. You can for instance make the file name depend on elements of the source path, or the number of elements already written (if you add a counter variable).
Get-ChildItem $arrayOfPaths | ForEach-Object {
$csv = "..." # <- construct CSV path here
$path = $_.FullName
Get-Acl $path | Select-Object -Expand Access |
Select-Object #{n='Path';e={$path}}, IdentityReference, AccessControlType,
FileSystemRights |
Export-Csv $csv -Append -NoType
}
}
Note that -Append was added to Export-Csv in PowerShell v3. On earlier versions you can sort of emulate it with ConvertTo-Csv and Add-Content.

PowerShell does not replace string although you can see it in cmd

I normally find the answer to my problem by going through the site, but this time I have read every question yet still I am in despair and really need an experienced eye.
What I have is basically a structural health monitoring system. I measure strains and receive raw data. This raw data is processed by a MATLAB executable that I wrote myself and then uploaded to an ftp-server. We had a student that automated this with a PowerShell script which was working perfectly until I changed literally one small line in MATLAB and recompiled the code.
I do not understand much about PowerShell, so please be patient with me. The error I receive is you cannot call a method on a null-valued expression. This occurs when I try to replace a set of strings (just called xxx_xxx) with a date that exists as a variable in PowerShell. I can see xxx_xxx in the command window (see attached image), I can print out the date that I want to use as replacement, but somehow it does not work.
I cannot provide a working code snippet because you would need the DAQ to generate data, and as I said, I don't understand the language much. But below is the code. For easier reading, the line that I am receiving the error is the following:
$outData = $cmdOutput.Replace("xxx_xxx",$snaps[$i].Substring(6,4)+"-"+$snaps[$i].Substring(3,2)+"-"+$snaps[$i].Substring(0,2)+" "+$snaps[$i].Substring(11,8)+";")
If anyone could help me with this, I would be eternally grateful!
$retry=3
while(1){
#$dir = "C:\Users\Petar\Documents\Zoo\PetarData\INPUT DATA\New folder\"
$dir = "C:\Users\Yunus\Documents\Micron Optics\ENLIGHT\Data\" + $(get-date -f yyyy) + "\" + $(get-date -f MM) + "\"
#$outdir = "C:\Users\Petar\Documents\Zoo\PetarData\OUTPUT DATA\New folder\"
$archivedirin = "C:\Users\Yunus\Documents\Elefantenhaus\Archive\IN\"
$archivedirout = "C:\Users\Yunus\Documents\Elefantenhaus\Archive\OUT\"
$tempdir = "C:\Users\Yunus\Documents\Elefantenhaus\Archive\TEMP\"
$prefix = "EHZZ";
$filecount=(Get-ChildItem $dir).Count
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
if($filecount -gt 1){
$exclude = $latest.name
$Files = GCI -path $dir | Where-object {$_.name -ne $exclude}
$dest = $archivedirin + "batch_"+$(get-date -f MM-dd-yyyy_HH_mm_ss)+"\"
new-item -type directory $dest
foreach ($file in $Files){move-item -path ($dir+$file) -destination $dest}
$latest = Get-ChildItem -Path $dest | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$filename = $dest + $latest.name
$s=Get-Content $filename
while($s -eq $null){
if($retry -lt 0){break}
write-host "could not read file"
$retry = $retry -1
$s=Get-Content $filename
}
#read content of input file
$snaps = $s
#loop through the lines in the file until the first occurence of a timestamp, that is our desired line
for ($i = 0; $i -lt $snaps.length; $i++)
{
$ismatch =[regex]::Matches($snaps[$i], '^(\d\d.\d\d.\d\d\d\d\s\d\d+)')
if ( $ismatch -ne $null -and $ismatch[0].Groups[1].Value)
{
$temp=Get-Content $filename | select -skip $i
$filenametemp = $tempdir+"\temp.txt" #temp file path, don't change the filename "temp.txt"
#$filename3 = $tempdir+"\test.txt"
Add-Content $filenametemp $temp
$filename = $archivedirout+$prefix+"_"+$snaps[$i].Substring(8,2)+$snaps[$i].Substring(3,2)+$snaps[$i].Substring(0,2)+"_"+$snaps[$i].Substring(11,2)+$snaps[$i].Substring(14,2)+$snaps[$i].Substring(17,2)+".txt"
$cmdOutput = (cmd /c new_modified.exe $tempdir) | Out-String
write-output $cmdOutput #"$cmdOutput is:"
#IF ([string]::IsNullOrWhitespace($cmdOutput)){
# break
#}
$outData = $cmdOutput.Replace("xxx_xxx",$snaps[$i].Substring(6,4)+"-"+$snaps[$i].Substring(3,2)+"-"+$snaps[$i].Substring(0,2)+" "+$snaps[$i].Substring(11,8)+";")
Add-Content $filename $outData
remove-item -path $filenametemp
break
}
}
#break
}
else
{
write-host "waiting for file"
}
Start-Sleep -s 30
}
I think what is happening is that the output of the external program isn't being piped into a variable correctly. I haven't had a chance to test this but Tee-Object looks like the appropriate method for you.
I would suggest you try replacing...
$cmdOutput = (cmd /c new_modified.exe $tempdir) | Out-String
with...
cmd /c new_modified.exe $tempdir | Tee-Object -variable $cmdOutput

add colum to merged csv file

Ok heres what I have code wise:
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
(get-content $a) | set-content $b
This pulls all the data of all the files into one merged file, but I need one additional item, I need to pull the name of the individual files and append it to the first column of the file for multiple files, several hundred at a time.
Not tested but something like this should do it:
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
Get-ChildItem $a | % {
Import-Csv $_.Fullname | Add-Member -MemberType NoteProperty -Name 'File Name' -Value $_.Name
} | Export-Csv $b
Assuming the CSV files each have the same column headings, I would lean toward using Import-CSV instead of Get-Content so that you can work with the CSV contents as arrays of objects with named properties.
Then all you need to do is iterate through each item of the array and append a property containing the file path, which you can do using the Add-Member cmdlet. Once that's done, export the array of objects using the Export-CSV cmdlet.
$directory = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\"
$search = $directory + "*.csv"
$exportpath = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
$paths = get-childitem $search
$objectArrays = #()
$paths | %{
$filepath = $_.fullname;
$objectArray = Import-CSV $filepath;
$objectArray | %{
Add-Member -inputobject $_ -Name "SourceFile" -Value $filepath -MemberType NoteProperty};
$objectArrays += $objectArray}
$objectArrays | export-csv -path $exportpath -notype
This puts the SourceFile property as the last column in the outputted CSV file
Ok, simplification... Search target folder, pipe to a ForEach-Object loop (shorthand % used), capture the file name as variable, import the CSV, add the sourcefile using the Select-Object cmdlet, convert it back to a CSV, end loop, pipe to destination file.
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
GCI $a | %{$FileName=$_.Name;Import-CSV $_|Select #{l='SourceFile';e={$FileName}},*|ConvertTo-CSV -NoType} | set-content $b

Resources