powershell wrong results for file length - powershell

I have a file with length of 309247 bytes as this image:
If I run this powershell script:
(Get-Item 'C:\TestFile.082').length
309247
But if run the same script but a $file variable it returns this:
$fpath = Get-Item $file.FullName
$fpath.length
309800
# or
(Get-Item $file.FullName).length
309800
# or
$file.length
309800
NOTE $file is a variable into a loop for all files contained in a directory
Why if I'm referencing the same file the results are different?
How can I do to get the real value (309247)?
This is my Full Code
$filesToFtp = Get-Item $pathToFtp -Exclude "*.PreARM"
foreach($file in $filesToFtp)
{
$fpath = Get-Item $file.FullName
$fpath.Length
$FileNode = $oXMLDocumentFiles.CreateElement("element","curtmpremotelxml_files","")
$nodoXML = "<versionnum>"+$versplain+"</versionnum>"+
"<filename>"+$file.Name.ToUpper()+"</filename>"+
"<filesize>"+$file.length+".00</filesize>"+
$FileNode.InnerXml = $nodoXML
$oXMLDocumentFiles.DocumentElement.AppendChild($FileNode)
}

It looks like you aren't using the cmdlet you think you are:
foreach( $file in (Get-Item 'c:\')) { ($file.FullName).Length }
is a condensed version of your variable to fill $fPath, but what it returns is a System.IO.FileSystemInfo object, of which there is a member called FullPath, a string.
What I assume you want it
foreach( $file in (Get-ChildItem 'c:\')) { ($file.FullName).Length }
which would return an Array of filepaths of the child items (or files within the folder)
This would also let you clean up a few things
$currentFilePath = $file.FullName
$fLength = $currentFilePath.Length

Related

Retrieving file information of specific date using powershell

I have a base folder as:
D:\St\Retail\AMS\AMS\FTP-FromClient\AMS
It contains various folders of dates:
2022-04-01
2022-04-02
...
...
2022-02-02
2021-05-05
2019-04-12
And each of these folders contains own files inside the folder. So, I want to retrieve all the filename inside the folder if it has 2022-04. So if the folder has '2022-04' as the base name ,I need to retreive all the file inside the folder like '2022-04-01','2022-04-02','2022-04-03'. The way I tried is:
cls
$folerPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$files = Get-ChildItem $folerPath
[System.Collections.ArrayList]$data = #()
foreach ($f in $files) {
$a = Get-ChildItem $f.FullName
foreach ($inner in $a) {
echo $inner.FullName
$outfile = $inner.FullName -match '*2022-04*'
$datepart = $inner.FullName.split('\')[-1]
if ($outfile) {
$data.add($datepart + '\' + $inner.Name.Trim())
}
}
}
My final $data may contains like this:
2022-04-01/abc.txt
2022-04-02/cde.pdf
2022-04-03/e.xls
You can do this by first collecting the directories you want to explore and then loop over these to get the files inside.
Using a calculated property you can output in whatever format you like:
$folderPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$data = Get-ChildItem -Path $folderPath -Filter '2022-04*' -Directory | ForEach-Object {
$dir = $_.Name
(Get-ChildItem -Path $_.FullName -File |
Select-Object #{Name = 'FolderFile'; Expression = {'{0}\{1}' -f $dir, $_.Name}}).FolderFile
}
After this, $data would be a string array with this content:
2022-04-01\abc.txt
2022-04-02\cde.pdf
2022-04-03\e.xls
By using wildcards for both directory and file name, you only need a single Get-ChildItem call:
$folderPath = 'D:\St\Retail\AMS\AMS\FTP-FromClient\AMS'
$folderDate = '2022-04'
[array] $data = Get-ChildItem "$folderPath/$folderDate*/*" -File | ForEach-Object{
# Join-Path's implicit output will be captured as an array in $data.
Join-Path $_.Directory.Name $_.Name
}
$data will be an array of file paths like this:
2022-04-01\abc.txt
2022-04-02\cde.pdf
2022-04-03\e.xls
Notes:
[array] $data makes sure that the variable always contains an array. Otherwise PowerShell would output a single string value when only a single file is found. This could cause problems, e. g. when you want to iterate over $data by index, you would iterate over the characters of the single string instead.
To make this answer platform-independent I'm using forward slashes in the Get-ChildItem call which work as path separators under both Windows and *nix platforms.
Join-Path is used to make sure the output paths use the expected default path separator (either / or \) of the platform.

Powershell - Skip folder if it doesn't exist

I would like to run a PowerShell script by right-clicking a .PS1 file -> Run with PowerShell. The issue is that the $srcRoot includes three different parent directories, which one, some, or none may exist of 'C:\parentfolder5.5\web\','C:\parentfolder7.0\web\', and/or 'C:\parentfolder8.0\web\'. However, running the script directly in a PowerShell terminal seems to work for the parent folders that do exist even though there are errors that pop up for the parent folders that do not exist.
The goal would be to continue running the script based on the parent directories that do exist, which currently it looks like the .PS1 file stops after looking at the first item in the $srcRoot list. Below is the code being worked on:
$filterLists = '*overview*', '*summary*', '*home*', '*floor*', '*flr*', '*level*', '*lvl*', '*roof*', '*basement*', '*first*', '*second*', '*third*', '*fourth*'
$srcRoot = 'C:\parentfolder5.5\web\','C:\parentfolder7.0\web\','C:\parentfolder8.0\web\'
$dstRoot = $MyInvocation.MyCommand.Path
$params = #{
Path = LiteralPath = $srcRoot |Where-Object { Test-Path -LiteralPath $_ -PathType Container }
Filter = 'views'
Recurse = $true
Directory = $true
}
# All folders under `$srcRoot` with name 'views'
$viewsFolders = Get-ChildItem #params #this line is where the issue
seems to start when Right-clicking -> Run with PowerShell
$params.LiteralPath = $viewsFolders.FullName
$params.Filter = 'graphics'
# All folders under `$viewsFolders` with name 'graphics'
$graphicsFolders = Get-ChildItem #params
$params.Remove('Directory')
$params.LiteralPath = $graphicsFolders.FullName
$params.File = $true # Only search for Files
$params.Force = $true
$params.Remove('Filter')
# All files under `$graphicsFolders`
foreach($file in Get-ChildItem #params)
{
# Where the file name contains one of these filters
foreach($filter in $filterLists)
{
if($file.Name -like $filter)
{
#$file
Copy-Item -Path $($file.FullName) -Destination $dstRoot
# if you want to stop at the first finding
# add `break` here
}
}
}
Help on this would be greatly appreciated!
You can use Where-Object to filter the list of paths. Use Test-Path to test whether each exists and is a directory path:
$params = #{
LiteralPath = $srcRoot |Where-Object { Test-Path -LiteralPath $_ -PathType Container }
Filter = 'views'
Recurse = $true
Directory = $true
}
# this will only attempt directory paths that actually exist now
$viewsFolders = Get-ChildItem #params
Note: Use of LiteralPath (instead of Path) above is intentional - using Path will cause PowerShell to attempt to expand wildcards like ?, * or [abc], whereas -LiteralPath only take exact file/folder names.

powershell : command does not work when called inside a loop

The following command does work in a powershell console
Restore-SvnRepository D:\temp\Backup\foo.vsvnbak
(Restore-SvnRepository is a command that comes with visualsvn, it expects a path or unc to a file to be restored as parameter)
As I need to execute this command for a large number of files (>500) I embedded it inside a powershell loop but then it doesn't work
$fileDirectory = "D:\temp\Backup"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in Get-ChildItem $fileDirectory)
{
$filePath = $fileDirectory + "\" + $file;
# escape string for spaces
$fichier = $('"' + $filepath + '"')
# write progress status
"processing file " + $fichier
# command call
Restore-SvnRepository $fichier
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
I don't understand why this does not work. The loop and filename looks good but when executed, each command throws the following error message
Restore-SvnRepository : Parameter 'BackupPath' should be an absolute or UNC path to the repository
backup file you would like to restore: Invalid method Parameter(s) (0x8004102F)
Could you help me?
EDIT
It looks like that I was confused by Get-ChildItem that return a System.IO.FileSystemInfo rather than a string.
I didn't notice because of a implicit call to the ToString() when writing to the console that made me think I was dealing with string (rather than FSI)
The following code works
$fileDirectory = "D:\temp\Backup\"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in $files)
{
# $file is an instance of System.IO.FileSystemInfo,
# which contains a FullName property that provides the full path to the file.
$filePath = $file.FullName
Restore-SvnRepository -BackupPath $filePath
}
$file isn't a string, it's an object containing file data.
You can simplify your code as follows:
$fileDirectory = "D:\temp\Backup"
$files = Get-ChildItem $fileDirectory -Filter "*.vsvnbak"
foreach($file in $files)
{
# $file is an instance of System.IO.FileSystemInfo,
# which contains a FullName property that provides the full path to the file.
$filePath = $file.FullName
# ... your code here ...
}

Retrieve Custom Object From Hashtable

I've written a PowerShell function to create a custom object and stores it into a hashtable. The issue I'm facing is retrieving that object. I need to retrieve that object because it contains an array, I need to loop through that array and write it to a text file.
function removeItem {
<#Madatory Parameters for function, it takes the path to the files/folders
to clean up and path to the hashtable.#>
Param([Parameter(Mandatory=$True)]
[string]$path,
[string]$writetoText,
[hashtable] $hashWrite=#{}
)
<#Begin if statement: Test if Path Exists#>
if (Test-Path ($path)) {
<#Begin if statement: Check if file is Directory#>
if ((Get-Item $path) -is [System.IO.DirectoryInfo]) {
$pathObj = [pscustomobject]#{
pathName = $path
Wipe = (Get-ChildItem -Path $path -Recurse)
Count = (Get-ChildItem -Path $path -Recurse | Measure-Object).Count
}
# Write-Output $pathObj.Wipe
#Add Data to Hashtable
$hashWrite.Add($pathObj.pathName,$pathObj)
foreach ($h in $hashWrite.GetEnumerator()) {
Write-Host "$($h.Name): $($h.Value)"
}
<#
[string[]]$view = $pathObj.Wipe
for ($i=0; $i -le $view.Count; $i++){
Write-Output $view[$i]
}
#>
$pathObj.pathName = $pathObj.pathName + "*"
}<#End if statement:Check if file is Directory #>
}
}
My function takes 3 arguments, a path, the text file path, and a hashtable. Now, I create a custom object and store the path, files/folders contained in that path, and the count. Now, my issue is, I want to retrieve that custom object from my hashtable so that I can loop though the Wipe variable, because it's an array, and write it to the text file. If I print the hashtable to the screen it see the Wipe variable as System.Object[].
How do I retrieve my custom object from the hash table so I can loop through the Wipe Variable?
Possible Solution:
$pathObj = [pscustomobject]#{
pathName = $path
Wipe = (Get-ChildItem -Path $path -Recurse)
Count = (Get-ChildItem -Path $path -Recurse | Measure-Object).Count
}
#Add Data to Hashtable
$hashWrite.Add($pathObj.pathName,$pathObj)
foreach ($h in $hashWrite.GetEnumerator()) {
$read= $h.Value
[string[]]$view = $read.Wipe
for ($i=0; $i -le $view.Count; $i++) {
Write-Output $view[$i]
}
}
Is this the ideal way of doing it?
There are uses for GetEnumerator(), but in your case you're better off just looping over the keys of the hashtable:
$hashWrite.Keys | % {
$hashWrite[$_].Wipe
} | select -Expand FullName

Get-Item fails with closed pipeline error

If I have an example function ...
function foo()
{
# get a list of files matched pattern and timestamp
$fs = Get-Item -Path "C:\Temp\*.txt"
| Where-Object {$_.lastwritetime -gt "11/01/2009"}
if ( $fs -ne $null ) # $fs may be empty, check it first
{
foreach ($o in $fs)
{
# new bak file
$fBack = "C:\Temp\test\" + $o.Name + ".bak"
# Exception here Get-Item! See following msg
# Exception thrown only Get-Item cannot find any files this time.
# If there is any matched file there, it is OK
$fs1 = Get-Item -Path $fBack
....
}
}
}
The exception message is ... The WriteObject and WriteError methods cannot be called after the pipeline has been closed. Please contact Microsoft Support Services.
Basically, I cannot use Get-Item again within the function or loop to get a list of files in a different folder.
Any explanation and what is the correct way to fix it?
By the way I am using PS 1.0.
This is just a minor variation of what has already been suggested, but it uses some techniques that make the code a bit simpler ...
function foo()
{
# Get a list of files matched pattern and timestamp
$fs = #(Get-Item C:\Temp\*.txt | Where {$_.lastwritetime -gt "11/01/2009"})
foreach ($o in $fs) {
# new bak file
$fBack = "C:\Temp\test\$($o.Name).bak"
if (!(Test-Path $fBack))
{
Copy-Item $fs.Fullname $fBack
}
$fs1 = Get-Item -Path $fBack
....
}
}
For more info on the issue with foreach and scalar null values check out this blog post.
I modified the above code slightly to create the backup file, but I am able to use the Get-Item within the loop successfully, with no exceptions being thrown. My code is:
function foo()
{
# get a list of files matched pattern and timestamp
$files = Get-Item -Path "C:\Temp\*.*" | Where-Object {$_.lastwritetime -gt "11/01/2009"}
foreach ($file in $files)
{
$fileBackup = [string]::Format("{0}{1}{2}", "C:\Temp\Test\", $file.Name , ".bak")
Copy-Item $file.FullName -destination $fileBackup
# Test that backup file exists
if (!(Test-Path $fileBackup))
{
Write-Host "$fileBackup does not exist!"
}
else
{
$fs1 = Get-Item -Path $fileBackup
...
}
}
}
I am also using PowerShell 1.0.