Why shortcuts are not created? - powershell

i have trouble in powershell (Windows 10 1809)
I want that this script seach all .exe files in my directory D:\Test in Recursive mode and make shortcuts in D:\ . But my code just creating link.lnk in D:\ . Why?
$1 = get-childitem "D:\Test" -recurse | where {$_.extension -eq ".exe"} | % {
Write-Host $_.FullName
}
ForEach ($item in $1) {
$Shell = New-Object -ComObject ("WScript.Shell")
$ShortCut = $Shell.CreateShortcut("D:\link.lnk")
$ShortCut.TargetPath= $1
$Shortcut.Save()
}

It looks like you are naming all of the shortcuts D:\link.lnk, and the loop will just overwrite the same name over and over. Specifying a unique shortcut link filename using the BaseName property (name minus the extension) should solve your problem. Also you need to use the loop $item to specify the TargetPath.
Also, the by looping through with the Write-Host you don't actually assign anything to your variable $1. By removing the % { Write-Host $_.FullName } $1 does get the proper output. So the proper code should be:
$1 = get-childitem "D:\Test" -recurse | where {$_.extension -eq ".exe"}
ForEach ($item in $1) {
Write-Host "Creating Shortcut: D:\$($item.BaseName).lnk Pointing to: $($item.FullName)"
$Shell = New-Object -ComObject ("WScript.Shell")
$ShortCut = $Shell.CreateShortcut("D:\$($item.BaseName).lnk")
$ShortCut.TargetPath= $item.FullName
$Shortcut.Save()
}

Related

Reading Directory + Creating Shortcuts with PowerShell

everyone. I'm trying to figure out a way to read the contents of a directory in Windows and find files of a specific file extension ('.hod' in this case), then create shortcuts to each of those files into 'C:\Users\Public\Desktop.'
Below is an example I've been testing with so far in PowerShell (I know it looks utterly terrible). I'd appreciate any input. Thanks.
$shortcutfiles = dir "C:\C:\IBM-Shortcuts\*.hod"
$DestinationDir = "C:\Users\Public\Desktop"
foreach ($shortcutfile in $shortcutfiles ) {
$TargetPath = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.FullName }
$BaseName = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.BaseName }
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$DestinationDir" & "$BaseName"+lnk)
$Shortcut.TargetPath = "$TargetPath"
$Shortcut.Save()
}
So a few things to note here:
Use Full cmdlet names instead of alias', this for clarity.
Get-Childitem instead of dir
... | Foreach-Object { ... instead of ... | % ...
Only iterate over the folders contents once and reference the $_ variable within the loop instead of looping within a loop
if a variable is only being used once then don't bother storing it in its own variable
$Destination is no longer used
Indent your code following basic formatting rules
As #mklement0 mentioned, it's worth executing $WshShell = New-Object -comObject WScript.Shell only once, before the pipeline.
$WshShell = New-Object -comObject WScript.Shell
Get-ChildItem "C:\IBM-Shortcuts\*.hod" | Foreach-Object {
$Shortcut = $WshShell.CreateShortcut("C:\Users\Public\Desktop\$($_.BaseName).lnk")
$Shortcut.TargetPath = $_.FullName
$Shortcut.Save()
}
Some other things to note:
On line 1 you are referencing "C:\C:\IBM-Shortcuts\*.hod", this has one too many C:\ in it.
Your use of $TargetPath = Get-ChildItem "C:\IBMiACS-Shortcuts" -Filter *.hod -Recurse | % { $_.FullName } is not setting the targetpath for the current iteration of $Shortcutfile, it is returning a list of all file paths in "C:\IBMiACS-Shortcuts"
Take a look into the basics of a foreach loop here

Powershell script to copy old files and create shortcuts

I am working on creating a stubbing script using powershell. My intentions for this script is to copy any data that hasn't been written to in the past x time period to a new location, check that the file copied, create a shortcut to the "archived" file in it's original location, and eventually delete the old file (I haven't written this section yet). Below is what I have so far. The issue I am having now is that a shortcut is created however all of the shortcuts are saved to the C:\Temp directory and not in any subfolders; if that is where the original file was stored. I think my issue is with $link and I need to split the path's and join them but I am not sure. Any assistance is greatly appreciated!
# Variables
$Original = "C:\Temp"
$Archive = "\\data\users\Temp"
Get-ChildItem -Path $Original -Recurse |
Where-Object {
$_.LastWriteTime -lt [datetime]::Now.AddMinutes(-1)
} | Copy-Item -Destination $Archive -Recurse -force
$sourceFiles = Get-ChildItem $Original -Recurse | Where-Object { $_.LastWriteTime -lt [datetime]::Now.AddMinutes(-1) } | Select Name, Fullname
$destFiles = Get-ChildItem $Archive -Recurse | Select Name, Fullname
$Comparison = Compare-Object $sourceFiles.name $destFiles.name
If ($comparison.sideindicator -ne "<=") {
get-childitem $Archive -Recurse | where { $_.PsIsContainer -eq $false } | ForEach-Object {
$path = '"' + $_.FullName + '"'
$link = $Original + '\' + $_.Basename + '.lnk'
$wshell = New-Object -ComObject WScript.Shell
$shortcut = $wshell.CreateShortcut($link)
$shortcut.TargetPath = $path
$shortcut.Save()
}
}
If ($comparison.sideindicator -eq "<=") {
$comparison.inputobject, $sourceFiles.fullname | Out-File 'C:\ScriptLogs\stubbing.csv' -Force
}
This is the problematic code:
$link = $Original + '\' + $_.Basename + '.lnk'
Basename is only the filename portion without the path, so the .lnk files end up directly in the top-level directory.
You have to use the relative path like this:
$RelativePath = [IO.Path]::GetRelativePath( $Archive, $_.FullName )
$link = [IO.Path]::ChangeExtension( "$Original\$RelativePath", 'lnk' )
This requires .NET 5 for GetRelativePath. To support older .NET versions you can use:
Push-Location $Archive
try { $RelativePath = Resolve-Path -Relative $_.FullName }
finally { Pop-Location }
Resolve-Path -Relative uses the current location as the base base. So we use Push-Location to temporarily change the current location. The try / finally is used to ensure restoring of the original location even in case Resolve-Path throws an exception (e. g. when you have set $ErrorActionPreference = 'Stop').
Although the code might work like this, I think it could be refactored like this:
A single Get-ChildItem call to store the files to be moved into an array (your current $sourceFiles = ... line)
A foreach loop over this array to move each file and create the shortcut.
I think the Compare-Object isn't even necessary, but maybe I'm missing something.
Currently you are iterating over the same directories multiple times, which isn't very efficient and a lot of duplicate code.

Powershell/WScript Create Shortcut if not exist

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" }
}

PowerShell: Extract specific files/folders from a zipped archive

Say foo.zip contains:
a
b
c
|- c1.exe
|- c2.dll
|- c3.dll
where a, b, c are folders.
If I
Expand-Archive .\foo.zip -DestinationPath foo
all files/folders in foo.zip are extracted.
I would like to extract only the c folder.
try this
Add-Type -Assembly System.IO.Compression.FileSystem
#extract list entries for dir myzipdir/c/ into myzipdir.zip
$zip = [IO.Compression.ZipFile]::OpenRead("c:\temp\myzipdir.zip")
$entries=$zip.Entries | where {$_.FullName -like 'myzipdir/c/*' -and $_.FullName -ne 'myzipdir/c/'}
#create dir for result of extraction
New-Item -ItemType Directory -Path "c:\temp\c" -Force
#extraction
$entries | foreach {[IO.Compression.ZipFileExtensions]::ExtractToFile( $_, "c:\temp\c\" + $_.Name) }
#free object
$zip.Dispose()
This one does not use external libraries:
$shell= New-Object -Com Shell.Application
$shell.NameSpace("$(resolve-path foo.zip)").Items() | where Name -eq "c" | ? {
$shell.NameSpace("$PWD").copyhere($_) }
Perhaps it can be simplified a bit.
Here is something that works for me. Of Course, you will need to edit the code to fit your objective
$results =#()
foreach ($p in $Path)
{
$shell = new-object -Comobject shell.application
$fileName = $p
$zip = $shell.namespace("$filename")
$Results += $zip.items()| where-object { $_.Name -like "*C*" -or $_.Name -like
"*b*" }
}
foreach($item in $Results )
{
$shell.namespace($dest).copyhere($item)
}

Access a PowerShell Variable Immediately After Mutation

I have a simple PowerShell script that bulk renames files and I'd like to have a table output that shows 'Old File Name' and 'New File Name' to show the changes. How do I access/reference the new file name while still inside the 'ForEach-Object' loop, assuming it can be done? Can I refresh that particular value of $_? I don't want to just use my variable $newname because I created that and since this is supposed to be output that shows the file names were actually changed, I'd like to access the file's new name from the system as determined by the script.
Write-Host "Old Name `t`t`t`t`t`t New Name"
Write-Host "-------- `t`t`t`t`t`t --------"
$files = Get-ChildItem -Path C:\Users\<user>\Desktop\Config
$files | % ({
if ((!$_.PsIsContainer) -and ($_.Extension -eq '')) {
$oldname = $_.FullName
$newname = "$($_.FullName).cfg"
Rename-Item $oldname $newname
# $_.Name still refers to the old file name without the extension
# How do I immediately access its new name (including extension)?
Write-Host $_.Name `t`t`t`t`t`t $newname
}
})
You can do a Get-Item on $newname:
(Get-Item $newname).Name
I have taken the liberty to rewrite your Script a little, using a Hash Table and New-Object.
To explain what I'm doing here, I will split it a bit up for you.
First I declar some Variables, I'm gonna use in my foreach loop, with this:
Note: -File parameter, is so it will only take file names, if you like to rename everything, files and folder, then remove it.
$files = Get-ChildItem -Path "C:\Users\dha\Desktop\PS test folder" -File
$OldFiles = $files
[int]$i = "0"
Then I'm taking each file one by one, and creating the new name in:
$Newname = ("Blop" + "$i" + ".txt")
And then take the file info for one file, and pipe it to rename-item here
$File | Rename-Item -NewName $Newname
The $ixx is just a plusser so increase the file number by one for each time the foreach loop runs.
Then I'm writing a Hash Table with $Old_And_New variable.
Last, I'm creating a Object with New-object and outputting it.
I Hope this help's and my explanation is understandable.
To script assembled:
$files = Get-ChildItem -Path "C:\Users\dha\Desktop\PS test folder" -File
$OldFiles = $files
[int]$i = "0"
foreach ($File in $files)
{
$Newname = ("Blop" + "$i" + ".txt")
$File | Rename-Item -NewName $Newname
$i++
$Old_And_New = #{
'Old File Name' = "$OldFiles";
'New File Name' = "$Newname"
}
$obj = New-Object -TypeName PSObject -Property $Old_And_New
Write-Output $obj
}