I have a PowerShell Script that finds all log4j jar files on a system. I want to separate the values out of the found paths based on just path, filename, and version.
For example, if I have a returned path of C:\Users\Administrator\AppData\Roaming\.minecraft\libraries\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar, how can I get:
Just the path - C:\Users\Administrator\AppData\Roaming\.minecraft\libraries\org\apache\logging\log4j\log4j-core\2.14.1\
Just the file - log4j-core-2.14.1.jar
Just the version - 2.14.1
I would simply use Get-ChildItem for that.
$Item = Get-ChildItem -Path 'C:\Users\Administrator\AppData\Roaming\.minecraft\libraries\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar'
# Folder containing the file
$Item.PSParentPath
# Filename
$Item.Name
# Version extracted from the Parent path
# As string
($ITem.PSParentPath.Split('\'))[-1]
# As System.Version object
[Version]($ITem.PSParentPath.Split('\'))[-1]
Or, using Split-Path
$FullPath = 'C:\Users\Administrator\AppData\Roaming\.minecraft\libraries\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar'
$Filename = Split-Path -Path $FullPath -Leaf
$ParentPath = Split-Path -Path $FullPath -Parent
# Version from Parent path
$Version = $ParentPath.Split('\')[-1]
Bonus
This is unnecessary since you can get the version straight from the path but if the path was not formatted in such a way, you could extract the version from the filename in the following way.
$Version = $Filename -replace 'log4j-core-(.*).jar', '$1'
Bonus #2
Let's say you want to be proactive with the possible filename change, you could then parse the extracted version into a System.Version object to make sure you are getting something that make sense.
$Version = $null
if (![Version]::TryParse(($Filename -replace 'log4j-core-(.*).jar', '$1'),[ref]$Version)) {
Write-Warning 'Version could not be parsed from the filename'
} else {
Write-Host "Version is $Version"
}
That would ensure that you indeed have a version of something. and not a different string (this would only happen if they suddently changed the filename to something else).
Related
I just want to know how to convert an images folder into a CBZ file READABLE for my Ebook (I checked the ebook and she can read this format).
Optimally, I would like to convert it without having to install everything. Just a script.
For those who are fast, I already answered my question... Just sharing it.
GO SEE UPDATE PART
Assuming your OS is Windows, we can do it with Batch or PowerShell.
For this one its process is quite easy, we just need to understand that a CBZ file IS a ZIP file with images in it. So we will just:
zip with 7zip because for some reasons the files converted with WinRAR didn't worked in my Ebook (wasn't even in the library) ;
Change the extension from .zip to .cbz.
I'm only going to show the PowerShell one because the .bat script had known issues.
Architecture
Architecture of the directory
The architecture should be:
My first folder
first image
second image
My second folder
first image
second image
PowerShell
Here's the code from my "#_ImagesFolderToCBZ.ps1"
Clear-Host
# INPUT - Script folder path
$i = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
# 7Zip path
$7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
# A list of the path of the zipped files
$listOfZippedFiles = New-Object Collections.Generic.List[String]
$listOfZippedNames = New-Object Collections.Generic.List[String]
# Ask the user if we delete the folders after their conversion
$isSdel = Read-Host -Prompt "Do you want to delete the folders after conversion [Y/N]: "
# Get the files inside the INPUT path and forEach children
Get-ChildItem "$i" | ForEach-Object {
# Get the full path of the file
$pathFolder = $_.FullName
# If the path here is a folder
if (Test-Path -Path "$pathFolder" -PathType Container) {
# Set the alias
Set-Alias 7z $7zipPath
# If the user asked for deletion of folders
if ("Y" -eq $isSdel.ToUpper()) {
# Zip the content of the folder
7z a "$pathFolder.zip" "$pathFolder\*" -sdel | FIND "ing archive"
}
else {
# Zip the content of the folder
7z a "$pathFolder.zip" "$pathFolder\*" | FIND "ing archive"
}
# Add the file name into the list
$listOfZippedFiles.Add("$pathFolder.zip")
$listOfZippedNames.Add("$_.zip")
}
# If the user asked for deletion of folders
if ("Y" -eq $isSdel) {
# Remove the now blank folders
if( $_.psiscontainer -eq $true){
if((gci $_.FullName) -eq $null){
$_.FullName | Remove-Item -Force
}
}
}
}
# For each zipped file
foreach ($file in $listOfZippedFiles) {
# Change the extension to CBZ
$dest = [System.IO.Path]::ChangeExtension("$file",".cbz")
Move-Item -Path "$file" -Destination $dest -Force
}
# Write for the user
Write-Host "`r`n`r`nConverted:"
# Displaying the converted files by their names
foreach ($file in $listOfZippedNames) {
$newName = [System.IO.Path]::ChangeExtension("$file",".cbz")
Write-Host "-- $newName"
}
# Blank line
Write-Host ""
# Pause to let us see the result
Pause
Output
output
As we can see, the folder is sucessfully created AND without loops like : I have ZIP files in the root folder of the script and they are also renamed into CBZ ones (I had this loop for my batch script).
I also added the choice to automatically delete the converted folders OR not.
Obviously, there's room for improvements (especially in how we delete the folders). I'll gladly take any advice.
UPDATE
I updated my script and it's much better. Less instructions, a list (in the prompt) that update itself when each folder is really converted. So no more: 1) ZIP all folders 2) rename their extension.
So a code that's more logic and also useful to show a beautiful process in real time.
Here's the updated code :
Clear-Host
# ROOT - Script folder path
$root = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
# 7Zip path
$7zipPath = "$env:ProgramFiles\7-Zip\7z.exe"
# Test if 7zip is installed
if (-not (Test-Path -Path $7zipPath -PathType Leaf)) {
throw "7 zip file '$7zipPath' not found"
}
# Ask the user if we delete the folders after their conversion
$isSdel = Read-Host -Prompt "Do you want to delete the folders after conversion [Y/N]: "
# Write for the user
Write-Host "`r`nConverted:"
# Get the files inside the INPUT path and forEach children
Get-ChildItem "$root" | ForEach-Object {
# Get the full path of the file
$pathFolder = $_.FullName
# If the path here is a folder
if (Test-Path -Path "$pathFolder" -PathType Container) {
# If the user asked for deletion of folders
if ("Y" -eq $isSdel.ToUpper()) {
# Zip the content of the folder while deleting the files zipped
& $7zipPath a "$pathFolder.zip" "$pathFolder\*" -sdel > $null
# Remove the now blank folder
if( $_.psiscontainer -eq $true){
if((gci $_.FullName) -eq $null){
$_.FullName | Remove-Item -Force
}
}
}
else {
# Zip the content of the folder
& $7zipPath a "$pathFolder.zip" "$pathFolder\*" > $null
}
# Change the extension to CBZ
$newName = [System.IO.Path]::ChangeExtension("$pathFolder.zip",".cbz")
Move-Item -Path "$pathFolder.zip" -Destination $newName -Force
# Tells the user this one is finished converting
Write-Host "--" -ForegroundColor DarkGray -NoNewline
Write-Host " $_.cbz"
}
}
# Tells the user it's finished
Write-Host "`r`nFinished`r`n" -ForegroundColor Green
# Pause to let us see the result
Pause
UPDATE 2
I made a GitHub project for this one. The URL is here:
https://github.com/PonyLucky/CBZ-Manga-Creator.
I currently have a CSV which contains 1 column that lists many file FullNames. (ie. "\\server\sub\folder\file.ext").
I am attempting to import this CSV, move the file to a separate location and append a GUID to the beginning of the filename in the new location (ie GUID_File.ext). I've been able to move the files, generate the GUID_ but haven't been able to store and reuse the existing filename.ext, it just gets cut off and the file ends up just being a GUID_. I just am not sure how to store the existing filename for reuse.
$Doc = Import-CSV C:\Temp\scripttest.csv
ForEach ($line in $Doc)
{
$FileBase = $Line.basename
$FileExt = $Line.extension
Copy-Item -path $line.File -Destination "\\Server\Folder\$((new-guid).guid.replace('-',''))_$($Filebase)$($FileExt)"
}
If possible, I'm going to also need to store and place all the new GUID_File.ext back into a CSV and store any errors to another file.
I currently have a CSV which contains 1 column that lists many file FullNames. (ie. "\server\sub\folder\file.ext").
This isn't a CSV. It's just a plaintext file with a list.
Here's how you can accomplish your goal, however:
foreach ($path in (Get-Content -Path C:\Temp\scripttest.csv))
{
$file = [System.IO.FileInfo]$path
$prefix = (New-Guid).Guid -replace '-'
Copy-Item -Path $file.FullName -Destination "\\Server\Folder\${prefix}_$file"
}
This will take your list, convert the item into a FileInfo type it can work with, and do the rest of your logic.
Based on:
$FileBase = $line.basename
$FileExt = $line.extension
it sounds like you mistakenly think that the $line instances representing the objects returned from Import-Csv C:\Temp\scripttest.csv are [System.IO.FileInfo] instances, but they're not:
What Import-Csv outputs are [pscustomobject] instances whose properties reflect the column values of the input CSV, and the values of these properties are invariably strings.
You must therefore use $line.<column1Name> to refer to the column containing the full filenames, where <column1Name> is the name defined for the column of interest in the header line (the 1st line) of the input CSV file.
If the CSV file has no header line, you can specify the column names by passing an array of column names to Import-Csv's -Header parameter, e.g.,
Import-Csv -Header Path, OtherCol1, OtherCol2, ... C:\Temp\scripttest.csv
I'll assume that the column of interest is named Path in the following solution:
$Doc = Import-Csv C:\Temp\scripttest.csv
ForEach ($rowObject in $Doc)
{
$fileName = Split-Path -Leaf $rowObject.Path
Copy-Item -Path $rowObject.Path `
-Destination "\\Server\Folder\$((new-guid).guid.replace('-',''))_$fileName"
}
Note how Split-Path -Leaf is used to extract the filename, including extension, from the full input path.
If I read your question carefully, you want to:
copy the files listed in the CSV file in the 'File' column.
the new files should have a GUID prepended to the filename
you need a new CSV file where the new filenames are stored for later reference
you want to track any errors and write those to a (log) file
Assuming you have an input CSV file looking something like this:
File,Author,MoreStuff
\\server\sub\folder\file.ext,Someone,Blah
\\server\sub\folder\file2.ext,Someone Else,Blah2
\\server\sub\folder\file3.ext,Same Someone,Blah3
Then below script does hopefully what you want.
It creates new filenames by prepending them with a GUID and copies the files in the CSV listed in column File to some destination path.
It outputs a new CSV file in the destination folder like this:
OriginalFile,NewFile
\\server\sub\folder\file.ext,\\anotherserver\sub\folder\38f7bec9e4c0443081b385277a9d253d_file.ext
\\server\sub\folder\file2.ext,\\anotherserver\sub\folder\d19546f7a3284ccb995e5ea27db2c034_file2.ext
\\server\sub\folder\file3.ext,\\anotherserver\sub\folder\edd6d35006ac46e294aaa25526ec5033_file3.ext
Any errors are listed in a log file (also in the destination folder).
$Destination = '\\Server\Folder'
$ResultsFile = Join-Path $Destination 'Copy_Results.csv'
$Logfile = Join-Path $Destination 'Copy_Errors.log'
$Doc = Import-CSV C:\Temp\scripttest.csv
# create an array to store the copy results in
$result = #()
# loop through the csv data using only the column called 'File'
ForEach ($fileName in $Doc.File) {
# check if the given file exists; if not then write to the errors log file
if (Test-Path -Path $fileName -PathType Leaf) {
$oldBaseName = Split-Path -Path $fileName.Path -Leaf
# or do $oldBaseName = [System.IO.Path]::GetFileName($fileName)
$newBaseName = "{0}_{1}" -f $((New-Guid).toString("N")), $oldBaseName
# (New-Guid).toString("N") returns the Guid without hyphens, same as (New-Guid).Guid.Replace('-','')
$destinationFile = Join-Path $Destination $newBaseName
try {
Copy-Item -Path $fileName -Destination $destinationFile -Force -ErrorAction Stop
# add an object to the results array to store the original filename and the full filename of the copy
$result += New-Object -TypeName PSObject -Property #{
'OriginalFile' = $fileName
'NewFile' = $destinationFile
}
}
catch {
Write-Error "Could not copy file to '$destinationFile'"
# write the error to the log file
Add-content $Logfile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - ERROR: Could not copy file to '$destinationFile'"
}
}
else {
Write-Warning "File '$fileName' does not exist"
# write the error to the log file
Add-content $Logfile -Value "$((Get-Date).ToString("yyyy-MM-dd HH:mm:ss")) - WARNING: File '$fileName' does not exist"
}
}
# finally create a CSV with the results of this copy.
# the CSV will have two headers 'OriginalFile' and 'NewFile'
$result | Export-Csv -Path $ResultsFile -NoTypeInformation -Force
Thank you to everyone for the solutions. All of them worked and worked well. I chose Theo as the answer for the fact that his solution solved the error logging and stored all the new renamed files with GUID_File.ext new to the existing CSV info.
Thank you all.
i am trying to loop through all files no matter the type, in a folder, and change a string with one that is input by the user..
i can do this now, with the code below, but only with one type of file extension..
This is my code:
$NewString = Read-Host -Prompt 'Input New Name Please'
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$InputFiles = Get-Item "$scriptPath\*.md"
$OldString = 'SolutionName'
$InputFiles | ForEach {
(Get-Content -Path $_.FullName).Replace($OldString,$NewString) | Set-Content -Path $_.FullName
}
echo 'Complete'
How do i loop through the files, no matter the extension ?
so no matter if it is a md, txt or cshtml or some other, it will replace the string as instructed.
To get all the files in a folder you can get use Get-ChildItem. Add the -Recurse switch to also include files inside of sub-folders.
E.g. you could rewrite your script like this
$path = 'c:\tmp\test'
$NewString = Read-Host -Prompt 'Input New Name Please'
$OldString = 'SolutionName'
Get-ChildItem -Path $path | where {!$_.PsIsContainer} | foreach { (Get-Content $_).Replace($OldString,$NewString) | Set-Content -Path $_.FullName }
this will first get all the files from inside the folder defined in $path, then replace the value given in $OldString with what the user entered in when prompted and finally save the files.
Note: the scripts doesn't make any difference regarding if the content of the files changed or not. This will cause all files modified date to get updated. If this information is important to you then you need to add a check to see if the files contains the $OldString before changing them and saving.
The folder structure is:
--root
--root\source-code\
--root\powershell-scripts\
I need the method below that is inside the \powershell-scripts folder to target files inside \source-code:
function Test($param)
{
dir -Include ASourceCodeFile.txt -Recurse |
% { SomeMethod $_ $param }
}
What am I missing?
The $PSScriptRoot automatic variable contains the path of the directory in which the current script is located. Use Split-Path to find its parent (your --root) and Join-Path to get the path to the source-code folder:
Join-Path -Path (Split-Path $PSScriptRoot -Parent) -ChildPath 'source-code'
$PSScriptRoot was introduced in PowerShell 3.0
A bit late, but maybe still helpful for someone:
Directory structure :
MyRoot\script\scriptrunning.ps1
config:
MyRoot\config.xml
to read the xml file from scriptrunning.ps1:
[xml]$Config = Get-Content -path "${PSScriptRoot}\..\config\config.xml"
if you have a script in --root\powershell-scripts\ and you want to reference something in --root\source-code\ or say get-content you can do this:
cd --root\powershell-scripts\
get-content '..\source-code\someFile.txt'
The ..\ references the parent directory which contains \source-code\ and then you reference or pull in file or scripts from that directory.
this was a trick that I used in vbs that I converted to PS...
$scriptPath = Split-Path $MyInvocation.MyCommand.Path -Parent
$a = $scriptPath.split("``\``") for ($i = 0 ; $i -lt $a.count-1 ; $i++){
$parentDir = $parentDir + $a[$i] <br>
if($i -lt $a.count-2){$parentDir = $parentDir + "``\``"}
}
Write-Output $parentDir
I am using a Powershell Script which should create a file that includes the Directory-Order (folder, subfolder, files, etc.):
$path = "golf.de/dgv"
Get-ChildItem -Path $folder -recurse | sort Directory, Name| format-Table -auto $path, Directory, Name | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
until now the output looks like this
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei1.txt
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei2.txt
C:\Users\J.Kammermeier\Desktop\Johannes\Sonstige Datein\Musik WACKEN.txt
but I need it in this order:
.../Johannes Test-Datei1.txt
...Johannes\Sonstige Datein\Musik WACKEN.txt
How to achieve it?
You'll have to mangle the Directory property a bit, using Select-Object and calculated properties:
# Set the path and folder property
$path = "golf.de/dgv"
$folder = "C:\Users\J.Kammermeier\Desktop\Johannes"
# Get the name of the parent folder (the part we want to remove)
$basePath = (Get-Item $folder).Parent.FullName
# Retrieve the files
$files = Get-ChildItem -Path $folder -Recurse
# Select the Name property and then two calculated properties, "Directory" and "Path"
$files = $files |Select-Object #{Name="BaseURL";Expression={"$path"}},
#{Name="Directory";Expression={$_.Directory.FullName.Substring($basePath.Length - 1)}},
Name
# Sort them
$files = $files |Sort-Object Directory, Name
# Formatted output to file
$files | Format-Table -AutoSize | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
From the details, I guess that you're trying to audit the files for a website, you could combine the Path and Directory properties and fix the back slashes with -replace:
#{Name="URLPath";Expression={"$path/" + $($_.Directory.FullName.Substring($basePath.Length - 1) -replace "\\","/")}}