Positional parameter cannot be found - powershell

I'm not sure why I'm getting the following error
Copy-Item : A positional parameter cannot be found that accepts argument 'C:\Code\PS\Auths\2.jpg'.
At C:\Code\PS\auth-grab.ps1:9 char:12
C:\Code\PS\Auths\2.jpg is the correct path.
(I'm getting one of these for each of the items in the pipeline)
When I echo $rv I get the correct pathand $_ should be right. Where am I going wrong?
oops script below :
function Generate-FileName($fi)
{
$rv = "C:\Code\PS\New\"+ $fi.Name
#echo rv
}
Get-ChildItem Auths\*.* -include 1.jpg, 2.jpg |
ForEach-Object {
Copy-Item $_ -destination Generate-FileName(Get-ChildItem $_)
}
Note if i echo $rv I get the path I want

Wrap the function - Generate-FileName in brackets like this -
ForEach-Object {
Copy-Item $_ -destination (Generate-FileName(Get-ChildItem $_))
}
Enclosing it in parenthesis forces the expression.
OR
Copy the return value of the function in a variable and use the variable in the Copy-Item as below -
function Generate-FileName($fi)
{
"C:\Code\PS\New\"+ $fi.Name
}
Get-ChildItem Auths\*.* -include 1.jpg, 2.jpg |
ForEach-Object {
$destination = Generate-FileName(Get-ChildItem $_)
Copy-Item $_ -destination $destination
}

I think your function Generate-FileName doesn't return anything.
I think your script generate this line with copy-item:
Copy-Item C:\Code\PS\Auths\2.jpg -destination
Try it like this:
function Generate-FileName($fi)
{
$rv = "C:\Code\PS\New\"+ $fi.Name
return $rv # here is the change
}

Related

Powershell - Rename files preserving part of the name + checksum

I have some long filenames that potensially can cause issues with Windows path max chars, and I would like to rename them preserving part of it - and also adding a RNG 4 letter/digit combination.
Filename example: 478432_1400_79834_SomeKindofText_UserInputSoItCanBeReallyLongCombinedWithANetworkPath.jpg
Wanted rename outcome:
478432_1400_79834_SomeKindofText_abc1.jpg
Where 'abc1' represents the 4 letter/digit combination of a checksum
This is the code I have so far:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.substring(0,3)
Rename-Item -NewName { $search_folder+$_.BaseName.Split('_')[0..3] + $checksum + $_.Extension }
}
My first problem is that Get-FileHash does not support substring method, generating a error message:
Method invocation failed because [Microsoft.Powershell.Utility.FileHash] does not contain a method named 'substring'.
My second problem is that it tries to do a Resolve-Path in my current PS shell directory instead of $search_folder
My third problem is that the underscores in the filename is not preserved, so a -WhatIf tag on the Rename-Item method yields a result like "478432 1400 79834 SomeKindofText"
Tips or suggestions would be most welcomed!
My first problem is that Get-FileHash does not support substring method, generating a error message:
Method invocation failed because [Microsoft.Powershell.Utility.FileHash] does not contain a method named 'substring'.
$checksum does not store the hash string, it stores an object that has a property named Hash, which in turn stores the string you want, so change this line:
$checksum = $checksum.substring(0,3)
To:
$checksum = $checksum.Hash.Substring(0,3)
My second problem is that it tries to do a Resolve-Path in my current PS shell directory instead of $search_folder
Two general solutions to this problem:
Pass the absolute path to the file explicitly:
Rename-Item -LiteralPath $_.FullName -NewName { ... }
Or pipe the output from Get-ChildItem directly to Rename-Item and let PowerShell bind the path correctly:
$_ |Rename-Item -NewName { ... }
My third problem is that the underscores in the filename is not preserved, so a -WhatIf tag on the Rename-Item method yields a result like "478432 1400 79834 SomeKindofText"
Splitting a string on '_' will also remove the underscores - to reverse this, use '_' as a delimiter in a -join operation:
$firstFourPartsOfBaseName = $_.BaseName.Split('_')[0..3] -join '_'
Putting this all together, we get:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.hash.substring(0,3)
$_ |Rename-Item -NewName {
# calculate new base name ("478432_1400_79834_SomeKindofText_abc1")
$newBasename = #($_.BaseName.Split('_')[0..3]; $checksum) -join ''
# add extension and output
$newBasename,$_.Extension -join '.'
}
}
Please see your script adjusted below:
$search_folder = "C:\PS\Test\"
Get-ChildItem $search_folder -File | ForEach-Object {
$checksum = Get-FileHash -Path $_
$checksum = $checksum.hash.substring(0,3)
Rename-Item -Path $_ -NewName ($search_folder + $_.BaseName.Split('_')[0..3] + $checksum + $_.Extension)
}
You were trying to get a sub string of an object, using the property hash on $Checksum will allow you to create a substring.
I have also added -path to the rename-item request and changed the parenthesis on the string construction (it could be either of these that were causing you an issue, unfortunately I haven't tested this.)

Powershell 5.1.17763.1007 How to execute a string-command stored in a variable (Copy-Item)

I have a simple tasks that doesnt work:
$Copy = copy-item -path "C:\Folder 0" -destination "C:\Folder $x\" -recurse
for($x=1; $x -le 9;$x++)
{
$Copy
}
I cannot execute the command in the variable $Copy, when I run the loop it just prints $Copy to the console as the variable seems to be empty.
I tried Invoke-Expression, & $Copy, putting the Copy-Item command under "", but that doesnt work here...
Any advice for a beginner?
Thanks in advance!
As explained in the comments, Copy-Item doesn't return anything by default, so the value of $copy is $null.
From your clarifying comment:
I wanted actually just to store the command
If you want an executable block of code that you can invoke later, you might want to define a [scriptblock]. Scriptblock literals in PowerShell are easy, simply wrap your code in {}, and optionally supply parameter definitions:
$CopyCommand = {
param([string]$X)
Copy-Item -Path "C:\Folder 0" -Destination "C:\Folder $X\" -Recurse
}
& $CopyCommand 1
# later in the script
& $CopyCommand 2
or you can define a function with the same:
function Copy-MyFolderTree
{
param([string]$X)
Copy-Item -Path "C:\Folder 0" -Destination "C:\Folder $X\" -Recurse
}
Copy-MyFolderTree -X 1
# later in the script
Copy-MyFolderTree -X 2

How to check all strings before doing the next step

I want to perform string-comparison. I use a foreach loop, but I want to do move-item after all strings have been compared.
My concept is " If all of strings not match, do move-item. "
How can I do that?
here is my present code but it is " if match,do move-item
$Myfile=dir *my file name*
$directory=dir D:\example (there are many directory)
foreach($dir in $directory){
if($dir match $Myfile.basename ){
move-item $Myfile.fullname -destination D:\example
}
}
I want to do move-item after string comparison to ensure that my file name doesn't exist $directory
here is what I want to achieve
if( ("all of the directory Name") -notmatch $myfile.basename ){
move-item XXX to XXX
}
if I use -notmatch , it will do move-item at the first time loop. So I need to do move-item after checking them all...
You can set a variable to true if something matches:
$Myfile=dir *my file name*
$directory=dir D:\example (there are many directory)
$move = $false
foreach($dir in $directory){
if($dir match $Myfile.basename ){
$move = $true
}
}
if ($move){
move-item $Myfile.fullname -destination D:\example
}
You can try using notcontains operator to check the file base name appears in the directory files :
$myfile = get-childitem "filepath"
$directory = #(get-childitem "D:\Example" | foreach {$_.basename})
if ($directory -notcontains $myfile.basename) {
move-item xxx to xxx
}

How can I select a particular function to run based on a filename using Powershell?

I have a function that will change the filename and move it to a specific computer. The function is duplicated for another computer, so I now have two specific functions for each filename. I can't seem to figure out how to get the script to choose the function I want it to use. Here is what I've tried with no luck:
$files = dir -Path \\server\PPTV\*\*.* -Include atrium.*, clirel.* -Recurse {
(If ($file.FullName -eq "atrium.*" | Rename-Copy-Atrium),
(If ($file.FullName -eq "clirel.*" | Rename-Copy-Clirel)))
}
How can I get this to pipe to the function and run? Rename-Copy-Atrium for example is one of the function names.
The proper syntax for an if() statement in powershell is:
if([condition])
{
[execute this code]
}
So you would want to do something like:
$files = dir -Path \\server\PPTV\*\*.* -Include atrium.*, clirel.* -Recurse
foreach($file in $files)
{
if($file.FullName -like "atrium.*")
{
# I assume the function takes a fileinfo object as a parameter argument
Rename-Copy-Atrium $file
}
elseif($file.FullName -like "clirel.*")
{
Rename-Copy-Clirel $file
}
}

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.