I have an XMLs with a mask XXXXX-sell-XXXXX.xml, code is:
<?xml version="1.0" encoding="utf-8"?>
<document xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<id>document-20210301-sell</id>
<number>1</number>
<type>a1</type>
<id>document-20210301-sell</id>
<number>2</number>
<type>a1</type>
.....
</document>
I want to copy the newest XML to another folder adding date taken from XML in filename with a mask:
yyyymmdd-sell.xml
My code is not doing a job:
$path1="C:\sell\"
$path2="c:\sell\in\"
Get-ChildItem -Path $path1 -Filter "*-sell-*" |
Where-Object { -not $_.PSIsContainer } |
Sort-Object -Property CreationTime |
Select-Object -Last 1 |
[xml]$xml = Get-Content
$date = $xml.document | Where-Object {$_.id -like "*20210301*"}
Copy-Item -Destination $path2 -PassThru |
Rename-Item -NewName {$Date + "-sell".xml"}
Welcome to StackOverflow, mcq. This might work, sometimes pipes make things complicated:
$path1="C:\sell\"
$path2="c:\sell\in\"
$f = Get-ChildItem -Path $path1 -Filter "*-sell-*" |
Where-Object { -not $_.PSIsContainer } |
Sort-Object -Property CreationTime |
Select-Object -Last 1
[xml]$xml = Get-Content $f
$date = $xml.document.id[0]
#
# get date like yyyymmdd:
#
if($date -match "\d+") {
$date = $matches[0];
}
#
# copy and rename:
#
Copy-Item $f -Destination $path2 -PassThru |
Rename-Item -NewName ($date + "-sell.xml")
With $date -match "\d+", \d is regex (Regular Expression) symbol representing any digit in [0-9] (from 0 to 9), "+" means more than 1 digit, in our case 8 for yyyymmdd numerical value, we capture the date substring in .id value, such as 20210301, 20190925, ...
Please also consult https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1 to start.
Related
im quiet new to powerhsell and I have the following goal:
My code is supposed to loop through selected subfolders and compare those. The names of the subfolders are identical in both parent folders, however the path before those selected folders are different: C:\temp\parentF1\BackUp* and C:\temp\parentF2\BackUp*
The problem that I have is that even tho I think my $vars that I use for the comparison should have a value, are NULL and I cant think of why!
$path = "subfolder1","subfolder2","subfolder3"
$excludeF1 = #(C:\temp\parentF1\BackUp\*\subfolder5)
$excludeF2 = #(C:\temp\parentF2\BackUp\*\subfolder5)
$x = 0
while($x -lt $path.Count){
$F1 = Get-ChildItem -Recurse -Path "C:\temp\parentF1\BackUp\$path[$x]" |
Where-Object {$_.FullName -notlike $excludeF1}
$F2 = Get-ChildItem -Recurse -Path "C:\temp\parentF2\BackUp\$path[$x]" |
Where-Object {$_.FullName -notlike $excludeF2}
Compare-Object -ref $F1 -dif $F2 |
Select-Object #{Label="$path[$x]";e={$_.InputObject}},`
#{n="Fundort";e={if($_.SideIndicator -like "=>") {write-output "BackUp F1"}`
elseif($_.SideIndicator -like "<="){Write-Output "BackUp F2"}}} | Out-File dif.txt
$x++
}
start .\dif.txt
also the out-file cmdlet doesnt work but that`s a dif topic
Thanks for any help in advance
This does not work as you expect:
Get-ChildItem ... -Path "C:\temp\parentF1\BackUp\$path[$x]"
When using -Path, PowerShell interprets [...] as part of its own wildcard syntax. Use -LiteralPath to prevent that.
Anything more complex than simple variable name ($var) must be enclosed in a subexpression $(...).
Similar issue:
Select-Object #{Label="$path[$x]"; ...
This can be solved by simply removing the quotation, as $path already contains strings.
Solution:
$F1 = Get-ChildItem -Recurse -LiteralPath "C:\temp\parentF1\BackUp\$($path[$x])" | ...
$F2 = Get-ChildItem -Recurse -LiteralPath "C:\temp\parentF2\BackUp\$($path[$x])" | ...
Compare-Object ... |
Select-Object #{Label=$path[$x]; ...
The above fixes your current code, but your code could be simplified like this to avoid the subexpressions:
foreach($currentPath in $path) {
$F1 = Get-ChildItem -Recurse -Path "C:\temp\parentF1\BackUp\$currentPath" |
Where-Object FullName -notlike $excludeF1
$F2 = Get-ChildItem -Recurse -Path "C:\temp\parentF2\BackUp\$currentPath" |
Where-Object FullName -notlike $excludeF2
Compare-Object -ref $F1 -dif $F2 |
Select-Object #{Label=$currentPath;e={$_.InputObject}},`
#{n="Fundort";e={if($_.SideIndicator -like "=>") {write-output "BackUp F1"}`
elseif($_.SideIndicator -like "<="){Write-Output "BackUp F2"}}} | Out-File dif.txt
}
I want to filter lines according to specific word from file in powershell.
For example: the files animal1.txt and animal2.txt. Every file contain lines
dog
cat
dog
dog
bird
Then I want to create two derived files:
animal1_bak.txt that stores lines which contains the word 'dog' from animal1.txt
animal2_bak.txt that stores lines which contains the word 'dog' from animal2.txt
What I found on web is:
Select-String -Path "*.*" -Pattern "dog"
But the instruction to create the derived word is missing.
What can I do?
You can first get-content and use set-content like below
Get-Content -Path E:\KTDocs\Scripts\animal1.txt | where {
$_ -like '*dog*'} |Set-Content e:\animalbak.txt
try Something like this
select-string -Path "c:\temp\animal*.txt" -Pattern "dog" | Group Path | %{
$FileName="{0}_bak.txt" -f $_.Name
$_.Group.Line | select -unique | Out-File $FileName -Append
}
$folderpath = "D:\AnimalFolder" # your folder path here
$Allfiles = Get-ChildItem -Path $folderpath -Recurse -File -Force -ErrorAction SilentlyContinue |where{$_.Name -match ".txt"} |Select-Object -expandproperty FullName
foreach($filepath in $allfiles)
{
$Data = get-content $filepath
foreach($line in $data)
{
if($line -match "dog")
{
$newpath = $filepath.split('.')[0]
$getfullpath = $newpath + "_bak.txt"
$line | out-file $getfullpath -append
}
}
}
I have a series of files in a directory with the following format:
file_ddMMyyyyhhttss.csv
eg:
myfile_151220171038.csv
myfile_301120171445.csv
myfile_121020161114.csv
I know how to select the latest by LastWriteTime:
gci "$pathtofile" | Sort LastWriteTime | Select -Last 1
but unsure how to split the "datestamp" in the file and then to sort by year, month and then date, in order to determine the latest file. Any suggestions?
Well, the specific format in your file names prevents any useful sorting without parsing it, but you can do just that:
Get-ChildItem $pathtofile |
ForEach-Object {
# isolate the timestamp
$time = $_ -replace '.*(\d{12}).*','$1'
# parse
$timestamp = [DateTime]::ParseExact($time, 'ddMMyyyyHHmm', $null)
# add to the objects so we can sort
$_ | Add-Member -PassThru NoteProperty Timestamp $timestamp
} |
Sort-Object Timestamp
Adjust to fit your exact date/time format, because the one you specified in your question does not match the one on your files.
My proposition ;)
Only take file, only file with format asked, Only print file with date when date are really date.
[System.DateTime]$parsedDate=get-date
Get-ChildItem "c:\temp\" -file -filter "*.csv" | where BaseName -match ".*(\d{12}).*" | %{
$DtString=$_.BaseName.substring($_.BaseName.Length - 12)
if ([DateTime]::TryParseExact($DtString, "ddMMyyyyhhmm",$null,[System.Globalization.DateTimeStyles]::None,[ref]$parseddate))
{
$_ | Add-Member -Name TimeInName -Value $parseddate -MemberType NoteProperty -PassThru
}
} | Sort TimeInName -Descending | Select -First 1
The Sort-Object cmdlet can work not only with regular but also with calculated properties. That allows you to sort the files without passing them through a ForEach-Object loop first. Like this:
$pattern = '.*_(\d{14}).*'
$datefmt = 'ddMMyyyyHHmmss'
$culture = [Globalization.CultureInfo]::InvariantCulture
Get-ChildItem $pathtofile | Sort-Object #{n='Timestamp';e={
$datestr = $_.Basename -replace $pattern, '$1'
[DateTime]::ParseExact($datestr, $datefmt, $culture)
}} | Select-Object -Last 1
I usually recommend using the InvariantCulture constant rather than $null as the third argument for ParseExact(), because using $null gave me errors in some cases.
If your source directory contains files that don't match your filename pattern you may want to exclude them with a Where-Object filter before sorting the rest:
Get-ChildItem $pathtofile | Where-Object {
$_.Basename -match $pattern
} | Sort-Object #{n='Timestamp';e={
$datestr = $_.Basename -replace $pattern, '$1'
[DateTime]::ParseExact($datestr, $datefmt, $culture)
}} | Select-Object -Last 1
I'm having ongoing trouble with a script I've written that is (supposed) to do the following.
I have one folder with a number of csv files, and I want to copy the latest file with the company name into another folder, and rename it.
It is in the current format:
21Feb17070051_CompanyName_Sent21022017
I want it in the following format:
CompanyName21022017
So I have the following powershell script to do this:
## Declare variables ##
$DateStamp = get-date -uformat "%Y%m%d"
$csv_dest = "C:\Dest"
$csv_path = "C:\Location"
## Copy latest Company CSV file ##
get-childitem -path $csv_path -Filter "*Company*.csv" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 |
copy-item -Destination $csv_dest
## Rename the file that has been moved ##
get-childitem -path $csv_dest -Filter "*Company*.csv" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 | rename-item $file -NewName {"Company" + $DateStamp + ".csv"}
The file seems to copy ok, but the rename fails -
Rename-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Powershell Scripts\MoveCompanyFiles.ps1:20 char:41
+ select-object -last 1 | rename-item $file -NewName {"CompanyName" + $DateSt ...
I think it is something to do with the order in which powershell works, or the fact it can't see the .csv in the $file variable. There are other files (text files, batch files) in the destination, in case that affects things.
Any help in where I'm going wrong would be appreciated.
As wOxxOm answered, you need to remove $file from Rename-Item as it is not defined and the cmdlet already receives the inputobject through the pipeline.
I would also suggest that you combine the two operations by passing through the fileinfo-object for the copied file to Rename-Item. Ex:
## Declare variables ##
$DateStamp = get-date -uformat "%Y%m%d"
$csv_dest = "C:\Dest"
$csv_path = "C:\Location"
## Copy and rename latest Company CSV file ##
Get-ChildItem -Path $csv_path -Filter "*Company*.csv" |
Where-Object { -not $_.PSIsContainer } |
Sort-Object -Property CreationTime |
Select-Object -Last 1 |
Copy-Item -Destination $csv_dest -PassThru |
Rename-Item -NewName {"Company" + $DateStamp + ".csv"}
You can rename and copy in a single command. Just use Copy-Item Command and give new path and name as -Destination parameter value. It will copy and rename the file. You can find an example below.
$source_path = "c:\devops\test"
$destination_path = "c:\devops\test\"
$file_name_pattern = "*.nupkg"
get-childitem -path $source_path -Filter $file_name_pattern |
Copy-Item -Destination { $destination_path + $_.Name.Split("-")[0] + ".nupkg"}
I am trying to create a CSV file for a file share that has deep folder structure.
I want the CSV to look like:
filename, filepath, file type, folderStructure
So far I have the following:
#Get-ChildItem -Path D:\ -Recurse
$directory="d:\"
gci $directory -recurse |
where {$_.psiscontainer} |
foreach {
get-childitem $_.fullname |
sort creationtime |
select -expand fullname -last 1
}
You don't need to recurse a recursive in Powershell. It will automatically go through all of the subdirectories of subdirectories.
I am also a little unsure of some of the information you wanted, but here is a script that does what you want mostly I believe and is IMO a little better to read.
Get-ChildItem -Path X:\Test -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv X:\Test\Results.csv -NoTypeInformation
You will need to change your path, as I created this for a test. My results look like this in Excel:
+-------------------------------+----------------+-------------------------------------+-----------+
| Name | Created | filePath | Extension |
+-------------------------------+----------------+-------------------------------------+-----------+
| test2 | 3/6/2013 19:21 | X:\Test\test2 | Folder |
| Results.csv | 3/6/2013 19:51 | X:\Test\Results.csv | .csv |
| test3 | 3/6/2013 19:21 | X:\Test\test2\test3 | Folder |
| New Text Document.txt | 3/6/2013 19:21 | X:\Test\test2\New Text Document.txt | .txt |
+-------------------------------+----------------+-------------------------------------+-----------+
Where it says "Folder" for the Extension just it returning that it is a directory instead of a blank (No extension).
OK, I changed the way it checks for the parent. It is no longer looking directly at the Parent attribute, and it should correct it now.
Get-ChildItem -Path D:\ -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$ParentS = ($_.Fullname).split("\")
$Parent = $ParentS[#($ParentS.Length - 2)]
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="Folder Name";e={if($Parent){$Parent}else{$Parent}}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv X:\Test\ResultsC.csv -NoTypeInformation
It is now taking the path to the current item, turning it into an array by splitting on the \, and then giving you the value at ArryLength - 2
I am not sure if this is the best approach but it works. I have a feeling code could be shorten to get the full path of a file.
$myDirectory = "D:\"
Get-ChildItem -Path $myDirectory -Recurse |`
foreach{
$Item = $_
$Type = $_.Extension
$Path = $_.FullName
$ParentS = ($_.Fullname).split("\")
$Parent = $ParentS[#($ParentS.Length - 2)]
$ParentPath = $_.PSParentPath
$ParentPathSplit = ($_.PSParentPath).split("::")
$ParentPathFinal = $ParentPathSplit[#($ParentPathSplit.Length -1)]
#$ParentPath = [io.path]::GetDirectoryName($myDirectory)
$Folder = $_.PSIsContainer
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="Folder Name";e={if($Parent){$Parent}else{$Parent}}},`
#{n="filePath";e={$Path}},`
#{n="Extension";e={if($Folder){"Folder"}else{$Type}}},`
#{n="Folder Name 2";e={if($Parent){$Parent}else{$Parent}}},`
##{n="Folder Path";e={$ParentPath}},`
#{n="Folder Path 2";e={$ParentPathFinal}}`
}| Export-Csv d:\ResultsC_2_F.csv -NoTypeInformation