I'm wondering if someone can help me? I've butchered a few powershell scripts I've found online that make shortcuts from $source to $destination. However, it appears to overwrite each time, and I only want it to create a .lnk on new.
The original source of the script is here and this is my current "non working" script.. I added the following, but it doesn't seem to work. I think I need to somehow get it to check the $destination and then continue if $file.lnk doesn't exist
If ($status -eq $false) {($WshShell.fso.FileExists("$Destination") + "*.lnk")
Full script:
function Create-ShortcutForEachFile {
Param(
[ValidateNotNullOrEmpty()][string]$Source,
[ValidateNotNullOrEmpty()][string]$Destination,
[switch]$Recurse
)
# set recurse if present
if ($Recurse.IsPresent) { $splat = #{ Recurse = $true } }
# Getting all the source files and source folder
$gci = gci $Source #splat
$Files = $gci | ? { !$_.PSisContainer }
$Folders = $gci | ? { $_.PsisContainer }
# Creating all the folders
if (!(Test-Path $Destination)) { mkdir $Destination -ea SilentlyContinue > $null }
$Folders | % {
$Target = $_.FullName -replace [regex]::escape($Source), $Destination
mkdir $Target -ea SilentlyContinue > $null
}
# Creating Wscript object
$WshShell = New-Object -comObject WScript.Shell
# Creating all the Links
If ($status -eq $false) {($WshShell.fso.FileExists("$Destination") + "*.lnk")
$Files | % {
$InkName = "{0}.lnk" -f $_.sBaseName
$Target = ($_.DirectoryName -replace [regex]::escape($Source), $Destination) + "\" + $InkName
$Shortcut = $WshShell.CreateShortcut($Target)
$Shortcut.TargetPath = $_.FullName
$Shortcut.Save()
}
}
}
Create-ShortcutForEachFile -Source \\myserver.domain.local\Folder1\Folder2\Test -Destination \\myserver2.domain.local\Folder1\Folder2\Test -Recurse
Hoping anyone can help me out, apologies for being a powershell/scripting noob.
My brother kindly reworked the script to suit better to my needs.
Here it is:
#################################################
<#
CREATE-SHORTCUT - creates shortcut for all files from a source folder
version : 1.0
Author :
Creation Date :
Modified Date :
#>
#------------------------------------------------------------[ variables ]----------------------------------------------------------
$sourceDir="D:\scripts\create-shortcut\source"
$targetDir="D:\scripts\create-shortcut\dest"
#-------------------------------------------------------------[ Script ]-----------------------------------------------------------
# get files/files from folder
$src_gci=Get-Childitem -path $sourceDir -Recurse
$src_files=$src_gci | ? { !$_.PSisContainer }
$src_folders=$src_gci | ? { $_.PSisContainer }
# create subfolders
$src_folders | Copy-Item -Destination { join-path $targetDir $_.Parent.FullName.Substring($sourceDir.Length) } -Force
# create shortcuts
$WshShell = New-Object -comObject WScript.Shell
$src_files | % {
$lnkName="{0}.lnk" -f $_.BaseName
$Target = ($_.DirectoryName -replace [regex]::escape($sourceDir), $targetDir) + "\" + $lnkName
$Shortcut = $WshShell.CreateShortcut($Target)
$Shortcut.TargetPath = $_.FullName
$Shortcut.Save()
# change to SourceFiles ModifiedDate #
$src_date=$_.LastWriteTime
Get-ChildItem $Target | % { $_.LastWriteTime = "$src_date" }
}
Related
Apologies, this question is scattered on the internet but I have yet to find a satisfactory answer that uses only Powershell 2.0 (with .NET v3.5) - no external libraries or programs
I'm using the following code to extract log.txt from ZipFile.zip (no matter log.txt's location)
$Destination = (new-object -com shell.application).NameSpace('C:\ZipExtractDir')
$ZipFile = (new-object -com shell.application).NameSpace('C:\ZipFile.zip')
$Destination.CopyHere(($Zipfile.Items() | where-object {$_.Name -like '*log.txt'}), 1044)
Works if log.txt is in directory root \log.txt
Fails if log.txt is in a subdirectory \Subfolder\log.txt
Fails if referencing the literal (.zip) path
{$_.Name -Like '*Subfolder\log.txt'} (both double & single quotes fail)
Have tried using -eq -like -contains '' "" $_.FullName
I'm quite certain that I'm filtering incorrectly - can anyone help with this code so that it will parse subdirectories as well?
Similar to what you have already done, you can set up the Shell.Application namespaces like this. Then you can copy the extracted directory to the destination path.
$zipFilePath = "Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
$zipfile = (New-Object -Com Shell.Application).NameSpace($zipFilePath)
$destination = (New-Object -Com Shell.Application).NameSpace($destinationPath)
$destination.CopyHere($zipfile.Items())
Then to list the log.txt files, we can contruct the full extracted path with Join-Path. This basically just appends the zip file name from System.IO.Path.GetFileNameWithoutExtension() to the destination path. Then just use Get-ChildItem to list the files recursively with the -Recurse and -Filter switches.
$extractedPath = Join-Path -Path $destinationPath -ChildPath ([System.IO.Path]::GetFileNameWithoutExtension($zipFilePath))
Get-ChildItem -Path $extractedPath -Filter log.txt -Recurse
And to test this for PowerShell 2.0 we can use -version 2 with powershell.exe:
powershell.exe -version 2 .\test.ps1
UPDATE
If you want to inspect files before extracting, you'll need to recurse the directories yourself. Below is a demo of how this can be done.
function New-ZipChildRootFolder
{
param
(
[string]$DestinationPath,
[string]$ZipFileName
)
$folderPath = Split-Path -Path $ZipFileName -Leaf
$destination = (New-Object -ComObject Shell.Application).NameSpace($DestinationPath)
$destination.NewFolder($folderPath)
}
function Get-ZipChildItems
{
param
(
[string]$ZipFilePath,
[string]$DestinationPath
)
$zipfile = (New-Object -ComObject Shell.Application).NameSpace($ZipFilePath)
$zipFileName = [System.IO.Path]::GetFileNameWithoutExtension($ZipFilePath)
Write-Output "Create root zip folder : $zipFileName"
New-ZipChildRootFolder -DestinationPath $DestinationPath -ZipFileName $zipFileName
foreach ($item in $zipFile.items())
{
Get-ZipChildItemsRecurse -Items $item -DestinationPath $DestinationPath -ZipFileName $zipFileName
}
}
function Get-ZipChildItemsRecurse
{
param
(
[object]$Items,
[string]$DestinationPath,
[string]$ZipFileName
)
foreach ($file in $Items.getFolder.Items())
{
if ($file.IsFolder -eq $true)
{
Write-Output "Creating folder : $($file.Path)"
New-ZipChildFolder -Folder $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
Get-ZipChildItemsRecurse -Items $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
else
{
$filename = Split-Path -Path $file.Path -Leaf
if ($filename -eq "log.txt")
{
Write-Output "Copying file : $($file.Path)"
New-ZipChildFile -File $file -DestinationPath $DestinationPath -ZipFileName $ZipFileName
}
}
}
}
function New-ZipChildFile
{
param
(
[object]$File,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $File.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$path = $items[$zipRootIndex..($items.Length - 2)] -join "\"
$fullPath = Join-path -Path $DestinationPath -ChildPath $path
$destination.NameSpace($fullPath).CopyHere($File)
}
function New-ZipChildFolder
{
param
(
[object]$Folder,
[string]$DestinationPath,
[string]$ZipFileName
)
$destination = New-Object -ComObject Shell.Application
$items = $Folder.Path.Split("\")
$zipRootIndex = [array]::IndexOf($items, $ZipFileName)
$folders = $items[$zipRootIndex..($items.Length - 1)]
$currentFolder = $DestinationPath
foreach ($folder in $folders)
{
$destination.NameSpace($currentFolder).NewFolder($folder)
$currentFolder = Join-Path -Path $currentFolder -ChildPath $folder
}
}
Usage:
$zipFilePath = "C:\Zipfile.zip"
$destinationPath = "C:\Users\Public\Downloads"
Get-ZipChildItems -ZipFile $zipFilePath -DestinationPath $destinationPath
Hello and thank you in advance. Currently I have a script running that downloads files from one location to another. I'm trying to clean up the script to only download files that have _Lthumb in the file name. Any help would be greatly appreciated.
The following line in which I added is causing the problems. Wheni remove it, the script runs fine, but it doesn't filter the data by _Lthumb:
| Where-Object {$_.Name -Match '*_Lthumb'}
Here is the script, including the portion above that breaks it:
if((Get-PSSnapin | Where {$_.Name -eq "Microsoft.SharePoint.PowerShell"}) -eq $null) {
Add-PSSnapin Microsoft.SharePoint.PowerShell;
}
######################## Start Variables ########################
$destination = "\\pulse-dev.hinshawad.com\C$\ProfilePhotos\ProfilePictures"
$webUrl = "http://mysites.hinshawad.com"
$listUrl = "http://mysites.hinshawad.com/user photos/profile pictures/"
##############################################################
$web = Get-SPWeb -Identity $webUrl
$list = $web.GetList($listUrl)
function ProcessFolder {
param($folderUrl)
$folder = $web.GetFolder($folderUrl)
foreach ($file in $folder.Files | Where-Object {$_.Name -Match '*_Lthumb'} ) {
#Ensure destination directory
$destinationfolder = $destination + "/" + $folder.Url
if (!(Test-Path -path $destinationfolder))
{
$dest = New-Item $destinationfolder -type directory
}
#Download file
$binary = $file.OpenBinary()
#$stream = New-Object System.IO.FileStream($destinationfolder + "/" + $file.Name), Create
$stream = New-Object System.IO.FileStream($destinationfolder + "/" + ($file.Name -replace "_Lthumb")), Create
$writer = New-Object System.IO.BinaryWriter($stream)
$writer.write($binary)
$writer.Close()
}
}
#Download root files
ProcessFolder($list.RootFolder.Url)
#Download files in folders
#foreach ($folder in $list.Folders) {
#ProcessFolder($folder.URL)
#}
I have a PowerShell script that moves all files from one location to another that have a date modified older than 3 years. I have it so the file when moved to the new location also keeps the file structure of the original.
I am trying to make it so once the file has been moved to the new location it creates a shortcut in the original directory which points to the new location of the file.
Below is my script so far which does all the above minus the shortcut.
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -Recurse |
Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$sourceDir")
$Shortcut.TargetPath = $destination
$Shortcut.Save()
}
In my script I have included my attempt at creating this shortcut but it hasn't helped. I have also read through the following but don't understand it too well..
How to create a shortcut using Powershell
Powershell Hard and Soft Links
Edit:
I have successfully got the shortcut to create and in the original folder. However, I can't seem to figure out how to pass a variable to use as the shortcut name. At the moment a string is hard coded, which is what the shortcut gets named. Please see code below: I would like to set the name as the item full name (Same name as document that was moved).
$sourceDir = "C:\Users\bgough\Documents\powershell\docs"
$archiveTarget = "C:\Users\bgough\Documents\archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
$items = Get-ChildItem $sourceDir -recurse | Where-Object {!$_.PsIsContainer -and $_.LastWriteTime -le $date}
foreach ($item in $items)
{
$withoutRoot = $item.FullName.Substring([System.IO.Path]::GetPathRoot($item.FullName).Length);
$destination = Join-Path -Path $archiveTarget -ChildPath $withoutRoot
$dir = Split-Path $destination
if (!(Test-Path $dir))
{
mkdir $dir
}
Move-Item -Path $item.FullName -Destination $destination
$wshshell = New-Object -ComObject WScript.Shell
$desktop = [System.Environment]::GetFolderPath('Desktop')
$lnk = $wshshell.CreateShortcut($sourceDir + "\ShortcutName.lnk")
$lnk.TargetPath = "$destination"
$lnk.Save()
}
.lnk files are fine when you're using Explorer but they don't play well in Powershell or a command prompt.
What you need to do is create a symbolic link for the file. You can't do this in Powershell, but there is a command line utility called mklink that does it. I've wrapped it in a function so that you can call it:
function CreateLink
{
param
(
[string] $LinkName,
[string] $TargetFile
)
&"cmd.exe" /c mklink "$LinkName" "$TargetFile" | Out-Null
}
In your example you would call it like this:
CreateLink -LinkName $item.FullName -TargetFile $destination
When you look at the directory in Powershell the file will show up as being 0 bytes in size. Don't worry about that.
Thanks for your script Android Magic.
I have modified it to:
Copy a set of files from source to destination
It creates the identical folder structure on the destination, even if the folders are empty
It then creates a symbolic link to the archived file. SymbolicLink support was added in Powershell v5.1. You have to run the script as Admin in order for the Symbolic Link creation to work.
I'd like to add a function to email if anything goes wrong and a summary of status, but that's for another day.
$sourceDir = "\\Fileserver1\IT\Vendor"
$archiveTarget = "\\FS-ARCHIVE\Archive\Fileserver1\IT\Vendor"
$rootArchivePath = "\\FS-ARCHIVE\Archive"
$dateToday = Get-Date
$date = $dateToday.AddYears(-3)
# Copy folder structure to Archive
Get-ChildItem -Path $sourceDir -Recurse |
?{ $_.PSIsContainer } |
Copy-Item -Destination {Join-Path $archiveTarget $_.Parent.FullName.Substring($sourceDir.length)} -Force
$items = Get-ChildItem $sourceDir -Recurse -Attributes !Directory |
Where-Object {$_.LastAccessTime -le $date}
foreach ($item in $items)
{
$withoutRoot = Split-Path -Path $item.FullName
$destination = $rootArchivePath + $withoutRoot.Remove(0,1)
$destFile = $destination + "\" + $item
Move-Item -Force -Path $item.FullName -Destination $destination -Verbose
New-Item -ItemType SymbolicLink -Path $withoutRoot -Name $item -Value $destFile -Force -Verbose
}
Basically, we have a lot of .mht files (generated periodically with MicroStrategy) and we need to serve these files on different folders to apply security. To avoid space consumption, we thought of creating shortcuts on several folders.
The files all start with groupings of 4 digits and an underscore (e.g. 0001_0041, 0001_0043, etc.).
This is what I have now:
Get-Childitem -Path "C:\Users\Max\Google Drive\Portal\Mermaid" -Recurse -Include "0002*.mht"
Foreach-Object {
$ruta = Get-Childitem -Path "C:\Users\Max\Google Drive\Portal\Mermaid" -Recurse -Include "0002*.mht"
$nombre = Get-Childitem -Name "C:\Users\Max\Google Drive\Portal\Mermaid" -Recurse -Include "0002*.mht"
$ncorto = $nombre | ForEach-Object {$nombre.Substring(0,9)}
$container = "C:\Users\Max\Google Drive\Portal\Mermaid\MHT_Shortcuts\$ncorto"
if (!(Test-Path $container)) {
New-Item -ItemType directory -Path $container | Out-Null
}
$TargetFile = $ruta
$ShortcutFile = "$container\$nombre.lnk"
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = $TargetFile
$Shortcut.Save()
}
This works as long as there is only one file starting with 0002. However, they keep historic files that have the same name with timestamp.
If there are multiple files that start with 0002 (there will be as they keep historic versions of these files), I get the error:
Exception setting "TargetPath": "The parameter is incorrect. (Exception from
HRESULT: 0x80070057 (E_INVALIDARG))"
En C:\Users\Max\Desktop\MK_LNK_V01.ps1: 25 Character: 7
+ $Shortcut.TargetPath = $TargetFile
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : CatchFromBaseAdapterSetValueTI
Sorry if this is a duplicate, I couldn't find a post about this specific issue.
Here's a short example of how to create .lnk files for all files in a directory:
$shortcutPath = "C:\TextFiles"
$wshShell = New-Object -ComObject "WScript.Shell"
Get-ChildItem (Join-Path $shortcutPath "*.txt") | ForEach-Object {
$lnkFilename = Join-Path $shortcutPath ("{0}.lnk" -f [IO.Path]::GetFilenameWithoutExtension($_.FullName))
$shortcut = $wshShell.CreateShortcut($lnkFilename)
$shortcut.TargetPath = $_.FullName
$shortcut.Save()
}
Of course you will need to modify this example to suit your needs.
Based on the script that #Bill_Stewart provided, I added some lines to first delete the links, then create the containing folder based on the 1st 9 characters of the filename and create the shortcuts in it:
$shortcutPath = "C:\Origin"
$contenedor = "C:Destination"
$wshShell = New-Object -ComObject "WScript.Shell"
Get-ChildItem -Path $contenedor -Include *.lnk -File -Recurse | foreach { $_.Delete()}
Get-ChildItem (Join-Path $shortcutPath "*.mht") | ForEach-Object {
$nombre = $_.BaseName
$ncorto = $nombre.SubString(0, 9)
$destino = Join-Path $contenedor $ncorto
if (!(test-Path $destino)) {
New-Item -ItemType directory -Path $destino | Out-Null}
$lnkFilename = Join-Path $destino("{0}.lnk" -f [IO.Path]::GetFilenameWithoutExtension($_.FullName))
$shortcut = $wshShell.CreateShortcut($lnkFilename)
$shortcut.TargetPath = $_.FullName
$shortcut.Save()
This worked as a charm. Thanks all for your help.
The TargetPath property of a WshShortcut object expects a single path, not a list of paths. Also, I'd recommend moving everything that doesn't require repeated exectution outside the loop.
$app = New-Object -ComObject 'WScript.Shell'
$container = 'C:\shortcut\folder'
$path = 'C:\Users\Max\Google Drive\Portal\Mermaid'
if (!(Test-Path $container)) {
New-Item -Type Directory -Path $container | Out-Null
}
Get-Childitem -Path $path -Recurse -Include "0002*.mht" | Foreach-Object {
$ShortcutFile = "$container\$nombre.lnk" -f $_.BaseName
$Shortcut = $app.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = $_.FullName
$Shortcut.Save()
}
I am new to powershell and am running into a problem while trying to exclude certain directories during recursive copy. Any help is appreciated!
Thanks in advance.
$Date = Get-Date
$Date = $Date.adddays(-1)
$destPath = "\\destination\test"
$srcPath = "H:\program files\symphony\Save"
$srcPathRemits = “H:\program files\symphony\files"
$destDrive = "X:"
$User = "user"
$Password = "password"
$exclude = #('H:\program files\symphony\files\Temp\*','H:\program files\symphony\files\Other\*','H:\program files\symphony\files\etc\*','H:\program files\symphony\files\ParsedXML\*')
$net = new-object -ComObject WScript.Network
$net.MapNetworkDrive($destDrive, $destPath, $false, $User, $Password)
gci -recurse -path $srcPathRemits -Exclude $exclude | ? {!($_.psiscontainer) -AND $_.lastwritetime -gt $Date} | % { write-host $_.fullname; Copy-Item -path $_.fullname -destination $destDrive}
$net.RemoveNetworkDrive($destDrive,"true","true")
You didn't say what the problem was, but I'll assume that the directories ($exclude) were not properly excluded. Try this instead, for the gci line:
Get-Item -Path H:\program files\symphony\files\* -Exclude Temp, Other, etc, ParsedXML | Get-ChildItem -recurse | ? {!($_.psiscontainer) -AND $_.lastwritetime -gt $Date} | % { write-host $_.fullname; Copy-Item -path $_.fullname -destination $destDrive}