How to extract environment path lines with conditions? - powershell

In short: How to get joined lines which not matches any string item of an array?
It seems trivial but I can't find solution. I'm trying to manage VSCode Python environments with PowerShell functions. I use PowerShell script to change vscode terminal path settings with ease. I want to get environment path lines which is not a Python path.
$noPythonPath = ($env:path.Split(';') | Where-Object {
$_ -notmatch 'python' -and
$_ -notmatch 'anaconda' -and
$_ -notmatch 'django' -and
$_ -notmatch 'pspenv'
}) -join ';'
I wonder is there are way to do this without manually listing all the path keys. But below code does not work. Seems -match not work with string array.
$key = $PC2paths.Keys
$noPythonPath = ($env:path.Split(';') | Where-Object {
$_ -notmatch $key.TrimEnd('1234567890')
}) -join ';'
And below is containing codes.
$PC2paths = #{
'Anaconda3' = "C:\Program Files\Anaconda3\python.exe;C:\Program Files\Anaconda3\Scripts;";
'DjangoVenv2' = "C:\Users\user\source\repos\CsStudy\.virtualenvs\Djangovenv2\Scripts\python.exe;" ;
"pypy2" = "C:\pypy2-v5.9.0-win32\pypy2-v5.9.0-win32\pypy.exe;C:\pypy2-v5.9.0-win32\pypy2-v5.9.0-win32\bin;";
'python2' = "c:\python27\python.exe;C:\Python27\Scripts;";
'python3' = "C:\Program Files\Python36-32\python.exe;C:\Program Files\Python36-32\Scripts;";
'ropepython2' = "C:\Users\user\source\repos\rope\ropeTest1\ropeTest1\rope1\.vscode\ropePython\Scripts\python.exe;";
'pspenv1' = "C:\Users\user\source\repos\psptools\.virtualenvs\pspenv1\Scripts\python.exe;";
'RefreshEnvironment' = 'C:\Users\user\source\repos\CsStudy\.vscode\refreshenv.cmd';
}
function workon($environment) {
& cmd /k workon.bat $environment
}
function useEnv([string]$envName) {
$pathKey = $envName
$SettingsFilePath = (UpSearch . "settings.json")
if ($SettingsFilePath -eq "file not found") {
Write-Output 'not found'
return
}
$key = $PC2paths.Keys
$noPythonPath = ($env:path.Split(';') | Where-Object {
$_ -notmatch $key.TrimEnd('1234567890')
}) -join ';'
$newpaths = (($PC2paths.$pathKey) + $noPythonPath)
$newpaths = $newpaths.split(';')
$exepath = $newpaths[0]
$newpaths[0] = Split-Path $newpaths[0]
$ofs = ';'
$newpath = "$newpaths"
$env:path = $newpath
$a = Get-Content $SettingsFilePath -Raw | ConvertFrom-Json
$a."python.pythonPath" = $exepath
$a."terminal.integrated.env.windows" | Add-Member -Name "path" -Value $newpath -MemberType NoteProperty -Force
$a | ConvertTo-Json -Depth 30 | Set-Content $SettingsFilePath
# $refresh = UpSearch . "RefreshEnv.cmd"
$refresh = $PC2paths."RefreshEnvironment"
cmd /c $refresh
}
function useAnaconda3 {
useEnv -envName 'Anaconda3'
}
function useDjangoVenv2 {
useEnv -envName 'DjangoVenv2'
}
function usePypy2 {
useEnv -envName 'pypy2'
}
function usePython2 {
useEnv -envName 'python2'
}
function usePython3 {
useEnv -envName 'python3'
}
function useropepython2 {
useEnv -envName 'ropepython2'
}
function usepspenv1 {
useEnv -envName 'pspenv1'
}
function UpSearch($pathtosearch, $filename) {
if ($pathtosearch -eq "") {
"file not founded"
return
}
$pathtosearch = (Get-Item -Path $pathtosearch -Verbose).FullName
if (Test-Path "$pathtosearch\.vscode\$filename" ) {
"$pathtosearch\.vscode\$filename"
} else {
UpSearch (Split-Path $pathtosearch) $filename
}
}

Correct, -match (as well as -notmatch) does not work with an array as the second operand. You can join the array to an alternation regular expression, though:
$a = 'foo', 'bar', 'baz'
$re = ($a | ForEach-Object { [regex]::Escape($_) }) -join '|'
'something', 'bar', 'else' -notmatch $re

Related

My use of Out-File and/or [system.io.file]::WriteAllText

Both commands work when running inside Visual Studio Code but if I execute the ps1 file directly in pwsh 7 the file is blank. Same if I right-click and select Open With pwsh.
Tried both commands, out-file first but moved to [system.io.file]::WriteAllText to remove blank rows. Both successfully create and populate a csv file as expected when I execute it from within Visual Studio Code.
try {
$files = #()
$files = Get-ChildItem -Path "\\networkShare\ps1files"
#$files = $files + (get-childitem -Path "\\networkShare\ps1files2")
$results = foreach ($file in $files) {
if ($file.FullName.substring($file.FullName.length - 3, 3) -eq "ps1") {
#Write-Host $file
$scriptblock = [scriptblock]::Create((Get-Content -Raw -Path $file.FullName))
$ast = $scriptblock.Ast
$commands = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$commandText = foreach ($command in $commands) {
$command.CommandElements[0].Extent.Text
}
$commandText |
Select-Object -Unique |
Sort-Object |
Select-Object #{
Label = "Module"
Expression = { (Get-Command $_).Source.Substring(0, (Get-Command $_).Source.Length) }
}
$value = ""
foreach ($result in $results) {
if ($result.Module -inotlike "C:*") {
if ($value -inotlike "*" + $result.Module + "*") {
$value = $value + $result.Module + "`n"
}
}
}
}
}
$ModulesInUse = #()
$NewModules = #()
$NewestMod = #()
#$NewestMod = ""
$ModulesInUse = Import-Csv \\networkShare\PSModulesUsed.csv -Header "Module"
$NewModules = $results.Where({ $_.module -notin $ModulesInUse.Module -and $_.Module.Length -gt 0 -and $_.Module -inotlike "C:*" })
if ($NewModules.count -gt 0) {
Write-Host "New Modules found `n"
# Create unique array of new modules found
foreach ( $mod in $NewModules ) {
if ($mod.Module -notin $NewestMod.Module) {
$NewestMod = $NewestMod + $mod
}
}
$NewestMod.module
Write-Host `n
$Prompt = 'Do you want to update the Modules Used csv file now? Y/N'
$UpdateFile = Read-Host -Prompt $Prompt
if ($UpdateFile -eq 'y') {
#$value | Out-File "\\networkShare\PSModulesUsed.csv"
[system.io.file]::WriteAllText(“\\networkShare\PSModulesUsed.csv”, $value)
}
}
else {
Read-Host "No new Modules found. Press any key to exit"
}
Big thanks to mklement0!!!
Making that change to Code revealed the $value I was sending was not correct, even though it was working before. When running outside of Code it was blank.
I moved the code below to outside the $Results block of code and it works now.
$value = ""
foreach ($result in $results) {
if ($result.Module -inotlike "C:*") {
if ($value -inotlike "*" + $result.Module + "*") {
$value = $value + $result.Module + "`n"
}
}
}

How to read a specific item of INI file using PowerShell?

I want to read a specific item of my INI file, I have 5 Items in the section of my INI file, and I want to read 4 items, except items number 3.
I already tried to read all the items, but I can not find a way how to specify the item that I want to read and the format of the file that I read is like this:
Name Value
AA 12
BB 13
CC 14
DD 15
EE 16
I used this command to execute it.
File1.ps1 Read-File -FilePath C:\Users\Data.ini -a_section Code -store C:\Users\
function Read-File {
Param(
[Parameter(Mandatory=$true)]$FilePath,
[Parameter(Mandatory=$true)]$a_section,
[Parameter(Mandatory=$true)]$store
)
$input_file = $FilePath
$ini_file = #{}
Get-Content $input_file | ForEach-Object {
$_.Trim()
} | Where-Object {
$_ -notmatch '^(;|$)'
} | ForEach-Object {
if ($_ -match '^\[.*\]$') {
$section = $_ -replace '\[|\]'
$ini_file[$section] = #{}
} else {
$key, $value = $_ -split '\s*=\s*', 2
$ini_file[$section][$key] = $value
}
}
$Path_Store = $store
$Get_Reg = $ini_file.($a_section)
$Output = $Get_Reg | Out-File $Path_Store\Out_Test
}
$cmd, $params = $args
& $cmd #params
My expectation result, I have an output file like this
AA=12
BB=13
DD=15
EE=16
My INI File look like this:
[Name]
1=Joe
2=Grace
[Code]
AA=12
BB=13
CC=14
DD=15
EE=16
Try this:
function Get-IniSection {
Param(
[Parameter(Mandatory=$true)]$Path,
[Parameter(Mandatory=$true)]$SectionName
)
$ini_file = #{}
Get-Content $Path | ForEach-Object {
$_.Trim()
} | Where-Object {
$_ -notmatch '^(;|$)'
} | ForEach-Object {
if ($_ -match '^\[.*\]$') {
$section = $_ -replace '\[|\]'
$ini_file += #{ $section = #{} }
} else {
$key, $value = $_ -split '\s*=\s*', 2
$ini_file[$section] += #{ $key = $value }
}
}
return $ini_file[$SectionName]
}
$section = Get-IniSection -Path "C:\temp\test.ini" -SectionName "code"
$section.GetEnumerator() | Where-Object { $_.Name -ne "EE" }
$section.GetEnumerator() | ForEach-Object { "$($_.Name)=$($_.Value)" }
$section.GetEnumerator() |
Where-Object { $_.Name -in #("A1","AE","AP","AS","E1","E2","JP","M1","M2","N1","N2","P1","P2","P3","P4","PR","RU","S1","S2","W1","W2","W3","W4","ZH") } |
Select-Object -ExpandProperty "Value"
$section.GetEnumerator() |
Where-Object { $_.Name -in #("A1","AE","AP","AS","E1","E2","JP","M1","M2","N1","N2","P1","P2","P3","P4","PR","RU","S1","S2","W1","W2","W3","W4","ZH") } |
Foreach-Object { ($_.Value -split ",")[0] }

How do I use Function in PowerShell to execute from Command Line?

I want to execute my script with command line. I use function inside my script.
I used this script:
Function Get-IniFile
{
Param(
[parameter(mandatory=$true)][string]$FilePath
)
$input_file = $FilePath
$ini_file = #{}
Get-Content $input_file | ForEach-Object {
$_.Trim()
} | Where-Object {
$_ -notmatch '^(;|$)'
} | ForEach-Object {
if ($_ -match '^\[.*\]$') {
$section = $_ -replace '\[|\]'
$ini_file[$section] = #{}
} else {
$key, $value = $_ -split '\s*=\s*', 2
$ini_file[$section][$key] = $value
}
}
$Get = $ini_file.Class.Amount
$Get
}
I execute this script from command line with this command:
PowerShell.ps1 Get-IniFile -FilePath
I dont get any result when I execute this code, but If I remove "Function Get-IniFile" I got the value of Amount.
This is my INI file
[Class]
Amount = 1000
Feature = 20
[Item]
Set = 100
Return = 5
You have to call your function inside your script. The script code is like your main function in C# or Java.
PowerShell.ps1 content :
# Global script parameters.
Param(
[parameter(mandatory=$true)][string]$FilePath
)
# Definition of the function
Function Get-IniFile
{
Param(
[parameter(mandatory=$true)][string]$Path
)
$input_file = $Path
$ini_file = #{}
Get-Content $input_file | ForEach-Object {
$_.Trim()
} | Where-Object {
$_ -notmatch '^(;|$)'
} | ForEach-Object {
if ($_ -match '^\[.*\]$') {
$section = $_ -replace '\[|\]'
$ini_file[$section] = #{}
} else {
$key, $value = $_ -split '\s*=\s*', 2
$ini_file[$section][$key] = $value
}
}
$Get = $ini_file.Class.Amount
$Get
}
# Calling the function with the global parameter $FilePath
Get-IniFile $FilePath

Find and Replace multi-line string

I'm trying to find a string and add the string needed for a program.
I need the code to look to see if the action = run fast already exists and if so do nothing.
$Input = GetContent "${Env:ProgramFiles}\myprogram\file.conf"
$replace = #"
[MyAction_Log]
action = run fast
"#
$Input -replace ('action = run fast') -replace ('\[MyAction_Log\]',$replace) | set-content "${Env:ProgramFiles}\myprogram\file.conf"
I would check before wantonly replacing things you think exist. Also, never use $Input as a variable name; it's an automatic variable and won't do what you think it will (treat it as read-only).
$path = "$Env:ProgramFiles\prog\file.conf"
$file = Get-Content -Path $path
$replacementString = #'
[MyAction_Log]
action = run fast
'#
if ($file -notmatch 'action\s=\srun\sfast')
{
$file -replace '\[MyAction_Log\]', $replacementString |
Set-Content -Path $path
}
An alternative that's able to cope with the action key being located anywhere in the [MyAction_Log] section
$Inside = $False
$Modified = $False
$Path = "$( $env:ProgramFiles )\prog\file.conf"
$NewLines = Get-Content $Path |
ForEach-Object {
if( $_.Trim() -like "[*]" ) { $Inside = $False }
if( $_.Trim() -like "*[MyAction_Log]*" ) { $Inside = $True }
If( $Inside -and $_ -like "action = *" -and $_ -notlike "*run fast*" ) { $Modified = $True; "action = run fast" } else { $_ }
}
If( $Modified ) { $NewLines | Set-Content $Path }

Retrieve matching strings from two text files

I have a text file file_paths.txt that contains full paths on each line:
C:\MyFolder1\app1.exe
C:\MyFolder2\l1.dll
C:\MyFolder3\app2.exe
C:\MyFolder1\l2.dll
C:\MyFolder5\app3.exe
C:\MyFolder3\app4.exe
C:\MyFolder6\app5.exe
I also have file folders.txt that contains list of folders:
C:\MyFolder1
C:\MyFolder2
C:\MyFolder3
C:\MyFolder4
C:\MyFolder8
I need to iterate through the list of folders in folders.txt, match it with files in file_paths.txt and write the results to a file result.txt like this:
In C:\MyFolder1 more than one files has been found:
C:\MyFolder1\app1.exe
C:\MyFolder1\l2.dll
In C:\MyFolder2 one file has been:
C:\MyFolder2\l1.dll
In C:\MyFolder3 more than one files has been found:
C:\MyFolder3\app2.exe
C:\MyFolder3\app4.exe
In C:\MyFolder4 no files has been found.
In C:\MyFolder8 no files has been found.
My attempt that doesn't work:
$paths = [System.IO.File]::OpenText("file_paths.txt")
$folders = [System.IO.File]::OpenText("folders.txt")
$result = "result.txt"
try {
for(;;) {
$folder = $folders.ReadLine()
if ($folder -eq $null) { break }
"In ">> $folder >> ": `n" >> $result
for(;;) {
$path = $paths.ReadLine()
if ($path -eq $null) { break }
if ($path -contains $folder) {" ">>$path>>"`n">>$result }
}
}
} finally {
$paths.Close()
$folders.Close()
}
I would separate processing from reporting. First build a hashtable from the contents of folders.txt and add the lines from file_paths.txt to the matching keys:
$folders = #{}
Get-Content 'folders.txt' | ForEach-Object { $folders[$_] = #() }
Get-Content 'file_paths.txt' | ForEach-Object {
$line = $_
$($folders.Keys) | Where-Object {
$line -like "$_*"
} | ForEach-Object {
$folders[$_] += $line
}
}
Then you can output the resulting data structure like this:
$folders.Keys | ForEach-Object {
'In {0} {1} files have been found' -f $_, $folders[$_].Count
if ($folders[$_].Count -gt 0) {
$folders[$_] | ForEach-Object { "`t$_" }
}
} | Out-File 'result.txt'
Below is a script you can use to do exactly what you need.
Note the $folderPath and $filePath variables. Replace with absolute or relative (to where you execute the script) path of the file_paths.txt and folders.txt files.
$folderPath = 'folders.txt'
$filePath = 'file_paths.txt'
(Get-Content $folderPath).Split('`r`n') | ForEach-Object {
$folder = $_
$count = 0
$fileArray = #()
(Get-Content $filePath).Split('`r`n') | ForEach-Object {
$file = $_
if( $file | Select-String $folder -Quiet ) {
$count++
$fileArray += $file
}
}
if($count -ne 0) {
Write-Output "In $folder, $count files has been found."
$fileArray | ForEach-Object {
Write-Output "`t$_"
}
} else {
Write-Output "In $folder, no files has been found."
}
}