Let me tell about my scenario.
I have a file on the desktop, I don't know its full name and where is it location. I just know that file starts with "ABC" and its an .exe. So I am trying to find the path to this file with the help of a script. I've tried to use this function.
function findPath ($path) {
return $thePath = Get-ChildItem -Path $path -Filter '*ABC*.exe' -Recurse | % { $_.FullName }
}
When I call the this function this fun gives me a string input like:
C:\Users\UserX\Desktop\New Folder\FolderA\FolderB\1_Abc.exe
Is there anyway to reach path of :
C:\Users\UserX\Desktop\New Folder\FolderA\FolderB\
Split-Path -Path works for me.
Guessing that you might be looking for the folder path of the target of a shortcut to an exxecutable. If so:
Function Get-TargetFolderPath ($LinkPath) {
(new-object -com wscript.shell).CreateShortcut($Path).TargetPath | Split-Path
}
And, exclusively for Desktop shortcuts and URLS, this requires only the Dispaly Name of the the shortcut and works for items from both the user's Desktop folder and the Public Desktop folder:
Function Get-DesktopShortcutTargetPath ($LinkName) {
(#((New-Object -com shell.application).NameSpace(0).Items()) | ? Name -eq $LinkName).ExtendedProperty("System.Link.TargetParsingPath") | Split-Path
}
PS > Function Get-DesktopShortcutTargetPath ($LinkName) {
>> (#((New-Object -com shell.application).NameSpace(0).Items()) | ? Name -eq $LinkName).ExtendedProperty("System.Link.TargetParsingPath") | Split-Path
>> }
PS >
PS > Get-DesktopShortcutTargetPath 'Adobe Acrobat DC'
C:\Program Files\Adobe\Acrobat DC\Acrobat
Related
I need to get the 'target' inside of a shortcut link to another server.
But… when I use -Recurse, it follows the links, instead of simply just getting the shortcut target.
Here is code that I found online that I edited to serve my purpose. But it goes into the links and tries to find shortcuts within another server:
#Created By Scott
$varLogFile = "E:\Server\GetBriefsTargets.log"
$varCSVFile = "E:\Server\GetBriefsTargets.csv"
function Get-StartMenuShortcuts
{
$Shortcuts = Get-ChildItem -Recurse "F:\Connect" -Include *.lnk
#$Shortcuts = Get-ChildItem -Recurse "D:\Scripts\scott_testing" -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
foreach ($Shortcut in $Shortcuts)
{
$Properties = #{
ShortcutName = $Shortcut.Name;
ShortcutFull = $Shortcut.FullName;
ShortcutPath = $shortcut.DirectoryName
Target = $Shell.CreateShortcut($Shortcut).targetpath
}
New-Object PSObject -Property $Properties
}
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
$Output = Get-StartMenuShortcuts
$Output
ECHO "$Output" >> $varLogFile
ECHO "$Output" >> $varCSVFile
Could someone please offer advice on what I can change so that it still finds all of the shortcut links in all of the folders? and stops going inside of those links?
ie:
F:\Connect\CLIENTA\shortcut.lnk
F:\Connect\CLIENTB\shortcut.lnk
etc.
There's about 100 clients that I have to get their links and I don't want to do it manually (each month).
Here is the error that I get upon trying to run this script:
Get-ChildItem : The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the directory name must
be less than 248 characters.
At D:\Scripts\scott_testing\GetShortcutTarget_edit1.ps1:8 char:18
+ $Shortcuts = Get-ChildItem -Recurse "F:\Connect" -Include *.lnk
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ReadError: (F:\Connect\CLIENTA\briefs\FILENAME:String) [Get-ChildItem], PathTooLongException
+ FullyQualifiedErrorId : DirIOError,Microsoft.PowerShell.Commands.GetChildItemCommand
It is going 'inside' of the link and trying to find shortcuts on another server.
I can't reproduce. All the targets are probably found.
The results shown in the console by the $output-line should show everything as it should be. If not, update the question with the actual output, the desired output.
The only error I see here is that you try to save CSV-objects using output (text) redirection. That would only return the string-representation of the objects, which is not CSV. In this situation it doesn't output anything when I try it because $output is an array of objects that return nothing when the ToString() method is called.
Replace:
ECHO "$Output" >> $varCSVFile
With:
$Output | Select ShortcutName, ShortcutFull, ShortcutPath, Target | Export-CSV -Path $varCSVFile -NoTypeInformation
Sample output:
"ShortcutName","ShortcutFull","ShortcutPath","Target"
"WinSystem.lnk","C:\Users\frode\Desktop\test\WinSystem.lnk","C:\Users\frode\Desktop\test","C:\Windows\System"
"Lol.lnk","C:\Users\frode\Desktop\Lol.lnk","C:\Users\frode\Desktop","C:\Windows\System32\notepad.exe"
"Windows.lnk","C:\Users\frode\Desktop\Windows.lnk","C:\Users\frode\Desktop","\\localhost\c$\Windows"
"WinSystem32.lnk","C:\Users\frode\Desktop\WinSystem32.lnk","C:\Users\frode\Desktop","\\127.0.0.1\c$\Windows\System32"
UPDATE If you want it do a recursive search you could try something like this. Be aware that the target of the "second-level" shortcut (ex \\server\share\shortcutb.nk might have c:\temp as a target which means you'll get a local path and not an UNC for the remote computer (see MyEnd.lnk in sample output below).
Warning: This could easily result in a infinite loop (circular referencing) or long running search (because of recursive search).
function Get-StartMenuShortcuts ($path) {
$Shortcuts = Get-ChildItem -Path $path -Recurse -Include *.lnk
#$Shortcuts = Get-ChildItem -Recurse "D:\Scripts\scott_testing" -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
foreach ($Shortcut in $Shortcuts)
{
$Properties = #{
ShortcutName = $Shortcut.Name;
ShortcutFull = $Shortcut.FullName;
ShortcutPath = $shortcut.DirectoryName
Target = $Shell.CreateShortcut($Shortcut).targetpath
}
Get-StartMenuShortcuts -path $Properties.Target
New-Object PSObject -Property $Properties
}
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
$output = Get-StartMenuShortcuts -path "F:\Connect"
Example output:
"ShortcutName","ShortcutFull","ShortcutPath","Target"
"WinSystem.lnk","C:\Users\frode\desktop\test\WinSystem.lnk","C:\Users\frode\desktop\test","C:\Windows\System"
"MyEnd.lnk","\\127.0.0.1\c$\EFI\MyEnd.lnk","\\127.0.0.1\c$\EFI","C:\Users\frode\Desktop\TheEnd"
"EFI.lnk","C:\Users\frode\desktop\EFI.lnk","C:\Users\frode\desktop","\\127.0.0.1\c$\EFI"
"Lol.lnk","C:\Users\frode\desktop\Lol.lnk","C:\Users\frode\desktop","C:\Windows\System32\notepad.exe"
If you want to only get the deepest level, you should add a "level"-counter to each object that increases for each recursion call and then keep only the highest ones etc. It might get complicated depending on you needs so that would require a separate detailed question.
I found a command that will work in command prompt, but never seemed to work with powershell:
DIR /AL /S F:\Connect
It took a while to run... but it did.
Also, I found a program that did it in like 2 seconds:
http://sumtips.com/2012/10/find-symbolic-links-ntfs-junction-points-windows.html
I was trying to find the 'symbolic links' ... which I was stating targets.
Let's say I have a virtual R: drive that actually goes to \\filesrver\share.
How can I get the full remote path of a file, expanding net shares?
(R:\Scripts\s.ps1 --> \\fileserver\share\Scripts\s.ps1)
try this:
get-item R:\Scripts\s.ps1 |
select #{n="path";e={ "$( get-psdrive $($_.psdrive) |
select -expa displayroot)$(split-path $_ -noqualifier)" }}
My solution, also works for local paths (it doesn't change them of course):
function Get-FullRemotePath($path) {
# Attempts to expand net shares
if (!(Test-Path $path)) { return $path }
$fileObj = Get-Item $path
$remotePath = (Get-PsDrive $fileObj.PSDrive).DisplayRoot
if ($remotePath) {
$path = $remotePath+(split-path $fileobj -noqualifier)
}
return $path
}
Not a one liner though.
All,
I am using Power Shell Community Extensions for PSv1 and the ZIP file is being created correctly. However, I only want the images in the ZIP file and I want to remove the folder from the ZIP file.
I have the folder called newpictures that is zipped up. I then use the -flattenpaths option in the Power Shell Community Extensions to put all the images in the base path, but the folder remains.
I have been searching online for a solution. I am not sure if I have this right, so can someone look over this code and let me know if this is correct before I proceed?
if (test-path $PSCXInstallDir) {
write-zip -path "Microsoft.PowerShell.Core\FileSystem::$TestSite" -outputpath "Microsoft.PowerShell.Core\FileSystem::$ZipFileCreationDir\$ZipFileName" -noclobber -quiet -flattenpaths -level 9
start-sleep -seconds 30
if (test-path $ZipFileCreationDir\$ZipFileName) {
$ShellApp = new-object -com shell.application
$TheZipFile = $ShellApp.namespace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.items() | where-object {$_.name -eq $FolderToCompress} | remove-item $FolderToCompress
}
}
The variables are:
$PSCXInstallDir = "C:\Program Files\PowerShell Community Extensions"
$TestSite = "\\10.0.100.3\www2.varietydistributors.com\catalog\newpictures"
$ZipFileCreationDir = "\\10.0.100.3\www2.varietydistributors.com\catalog"
$ZipFileName = "vdi-test.zip"
$FolderToCompress = "newpictures"
Thanks in advance for any feedback. In short, I just want to remove the single folder within the ZIP file.
Remove-Item won't work on items inside the zip file. You need to move the objects you want to delete outside the zip file before you can delete them:
$ShellApp = New-Object -COM 'Shell.Application'
$TheZipFile = $ShellApp.NameSpace("$ZipFileCreationDir\$ZipFileName")
$TheZipFile.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$ShellApp.NameSpace($env:TEMP).MoveHere($_)
Remove-Item (Join-Path $env:TEMP $_.Name)
}
Note that Items() doesn't recurse into nested folders, it only enumerates the files and folders of the current namespace. If you need to process contents of nested folders you need to specify the nested path:
$NestedFolder = $ShellApp.NameSpace('C:\path\to\your.zip\nested\folder')
or recurse with something like this:
function RecurseIntoZip($fldr) {
$fldr.Items() | ? { $_.Name -eq $FolderToCompress } | % {
$_.Name
}
$fldr.Items() | ? { $_.Type -eq 'File folder' } | % {
RecurseIntoZip $_.GetFolder
}
}
RecurseIntoZip $TheZipFile
I am trying to search thru a zip file and just extract all of the .sql files to a directory. I can make it extract all the files, but there are over 200 misc files in the zip, and I only need the 6 .sql's. Is there an easy way to designate just the .sql?
Here is the example code that I was trying to get to work, if there is a better way, I would love to hear.
$shell = new-object -com shell.application
$zip = $shell.NameSpace(“C:\Temp”)
foreach($item in $zip.items()){
if([System.IO.Path]::GetExtension($item.Path) -eq ".sql"){
$shell.Namespace(“C:\Project\”).copyhere($item)
}
}
If you have (or grab) the PowerShell Community Extensions, you can use its archive commands:
Read-Archive C:\temp\foo.zip | %{$_} | Where Name -match '\.sql' | Expand-Archive
If you are on PowerShell V3 on a system with .NET 4.5 installed, you can use the System.IO.Compression.ZipFile class to extract the sql files.
Add-Type -Assembly system.io.compression.filesystem
[IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath)
I'd simplify it a little and use variables instead of the string literals, like this:
$shell = New-Object -COM 'Shell.Application'
$zipfile = 'C:\Temp\some.zip'
$destination = 'C:\Project'
$zip = $shell.NameSpace($zipfile)
$zip.Items() | ? { $_.Path -like '*.sql' } | % {
$shell.NameSpace($destination).CopyHere($_)
}
but other than that your code should do just fine.
Note, however, that it won't recurse into nested folders inside the zip file. You need something like this for processing nested folders as well:
function ExtractZip($fldr, $dst) {
$fldr.Items() | ? { $_.Path -like '*.sql' } | % {
$shell.NameSpace($dst).CopyHere($_)
}
$fldr.Items() | ? { $_.Type -eq 'File folder' } | % {
ExtractZip $_.GetFolder $dst
}
}
ExtractZip $shell.NameSpace($zipfile) $destination
I am writing a PowerShell script to create several directories if they do not exist.
The filesystem looks similar to this
D:\
D:\TopDirec\SubDirec\Project1\Revision1\Reports\
D:\TopDirec\SubDirec\Project2\Revision1\
D:\TopDirec\SubDirec\Project3\Revision1\
Each project folder has multiple revisions.
Each revision folder needs a Reports folder.
Some of the "revisions" folders already contain a Reports folder; however, most do not.
I need to write a script that runs daily to create these folders for each directory.
I am able to write the script to create a folder, but creating several folders is problematic.
Try the -Force parameter:
New-Item -ItemType Directory -Force -Path C:\Path\That\May\Or\May\Not\Exist
You can use Test-Path -PathType Container to check first.
See the New-Item MSDN help article for more details.
$path = "C:\temp\NewFolder"
If(!(test-path -PathType container $path))
{
New-Item -ItemType Directory -Path $path
}
Test-Path -PathType container checks to see if the path exists and is a directory. When it does not, it will create a new directory. If the path exists but is a file, New-Item will raise an error (you can overwrite the file by using the -force argument if you are risky).
[System.IO.Directory]::CreateDirectory('full path to directory')
This internally checks for directory existence, and creates one, if there is no directory. Just one line and native .NET method working perfectly.
Use:
$path = "C:\temp\"
If (!(test-path $path))
{
md $path
}
The first line creates a variable named $path and assigns it the string value of "C:\temp"
The second line is an If statement which relies on the Test-Path cmdlet to check if the variable $path does not exist. The not exists is qualified using the ! symbol.
Third line: If the path stored in the string above is not found, the code between the curly brackets will be run.
md is the short version of typing out: New-Item -ItemType Directory -Path $path
Note: I have not tested using the -Force parameter with the below to see if there is undesirable behavior if the path already exists.
New-Item -ItemType Directory -Path $path
The following code snippet helps you to create a complete path.
Function GenerateFolder($path) {
$global:foldPath = $null
foreach($foldername in $path.split("\")) {
$global:foldPath += ($foldername+"\")
if (!(Test-Path $global:foldPath)){
New-Item -ItemType Directory -Path $global:foldPath
# Write-Host "$global:foldPath Folder Created Successfully"
}
}
}
The above function split the path you passed to the function and will check each folder whether it exists or not. If it does not exist it will create the respective folder until the target/final folder created.
To call the function, use below statement:
GenerateFolder "H:\Desktop\Nithesh\SrcFolder"
I had the exact same problem. You can use something like this:
$local = Get-Location;
$final_local = "C:\Processing";
if(!$local.Equals("C:\"))
{
cd "C:\";
if((Test-Path $final_local) -eq 0)
{
mkdir $final_local;
cd $final_local;
liga;
}
## If path already exists
## DB Connect
elseif ((Test-Path $final_local) -eq 1)
{
cd $final_local;
echo $final_local;
liga; (function created by you TODO something)
}
}
When you specify the -Force flag, PowerShell will not complain if the folder already exists.
One-liner:
Get-ChildItem D:\TopDirec\SubDirec\Project* | `
%{ Get-ChildItem $_.FullName -Filter Revision* } | `
%{ New-Item -ItemType Directory -Force -Path (Join-Path $_.FullName "Reports") }
BTW, for scheduling the task please check out this link: Scheduling Background Jobs.
There are three ways I know to create a directory using PowerShell:
Method 1: PS C:\> New-Item -ItemType Directory -path "C:\livingston"
Method 2: PS C:\> [system.io.directory]::CreateDirectory("C:\livingston")
Method 3: PS C:\> md "C:\livingston"
From your situation it sounds like you need to create a "Revision#" folder once a day with a "Reports" folder in there. If that's the case, you just need to know what the next revision number is. Write a function that gets the next revision number, Get-NextRevisionNumber. Or you could do something like this:
foreach($Project in (Get-ChildItem "D:\TopDirec" -Directory)){
# Select all the Revision folders from the project folder.
$Revisions = Get-ChildItem "$($Project.Fullname)\Revision*" -Directory
# The next revision number is just going to be one more than the highest number.
# You need to cast the string in the first pipeline to an int so Sort-Object works.
# If you sort it descending the first number will be the biggest so you select that one.
# Once you have the highest revision number you just add one to it.
$NextRevision = ($Revisions.Name | Foreach-Object {[int]$_.Replace('Revision','')} | Sort-Object -Descending | Select-Object -First 1)+1
# Now in this we kill two birds with one stone.
# It will create the "Reports" folder but it also creates "Revision#" folder too.
New-Item -Path "$($Project.Fullname)\Revision$NextRevision\Reports" -Type Directory
# Move on to the next project folder.
# This untested example loop requires PowerShell version 3.0.
}
PowerShell 3.0 installation.
Here's a simple one that worked for me. It checks whether the path exists, and if it doesn't, it will create not only the root path, but all sub-directories also:
$rptpath = "C:\temp\reports\exchange"
if (!(test-path -path $rptpath)) {new-item -path $rptpath -itemtype directory}
I wanted to be able to easily let users create a default profile for PowerShell to override some settings, and ended up with the following one-liner (multiple statements yes, but can be pasted into PowerShell and executed at once, which was the main goal):
cls; [string]$filePath = $profile; [string]$fileContents = '<our standard settings>'; if(!(Test-Path $filePath)){md -Force ([System.IO.Path]::GetDirectoryName($filePath)) | Out-Null; $fileContents | sc $filePath; Write-Host 'File created!'; } else { Write-Warning 'File already exists!' };
For readability, here's how I would do it in a .ps1 file instead:
cls; # Clear console to better notice the results
[string]$filePath = $profile; # Declared as string, to allow the use of texts without plings and still not fail.
[string]$fileContents = '<our standard settings>'; # Statements can now be written on individual lines, instead of semicolon separated.
if(!(Test-Path $filePath)) {
New-Item -Force ([System.IO.Path]::GetDirectoryName($filePath)) | Out-Null; # Ignore output of creating directory
$fileContents | Set-Content $filePath; # Creates a new file with the input
Write-Host 'File created!';
}
else {
Write-Warning "File already exists! To remove the file, run the command: Remove-Item $filePath";
};
$mWarningColor = 'Red'
<#
.SYNOPSIS
Creates a new directory.
.DESCRIPTION
Creates a new directory. If the directory already exists, the directory will
not be overwritten. Instead a warning message that the directory already
exists will be output.
.OUTPUT
If the directory already exists, the directory will not be overwritten.
Instead a warning message that the directory already exists will be output.
.EXAMPLE
Sal-New-Directory -DirectoryPath '.\output'
#>
function Sal-New-Directory {
param(
[parameter(mandatory=$true)]
[String]
$DirectoryPath
)
$ErrorActionPreference = "Stop"
try {
if (!(Test-Path -Path $DirectoryPath -PathType Container)) {
# Sal-New-Directory is not designed to take multiple
# directories. However, we use foreach to supress the native output
# and substitute with a custom message.
New-Item -Path $DirectoryPath -ItemType Container | `
foreach {'Created ' + $_.FullName}
} else {
Write-Host "$DirectoryPath already exists and" `
"so will not be (re)created." `
-ForegroundColor $mWarningColor
}
} finally {
$ErrorActionPreference = "Continue"
}
}
"Sal" is just an arbitrary prefix for my own library. You could remove it or replace it with your own.
Another example (place here because it otherwise ruins stackoverflow syntax highlighting):
Sal-New-Directory -DirectoryPath ($mCARootDir + "private\")
Example, create a 'Reports' folder inside of the script's folder.
$ReportsDir = $PSScriptRoot + '\Reports'
$CreateReportsDir = [System.IO.Directory]::CreateDirectory($ReportsDir)