I am currently working on a powershell script that maps directories along with loading database software. I have this current vbscript I am converting to powershell that is suppose to validate a temporary file path , but I am getting a little confused on what I may need to take out and what I can leave in.
Here is the original vbscript ...
'
' assure that temp version of Perl is used
'
perlPath = basePath & "install\perl\bin;"
WshShell.Environment("Process")("PATH") = perlPath & WshShell.Environment("System")("PATH")
'
' assure that temp version of Perl Lib is used
'
perlLib = basePath & "\install\perl\lib;" & basePath & "\install\perl\site\lib;"
WshShell.Environment("Process")("PERL5LIB") = perlLib
Here is what I have written in powershell so far ...
#
# assure that Oracle's version of Powershell is used
#
$psPath = $basePath + "install\powershell\bin;"
$sysPath = $WshShell.Environment("System") | Where-Object { $_ -match "PATH" } |
foreach-object {$_.Substring(9)} | Out-String
$psPos = $sysPath.contains($psPath)
if( -not ($psPos)){
[Environment]::SetEnvironmentVariable("PATH", ($psPath + $sysPath), "Process")
}
#
# assure that Oracle's version of Powershell Module is used
#
$psMod = $homePath + "\perl\lib;" + $homePath + "\perl\site\lib;" # still need to convert
$sysMod = $Env:PSModulePath
$psPos = $sysMod.contains($psMod)
if( -not ($psPos)){
[Environment]::SetEnvironmentVariable("PATH", ($psPath + $sysChk), "Process")
}}
The same validation is done later in the script with the "System" variables. I do have a module that I will be using, but the rest are scripts. I guess I am not sure if what I am converting is the right way to verify these pathways exist and if not to add the new pathways.
First of all, you should use the Join-Path cmdlet for combining a path:
$psPath = Join-Path $basePath "install\powershell\bin"
You can access the Pathvariable using $env:Path split it using -split ';' and select the first path entry using [0]. All in all, I would define the three path you want to set, put them into an array and iterate over it.
$powershellBin = Join-Path $basePath "install\powershell\bin"
$perLib = Join-Path $homePath "\perl\lib"
$perlSiteLib = Join-Path $homePath "\perl\site\lib"
#($powershellBin, $perLib, $perlSiteLib) | foreach {
if (-not (($env:Path -split ';')[0].Equals($_)))
{
[Environment]::SetEnvironmentVariable("PATH", ("{0};{1}" -f $_, $env:Path), "Process")
}
}
Related
My script is running in a loop on files and display the path for each file, I need to capture the name of the lower sub folder above the file name or even 2 subfolders (maybe there is a way to capture the needed subfolder or something)
For example:
$file = C:\Users\Bandit\AppData\Local\Temp\9c86ee608bb9477ebb11914c36a5a76d\638110173610123239\IDUDatabase_4.0.0.119\IDUDatabase\test.sql
Currently I know to remove a prefix and to display the relevant path (without the temp and sub temp)
$displayPath = ($file.FullName).Substring($subTemp.Length + 1)
$displayPath
# IDUDatabase_4.0.0.119/IDUDatabase
But its not what I need!!
I want to get the lower subfolder above the file name . in this case I want to get the 'IDUDatabase' (or if it possible to get also the IDUDatabase_4.0.0.119)
Example 2:
C:\Users\Bandit\AppData\Local\Temp\9c86ee608bb9477ebb11914c36a5a76d\638110173610123239\ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x\test.dll
The output should be 'ForwarderServiceVersion_8_6x'
cmdlet version
This way uses pure PowerShell cmdlets...
$path = "C:\Users\Bandit\AppData\Local\Temp\9c86ee608bb9477ebb11914c36a5a76d\638110173610123239\ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x\test.dll"
# get the full path of the parent directory
$parent = Split-Path -Path $path -Parent;
# "C:\Users\Bandit\AppData\Local\Temp\9c86ee608bb9477ebb11914c36a5a76d\638110173610123239\ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x
# get the last part of the above
$name = Split-Path -Path $parent -Leaf;
# ForwarderServiceVersion_8_6x
You can put this together into one expression as follows:
$name = Split-Path -Path (Split-Path -Path $path -Parent) -Leaf;
# ForwarderServiceVersion_8_6x
and you can simplify that a bit more by leaving out optional / default parameters:
$name = Split-Path (Split-Path $path) -Leaf;
# ForwarderServiceVersion_8_6x
dotnet version
Alternatively, if you don't mind a bit of dotnet class interaction you can do this:
$name = $path.Split([System.IO.Path]::DirectorySeparatorChar)[-2];
# ForwarderServiceVersion_8_6x
which splits the full path into an array of components and then returns the second-to-last item.
Update
If you want to select arbitrary parts of the path you can use the dotnet version and extract the directory levels that you're interested in as follows:
$separator = [System.IO.Path]::DirectorySeparatorChar;
# get the last two directories in the path
$name = $path.Split($separator)[-3..-2] -join $separator;
# ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x
# get the last two directories and filename
$name = $path.Split($separator)[-3..-1] -join $separator;
# ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x\test.dll
# get everything except the first $x parts of the path
$x = 7;
$parts = $path.Split($separator);
$name = $parts[$x..($parts.Length-1)] -join $separator;
# 638110173610123239\ElcServices_4.0.0.120\ForwarderServiceVersion_8_6x\test.dll
How can I tell if a specified folder is in my PATH using PowerShell?
A function like this would be great:
function FolderIsInPATH($Path_to_directory) {
# If the directory is in PATH, return true, otherwise false
}
Going off this question, you don't need a function for this but can retrieve this with $Env:Path:
$Env:Path -split ";" -contains $directory
The -contains operator is case-insensitive which is a bonus. It could be useful placing this in a function to ensure trailing slashes are trimmed, but that's uncommon:
function inPath($directory) {
return ($Env:Path -split ';').TrimEnd('\') -contains $directory.TrimEnd('\')
}
There's a bunch of answers that do a $path.Split(";") or $path -split ";" that will probably be fine for 99.9% of real-world scenarios, but there's a comment on the accepted answer on a similar question here by Joey that says:
Will fail with quoted paths that contain semicolons.
Basically, it's a bit of an edge case, but this is a perfectly valid PATH on Windows:
c:\temp;"c:\my ; path";c:\windows
so here's a hot mess of code to address that...
function Test-IsInPath
{
param( [string] $Path, [string] $Folder )
# we're going to treat the path as a csv record, but we
# need to know how many columns there are so we can create
# some fake header names. this might give a higher count
# than the real value if there *are* quoted folders with
# semicolons in them, but that's not really an issue
$columnCount = $Path.Length - $Path.Replace(";","").Length
# generate the list of column names. the actual names
# don't matter - it's just so ConvertFrom-Csv treats our
# PATH as a data row instead of a header row
$headers = 0..$columnCount
# parse the PATH as a csv record using ";" as a delimiter
$obj = $path | ConvertFrom-Csv -header $headers -delimiter ";"
# extract an array of all the values (i.e. folders)
# in the record we just parsed
$entries = $obj.psobject.properties.value
# check if the folder we're looking for is in the list
return $entries.Contains($Folder)
}
Whether this is a "better" answer than the simple split approach depends on whether you expect to have quoted folders that contain semicolons in your PATH or not :-)...
Example usage:
PS C:\> Test-IsInPath -Path $env:PATH -Folder "c:\temp"
False
PS C:\> Test-IsInPath -Path "c:\temp;`"c:\my ; path`";c:\windows" -Folder "c:\temp"
True
PS C:\> Test-IsInPath -Path "c:\temp;`"c:\my ; path`";c:\windows" -Folder "c:\my ; path"
True
Note: what this still doesn't solve is paths that end (or don't end) with a trailing "\" - e.g. testing for C:\temp when the PATH contains C:\temp\ and vice versa.
I would go for something like this
function FolderIsInPATH {
param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$your_searched_folder
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path
)
$Folders = Get-Childitem -Path $Path -Directory
foreach ($Folder in $Folders) {
if ($Folder.Name -eq $your_searched_folder) {
##Folder found
} else {
##folder not found
}
}
}
You can get your PATH using [Environment]::GetEnvironmentVariables()
[Environment]::GetEnvironmentVariables()
Or if you want to get the user environment variables:
[Environment]::GetEnvironmentVariables("User")
Next, get the PATH variable:
$Path = [Environment]::GetEnvironmentVariables().Path # returns the PATH
Then check if the specified folder is in your PATH:
$Path.Contains($Path_to_directory + ";")
The function put together:
function FolderIsInPath($Path_to_directory) {
return [Environment]::GetEnvironmentVariables("User").Path.Contains($Path_to_directory + ";")
}
However, this function is case-sensitive. You can use String.ToLower() to make it not case-sensitive.
function FolderIsInPath($Path_to_directory) {
return [Environment]::GetEnvironmentVariables("User").Path.ToLower().Contains($Path_to_directory.ToLower() + ";")
}
Now call your function like this:
FolderIsInPath("C:\\path\\to\\directory")
Note that the path must be absolute.
As pointed out in mclayton's comment, this function won't work for the last path variable. To address this issue, simply add a ; to the end of the path. Your function would now look like this.
function FolderIsInPath($Path_to_directory) {
return [Environment]::GetEnvironmentVariables("User").Path.ToLower() + ";".Contains($Path_to_directory.ToLower() + ";")
}
i need you help again :D
I have created a function to put the error logs in a file who take the name of my script (i call multiples scripts so it's very helpful), here is my function :
function ExportLog{
$path = Get-Location
$LogFile = [io.path]::ChangeExtension($MyInvocation.ScriptName,"log")
Write-Host $LogFile
$timestamps = Get-Date
$string_err = $_ | Out-String
$ExportError = "[" + $timestamps.DateTime + "]`n" + $string_err + "`n"| Out-File -FilePath $LogFile -Append
Read-Host “Appuyez sur ENTRER pour quitter...”}
This works fine but the log file created or edited is in the path of my script.
My question is how can i add \log\ in the path who is in my variable $LogFile ?
I tried to use Join-Path, but it just add path like this : C:\import\Modif_CSV.log\Logs ... I wan't to add the Logs folder before the name of the file ^^
Ty for help :)
You can split the current script filename from the full path and change the extension with:
$LogFileName = [IO.Path]::ChangeExtension((Split-Path $PSCommandPath -Leaf), 'log')
Next combine the current script path with the subfolder 'log' and with the new filename
$LogFullName = [IO.Path]::Combine($PSScriptRoot, 'log', $LogFileName)
Theo's helpful answer shows a .NET API-based solution that works in both Windows PowerShell and PowerShell (Core) 7+.
Here's a PowerShell (Core) 7+ solution that showcases new features (relative to Windows PowerShell):
$dir, $name = $PSCommandPath -split '\\', -2
Join-Path $dir log ((Split-Path -LeafBase $name) + '.log')
-split '\\', -2 splits the path into two strings by \: the last \-separated token, preceded by everything before the last \, thereby effectively splitting a file path into its directory path and file name. That is, -split now accepts a negative number as the count of tokens to return, with -$n meaning: return $n-1 tokens from the right of the input string (albeit in left-to-right order), and save any remaining part of the string in the return array's first element; e.g., 'a/b/c/d' -split '/', -3 yields 'a/b', 'c', 'd'
Split-Path -LeafBase returns a file path's file-name base, i.e. the file name without its extension.
Join-Path now accepts an open-ended number of child paths to join to the parent path; e.g., Join C:\ dir subdir now works to create C:\dir\subdir, whereas in Windows PowerShell you had to nest calls: Join-Path (Join-Path C:\ dir) subdir
Note: It would be handy if Split-Path supported returning all components of a given path in a single operation; GitHub issue #6606 proposes an -All switch that returns an object whose properties reflect all the constituent parts of the path, which would enable the following simplified solution:
# WISHFUL THINKING, as of PowerShell 7.2
$pathInfo = Split-Path -All $PSCommandPath
Join-Path $pathInfo.Parent log ($pathInfo.LeafBase + '.log')
I have an old piece of PowerShell script that uses Add-Content in a for loop to write data to a file and it works fine when run locally on a PC's C: drive.
I've now been asked to relocate the script and files to a QNAP folder share (not sure if this has anything to do with the problem).
Now when the script runs from the share, the for loop runs infinitely - you can see the file size increasing and check the row count once you break out of the program.
It doesn't seem to matter if I use UNC or drive mapping the infinite looping still occurs.
Here is the script block:
####################
# Define Variables #
####################
$Source = 'G:\Label_Spreadsheets\' + $textbox1.Text + '.csv'
$Target = 'G:\Label_Spreadsheets\labels.csv'
$EndNum = ($LastUsedNumber + $numericupdown1.Value)
######################
# Create Source File #
######################
#######################
# Add CSV Header rows #
#######################
Add-Content $Source "Stock Code,Filter#,ProductionOrder#,SalesOrder#,Compatability";
#####################
# Add specific Rows #
#####################
for ($i = $StartNum; $i -le $EndNum; $i++) {
$line = $combobox1.SelectedItem + ',' + $i + ',' + $textbox1.Text + ',' + $textbox2.Text + ','
Add-Content $Source $line
}
I wondered if it was a known provider problem (as shown at this URL) but trying these did not resolve the issue.
And yes I know it's writing a CSV - like I said: old script.
My first excursion into a real app in Powershell; I could write the code in a traditional .Net language, but I think Powershell is ideal for console apps like this. I've searched StackOverflow and found the (nearly) exact script I need; my issue is that I want to take a folder of files (on server fsA) and create 7-Zip archives on fsB named by the file date. For instance, all the files that were last written on 7/21/12 would be found in archive 20120721.7z. I'm basing this on the solution found here: Create a 7z Archive for files created by date using Powershell
Here is what I have so far:
$prog_dir = "C:\Progra~1\7-Zip\7z.exe"
$archive_dir = "\\fsa\Backup"
$input_dir = "\\fsb\Xml\Archive\"
$7zOpts = "-m0=PPMd:o32:mem192M"
$groups = dir $input_dir | group-object -property {$_.LastWriteTime.Date}
$groups | foreach{$cmd = $prog_dir + " a " + $archive_dir + "\$((Get-Date $_.Name).ToString(`"yyyyMMdd`")).7z $([string]::join(`" `", $_.Group)) " + $7zOpts} #; invoke-expression $cmd}
#
#Where to put this: "+ $input_dir +"???
#
$cmd
I can't seem to find a way to specify the input directory in the foreach line; I can put it at the beginning of the join statement, but it only adds the input directory to the first file in the list. For instance:
$groups | foreach{$cmd = $prog_dir + " a " + $archive_dir + "\$((Get-Date $_.Name).ToString(`"yyyyMMdd`")).7z " + $input_dir + "$([string]::join(`" `", $_.Group)) " + $7zOpts} #; invoke-expression $cmd}
produces the output
C:\Progra~1\7-Zip\7z.exe a \\fsa\Backup\20120722.7z \\fsb\Xml\Archive\255IQR.xml 2573WV.xml 257RMC.xml
where the last two files do not have the full path prepended? What am I doing wrong?
PowerShell is a shell, so running commands is pretty much the thing it should do well. So generally, if you resort to Invoke-Expression when running commands you're doing something very wrong. Let's try a different way:
$prog_dir = 'C:\Progra~1\7-Zip\7z.exe' # you don't need 8.3 names here, by the way
$archive_dir = '\\fsa\Backup'
$input_dir = '\\fsb\Xml\Archive\'
$7zOpts = '-m0=PPMd:o32:mem192M'
$groups = dir $input_dir | group-object -property {$_.LastWriteTime.Date}
$groups | foreach {
$files = $_.Group | select -expand FullName
$date = (Get-Date $_.Name).ToString('yyyyMMdd')
& $prog_dir a $archive_dir\$date.7z $files $7zOpts
}