How do i copy files starting with HYPHEN using power shell script...? - powershell

I wrote a PowerShell script that will copy files from one location to another... But few files/documents in my source location were starting with "-" in their names, they couldn't get copied to the destination..bcoz of that hyphen symbol in starting. What change can make my code to ignore the symbol HYPHEN and make my files get copied to my destination location. Files that don't have HYPHEN in starting of their names got copied successfully. I can't replace or remove these hyphen's from the doc name it has to be there and i want to copy them.
This is the code i tried so far.
**$newstreamreader = New-Object System.IO.StreamReader("Z:\RedirectedDesktop\TextDocuments\Hyphen.txt") #MY SOURCE LOCATION THAT HAVE PHYSICAL FILES PATH
$eachlinenumber = 1
while (($readeachline =$newstreamreader.ReadLine()) -ne $null)
{
$string1="Y:\"+$readeachline #LOCATION OF PHYSICAL FILES
if (Test-Path -Path $string1){
$LogFile = "Z:\RedirectedDesktop\LogFileCheckPresent.txt" #GENERATES LOG FILE IF FILE PRESENT PHYSICALLY
$Message = $readeachline+",True";
$Message >> $LogFile;
$string2="Z:\RedirectedDesktop\Sample" #DESTINATION
$string3= Split-Path $readeachline -leaf
$string1=$string1.substring(0,$string1.LastIndexOf('\'))
Write-Host "$string1"
Write-Host "$string2"
Write-Host "$string3"
$execution =robocopy $string1 $string2 $string3;
if($execution -match 'ERROR'){
$execution | out-file "Z:\RedirectedDesktop\test_logerror.txt" -Encoding ascii -Append # CREATES A ERROR LOG FILE IF THERE IS AN ERROR IN COPYING
}
else{
$execution | out-file "\Z:RedirectedDesktop\test_log1.txt" -Encoding ascii -Append # CREATES A TEST LOG FILE IF THERE IS NO ERROR IN COPYING
}
}
else{
$LogFile="Z:\RedirectedDesktop\LogFileCheckNotPresent.txt" #GENERATES LOG FILE IF FILE NOT PRESENT PHYSICALLY
$Message = $readeachline+",False";
$Message >> $LogFile;
}
Write-Host "$eachlinenumber $readeachline"
$eachlinenumber++
}
$newstreamreader.Dispose()**
**
ERROR I GOT **
ERROR : Invalid Parameter #3 : "-2 Quote wired second company india private Limited.msg"
Simple Usage :: ROBOCOPY source destination /MIR
source :: Source Directory (drive:\path or \\server\share\path).
destination :: Destination Dir (drive:\path or \\server\share\path).
/MIR :: Mirror a complete directory tree.
For more usage information run ROBOCOPY /?
**** /MIR can DELETE files as well as copy them !

Your problem is specific to robocopy.exe:
If a directory-path or file-name argument starts with - (HYPHEN-MINUS, U+002D), it is invariably interpreted as an option (such as -MIRROR - RoboCopy accepts / and - interchangeably as option prefixes) - whether that path or name is double-quoted or not.
Workaround:
For the directory path arguments (the first two): Make the paths start with a different character, for which you have two options:
Pass a full file path (which by definition doesn't start with -).
Prepend .\ to a relative path / directory name; e.g.:
For the file name argument (the third one):
There is no direct workaround I'm aware of, only a suboptimal one that runs the risk of false positives:
Replace the initial - with wildcard character ?
While this will match your --prefixed file, it may also match other files, if their name is the same except for the first character.
Applied to your case:
robocopy $string1 $string2 ('?' + $string3.Substring(1))

Related

Copying a document into a folder and then renaming it with part of the folder name

I have a large set of folders in Windows named like this:
firstname lastname_xxxxxxx_
where xxxxxxx is a numeric ID.
All these sub-folders are in a folder called "T:\Tests2022"
The directories look like this:
John Smith_12345678_
Mary Scott_87945687_
William Tell_9875348_
Jane Doe_57982388_
e.g. a complete path is T:\Tests2022\John Smith_12345678_
I have a document "testscript.txt" that I want to move into each of these folders. This file also sits in the root of "Tests2022" (i.e. T:\Tests2022\testscript.txt)
However, I would like to prepend "Firstname_" to the file. (e.g. in the first folder the file would be called "T:\Tests2022\John Smith_12345678_\John_testscript.txt").
Ideally, if the world were perfect, the file would be named "john Smith_tescript.txt")
Simply moving the file in the CMD line is easy: e.g. 'Moveit.bat' contains
for /D %%a in (".\*.*") do xcopy /y /d ".\testscript.txt" "%%a\"
I could do this manually, but I have to do something similar every two weeks with a different list of test directories so that a macro would be ideal.
I need help to move this to PowerShell and then use PowerShell to parse and collect text either to the first space (between the firstname/lastname pair) or the first underscore.
Inline comments should help to understand the logic. If you want to learn something new you should look for yourself the commands being used in the script.
This link https://regex101.com/r/h0SYVz/1 should explain this:
$_.Name -replace '(?<=_).+'
$targetFile = 'T:\Tests2022\testscript.txt'
# Enumerate all Directories in Test2022
Get-ChildItem T:\Tests2022\ -Directory | ForEach-Object {
# if this Subdirectory has the `testscript.txt` in it
if($_ | Get-ChildItem -Filter *testscript.txt) {
# go to the next Directory, nothing to do here
return
}
# if this Subdirectory doesn't have the file,
# extract everything up until the underscore for the folder name
# and concatenate with `testscript.txt`
$name = ($_.Name -replace '(?<=_).+') + 'testscript.txt'
# Join this folder's absolute path with the new file name
$destination = Join-Path $_.FullName -ChildPath $name
# now we can copy the `.txt` file into this folder
Copy-Item $targetFile -Destination $destination
}

Expanding Environmental Variable using CSV file into Powershell script

i am trying to create a backup script with csv file which will contain all the local (where backup will be stored) and backup (folder to backup) loaction and run robocopy to copy files from backup to local folder. i want to import environmental variable from csv file that are used for default folders (e.g. env:APPDATA) as location to be use for backing up some files and folders in user documents or pulbic documents. When i use the env variable directly or use full path address in the script the copy action works fine.
robocopy "&env:localappdata\steam" "$backup"
but when import from csv, the script does not see it as env variable. robocopy shows the error because it picks up the locations like this
Source : C:\WINDOWS\system32\$env:LOCALAPPDATA\steam\
Dest : D:\backup\steam\
Below is the full code i am using.
$path = "$PSScriptRoot\backup\"
$locations = import-csv "$PSScriptRoot\backup\local.csv" -Delimiter "," -Header 'Local','Backup','Display' | Select-Object Local,Backup,display
foreach($location in $locations){
$source = $location.Local
$source = $source.ToString()
$destination = $location.Backup
$destination = $destination.tostring()
$Name = $location.Display
$Name = $name.tostring()
Write-host "Copying $Name, please wait...." -foregroundColor Yellow
robocopy "$destination" "$path$source" \s \e
}
And my CSV file looks like this
Steam, $env:LOCALAPPDATA\steam, Steam files Backup
As you have added a path in the csv as $env:LOCALAPPDATA\steam, and knowing that whatever you read from file using Import-Csv is a string, you need to use regex to convert the $env:VARNAME into %VARNAME% in order to be able to resolve that environment variable into a path.
Instead of
$source = $source.ToString()
do this
$source = [Environment]::ExpandEnvironmentVariables(($source -replace '\$env:(\w+)', '%$1%'))
$source wil now have a valid path like C:\Users\Aman\AppData\Local\Steam
Do the same with $destination
Regex details:
\$ Match the character “$” literally
env: Match the characters “env:” literally
( Match the regular expression below and capture its match into backreference number 1
\w Match a single character that is a “word character” (letters, digits, etc.)
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
P.S. Instead of combining a path with string concatenation like you do in "$path$source", it is much safer to use the Join-Path cmdlet for that

Powershell pattern matching

I have a requirement to create directories based on a numeric 9 digit with leading zeros primary key from an order entry database on a remote server.
Each morning a new directory should be created. The directory name will be the primary key number of the new customer. I already have the sql statement extracting the primary key information into a text file using BCP that preserves the leading zeros.
This file is transferred from the database server to the local directory where the folders need to be created. Using some PowerShell code that I think I found I am trying to create the folders from the text file that I have been modifying.
I need the leading zeros preserved in the folder name so I can reference back to the database later in the project. My problem is that when I run the PowerShell script, no folders are created. I think I have the problem isolated to the pattern definition, but don't understand what is wrong.
Input txt file example
001132884
001454596
001454602
001454605
001454606
001454601
001107119
001454600
001454608
PowerShell script
$folder="Customerdocuments"; # Directory to place the new folders in.
$txtFile="E:\dirtext.txt"; # File with list of new folder-names
$pattern="\d+.+"; # Pattern that lines must match
Get-Content $txtFile | %{
if($_ -match $pattern)
{
mkdir "$folder\$_";
}
}
I'm missing a clear question/error report.
Your pattern takes all digits (greedy) from input \d+
and requires at least one other (any) character .+ which isn't present in your sample file.
So your issue isn't related to leading zeroes.
Better specify exactly 9 digits and put that into a Where-object,
The path from $folder will be relative to the current folder and should be build using Join-Path
As mkdir is just a wrapper function for New-Item and it supports piped input I'd use it directly.
$folder="Customerdocuments"; # Directory to place the new folders in.
$txtFile="E:\dirtext.txt"; # File with list of new folder-names
$pattern="^\d{9}$" # Pattern that lines must match
Get-Content $txtFile | Where-Object {$_ -match $pattern}|
New-Item -Path {Join-Path $folder $_} -ItemType Directory | Out-Null
}

How to do a copy /b in a powershell script to insert a BOM marker, but as a batch for files that match a filter and changes the ext on output?

REASONS WHY THIS IS NOT A DUPLICATE
Since 3 people have already voted to close, I guess I should explain why this question is not a duplicate:
I cannot use cat or >> as these mess up the encoding of the files, which are UTF8 on input and need to be UTF8-BOM on output.
The linked question does not show how to loop through all files that match a given pattern in a directory, and concatenate a single file to each of the matching files on output, plus give the new file a different extension.
Using Set-Content is not Powershell 6 future-proof, since Set-Content will NOT add a BOM marker. In Powershell 5 and below, it sometimes adds a BOM marker and sometimes not, depending on the configuration settings of the executing user. See 'quick note on encoding' at the end of this article.
So in conclusion I am looking for a solution that uses copy (hence the question title) and does NOT use Cat or Set-Content.
I need to loop through certain files in a given directory and run the following on each file:
copy /b BOMMarker.txt+InputFile.dat OutputFile.txt
This inserts the contents of the BOMMarker.txt file at the start of the InputFile.dat and writes the output to OutputFile.txt
I found this question which explains how I can loop through the folder to load each file into Powershell, but how do I apply the "copy /b" command so that I can get the BOM marker at the start of each file?
EDIT
The comment from Jeroen indicates I can just do Set-Content on the output file, as Powershell will automatically add the BOM at the start.
But I also need to change the extension. So the output filename needs to be the same as the input filename, just with a changed extension (from .dat to .txt) and including the BOM.
I am guessing I can use Path.ChangeExtension somehow to do this, but not sure how to combine that with also adding the BOM.
EDIT - for Bounty
The example answer I posted does not work in all environments I tested it, and I do not know why (possibly different default Powershell setttings) but also, it is not future proof since Powershell 6 will not output BOM by default.
From the given directory, I need to process all files that match the filter (DIL_BG_TXN*.dat).
For each of those files, I need to copy it with a BOM at the start but the resultant new file needs to be the same name but with the extension .txt instead of .dat.
This solutions uses streams, that reliably read and write as-is:
$bomStream = [IO.File]::OpenRead('BOMMarker.txt')
$location = "" # set this to the folder location
$items = Get-ChildItem -Path $location -Filter DIL_BG_TXN*.dat
foreach ($item in $items) {
$sourceStream = [IO.File]::OpenRead($item.FullName)
$targetStream = [IO.File]::OpenWrite([IO.Path]::ChangeExtension($item.FullName, '.txt'))
$bomStream.CopyTo($targetStream)
$sourceStream.CopyTo($targetStream)
$targetStream.Flush()
$targetStream.Close()
$sourceStream.Close()
$bomStream.Position = 0
}
$bomStream.Close()
Of course please write the absolute path of BOMMarker.txt (1st line) according to its location.
This finally worked:
$Location = "C:\Code\Bulgaria_Test"
$items = Get-ChildItem -Path $Location -Filter DIL_BG_TXN*.dat
ForEach ($item in $items) {
Write-Host "Processing file - " $item
cmd /c copy /b BOMMarker.txt+$item ($item.BaseName + '.txt')
}
Description:
Set the directory location where all the .dat files are.
Load only those files that match the filter into the array $items.
Loop through each $item in the array.
With each $item, call cmd shell with the copy /b command and concatenate the bom marker file with the $item file and write the result to the basename of $item plus the new extension.

Strings and Test-Path Issues

I am working on a PowerCLI (VMware) script that will copy the VMX from the datastores for each VM. One of the initial issues was working with spaces in the VM name. After fixing that issue, there was another hurdle that I'm still struggling on. I am doing a Test-Path to check to make sure the datastore and folder where the VMX file resides can be reached before trying to copy the file. However, the Test-Path fails with the following message:
Set-Location : Cannot find drive. A drive with the name ''vmstore' does not exist.
The ultimate issue is the ' (single quote) just before vmstore being sent to the Test-Path cmdlet. But the output of the variable containing the full path doesn't contain that additional quote. Also when doing the Test-Path with the path entered in directly and using the single quotes, it works fine.
Here's the full script of getting the VMX file location and testing the path:
$gstrdatastorepath = "vmstore:\Cluster_Name\"
$lobjPrimaryDisk = Get-HardDisk -VM $VM | where {$_.name -EQ "Hard disk 1"} #looks for the first hard disk listed in VM definition. This is typically the primary source for the VMX file
$lstrDiskText = $lobjPrimaryDisk.filename #convert the filename to a string for manipulation.
#The string is typically as follows (without the quotes): "[datastore_name] folder_name/filename.vmdk
#Only the datastore_name and the folder_name are needed. The following now splits those from the vmdk file
$lstrDiskSplitInfo = $lstrDiskText.split("\/") #splits the text into two sections. First section is the primary information needed, the second is not used
#Now the datastore_name and the folder_name need to be split
$lstrDiskFolderSplitInfo = $lstrDiskSplitInfo[0] -split " ", 2 #splits the text into the datastore and folder name on the datastore. option 2 used in the event spaces are in VM name.
#Put the Disk and Folder information into separate variables
$lstrdatastorename = $lstrDiskFolderSplitInfo[0] -replace '[[\]]','' #removes the square brackets from the datastore name
$lstrfoldername = $lstrDiskFolderSplitInfo[1] #gets the folder name from the previous split
$completeDataStorePath = $gstrdatastorepath + $lstrdatastorename + "\" + $lstrfoldername #combines the information to return the full path of the VMX file
$completeVMXPathQuoted = "`'$completeDataStorePath`'" #put the path in a single quote for VM's with spaces in the name
The value of $completeVMXPathQuoted is 'vmstore:\Cluster_Name\Data_Store\VM_Name' where VM_Name may or may not contain spaces.
When doing the Test-Path, using the $completeVMXPathQuoted variable, it fails with the message mentioned above.
But when doing Test-Path -Path 'vmstore:\Cluster_Name\Data_Store\VM_Name' it works fine.
What am I missing? Where is that second single quote mark coming from?
Thank you in advance for any assistance.