How to get full remote path in Powershell? - powershell

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.

Related

How to find the directory path of a file in powershell

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

Powershell - Skip folder if it doesn't exist

I would like to run a PowerShell script by right-clicking a .PS1 file -> Run with PowerShell. The issue is that the $srcRoot includes three different parent directories, which one, some, or none may exist of 'C:\parentfolder5.5\web\','C:\parentfolder7.0\web\', and/or 'C:\parentfolder8.0\web\'. However, running the script directly in a PowerShell terminal seems to work for the parent folders that do exist even though there are errors that pop up for the parent folders that do not exist.
The goal would be to continue running the script based on the parent directories that do exist, which currently it looks like the .PS1 file stops after looking at the first item in the $srcRoot list. Below is the code being worked on:
$filterLists = '*overview*', '*summary*', '*home*', '*floor*', '*flr*', '*level*', '*lvl*', '*roof*', '*basement*', '*first*', '*second*', '*third*', '*fourth*'
$srcRoot = 'C:\parentfolder5.5\web\','C:\parentfolder7.0\web\','C:\parentfolder8.0\web\'
$dstRoot = $MyInvocation.MyCommand.Path
$params = #{
Path = LiteralPath = $srcRoot |Where-Object { Test-Path -LiteralPath $_ -PathType Container }
Filter = 'views'
Recurse = $true
Directory = $true
}
# All folders under `$srcRoot` with name 'views'
$viewsFolders = Get-ChildItem #params #this line is where the issue
seems to start when Right-clicking -> Run with PowerShell
$params.LiteralPath = $viewsFolders.FullName
$params.Filter = 'graphics'
# All folders under `$viewsFolders` with name 'graphics'
$graphicsFolders = Get-ChildItem #params
$params.Remove('Directory')
$params.LiteralPath = $graphicsFolders.FullName
$params.File = $true # Only search for Files
$params.Force = $true
$params.Remove('Filter')
# All files under `$graphicsFolders`
foreach($file in Get-ChildItem #params)
{
# Where the file name contains one of these filters
foreach($filter in $filterLists)
{
if($file.Name -like $filter)
{
#$file
Copy-Item -Path $($file.FullName) -Destination $dstRoot
# if you want to stop at the first finding
# add `break` here
}
}
}
Help on this would be greatly appreciated!
You can use Where-Object to filter the list of paths. Use Test-Path to test whether each exists and is a directory path:
$params = #{
LiteralPath = $srcRoot |Where-Object { Test-Path -LiteralPath $_ -PathType Container }
Filter = 'views'
Recurse = $true
Directory = $true
}
# this will only attempt directory paths that actually exist now
$viewsFolders = Get-ChildItem #params
Note: Use of LiteralPath (instead of Path) above is intentional - using Path will cause PowerShell to attempt to expand wildcards like ?, * or [abc], whereas -LiteralPath only take exact file/folder names.

Parse directory listing and pass to another script?

I am trying to write a PowerShell script that will loop through a directory in C:\ drive and parse the filenames with the file extension to another script to use.
Basically, the output of the directory listing should be accessible to be parsed to another script one by one. The script is a compiling script which expects an argument (parameter) to be parsed to it in order to compile the specific module (filename).
Code:
Clear-Host $Path = "C:\SandBox\"
Get-ChildItem $Path -recurse -force | ForEach { If ($_.extension -eq ".cob")
{
Write-Host $_.fullname
}
}
If ($_.extension -eq ".pco")
{
Write-Host $_.fullname }
}
You don't need to parse the output as text, that's deprecated.
Here's something that might work for you:
# getmyfiles.ps1
Param( [string])$Path = Get-Location )
dir $Path -Recurse -Force | where {
$_.Extension -in #('.cob', '.pco')
}
# this is another script that calls the above
. getmyfile.ps1 -Path c:\sandbox | foreach-object {
# $_ is a file object. I'm just printing its full path but u can do other stuff eith it
Write-host $_.Fullname
}
Clear-Host
$Path = "C:\Sandbox\"
$Items = Get-ChildItem $Path -recurse -Include "*.cob", "*.pco"
From your garbled code am guessing you want to return a list of files that have .cob and .pco file extensions. You could use the above code to gather those.
$File = $Items.name
$FullName = $items.fullname
Write-Host $Items.name
$File
$FullName
Adding the above lines will allow you to display them in various ways. You can pick the one that suites your needs then loop through them on a for-each.
As a rule its not a place for code to be writen for you, but you have tried to add some to the questions so I've taken a look. Sometimes you just want a nudge in the right direction.

In PowerShell, how can I test if a user-supplied string represents a full absolute path? [duplicate]

I'm trying to process a list of files that may or may not be up to date and may or may not yet exist. In doing so, I need to resolve the full path of an item, even though the item may be specified with relative paths. However, Resolve-Path prints an error when used with a non-existant file.
For example, What's the simplest, cleanest way to resolve ".\newdir\newfile.txt" to "C:\Current\Working\Directory\newdir\newfile.txt" in Powershell?
Note that System.IO.Path's static method use with the process's working directory - which isn't the powershell current location.
You want:
c:\path\exists\> $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(".\nonexist\foo.txt")
returns:
c:\path\exists\nonexists\foo.txt
This has the advantage of working with PSPaths, not native filesystem paths. A PSPath may not map 1-1 to a filesystem path, for example if you mount a psdrive with a multi-letter drive name.
What's a pspath?
ps c:\> new-psdrive temp filesystem c:\temp
...
ps c:\> cd temp:
ps temp:\>
temp:\ is a drive-qualified pspath that maps to a win32 (native) path of c:\temp.
-Oisin
When Resolve-Path fails due to the file not existing, the fully resolved path is accessible from the thrown error object.
You can use a function like the following to fix Resolve-Path and make it work like you expect.
function Force-Resolve-Path {
<#
.SYNOPSIS
Calls Resolve-Path but works for files that don't exist.
.REMARKS
From http://devhawk.net/blog/2010/1/22/fixing-powershells-busted-resolve-path-cmdlet
#>
param (
[string] $FileName
)
$FileName = Resolve-Path $FileName -ErrorAction SilentlyContinue `
-ErrorVariable _frperror
if (-not($FileName)) {
$FileName = $_frperror[0].TargetObject
}
return $FileName
}
I think you're on the right path. Just use [Environment]::CurrentDirectory to set .NET's notion of the process's current dir e.g.:
[Environment]::CurrentDirectory = $pwd
[IO.Path]::GetFullPath(".\xyz")
Join-Path (Resolve-Path .) newdir\newfile.txt
This has the advantage of not having to set the CLR Environment's current directory:
[IO.Path]::Combine($pwd,"non\existing\path")
NOTE
This is not functionally equivalent to x0n's answer. System.IO.Path.Combine only combines string path segments. Its main utility is keeping the developer from having to worry about slashes. GetUnresolvedProviderPathFromPSPath will traverse the input path relative to the present working directory, according to the .'s and ..'s.
I've found that the following works well enough.
$workingDirectory = Convert-Path (Resolve-Path -path ".")
$newFile = "newDir\newFile.txt"
Do-Something-With "$workingDirectory\$newFile"
Convert-Path can be used to get the path as a string, although this is not always the case. See this entry on COnvert-Path for more details.
function Get-FullName()
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline = $True)] [object[]] $Path
)
Begin{
$Path = #($Path);
}
Process{
foreach($p in $Path)
{
if($p -eq $null -or $p -match '^\s*$'){$p = [IO.Path]::GetFullPath(".");}
elseif($p -is [System.IO.FileInfo]){$p = $p.FullName;}
else{$p = [IO.Path]::GetFullPath($p);}
$p;
}
}
}
I ended up with this code in my case. I needed to create a file later in the the script, so this code presumes you have write access to the target folder.
$File = ".\newdir\newfile.txt"
If (Test-Path $File) {
$Resolved = (Resolve-Path $File).Path
} else {
New-Item $File -ItemType File | Out-Null
$Resolved = (Resolve-Path $File).Path
Remove-Item $File
}
I also enclosed New-Item in try..catch block, but that goes out of this question.
I had a similar issue where I needed to find the folder 3 levels up from a folder that does not exist yet to determine the name for a new folder I wanted to create... It's complicated. Anyway, this is what I ended up doing:
($path -split "\\" | select -SkipLast 3) -join "\\"
You can just set the -errorAction to "SilentlyContinue" and use Resolve-Path
5 > (Resolve-Path .\AllFilerData.xml -ea 0).Path
C:\Users\Andy.Schneider\Documents\WindowsPowerShell\Scripts\AllFilerData.xml
6 > (Resolve-Path .\DoesNotExist -ea 0).Path
7 >
There is an accepted answer here, but it is quite lengthy and there is a simpler alternative available.
In any recent version of Powershell, you can use Test-Path -IsValid -Path 'C:\Probably Fake\Path.txt'
This simply verifies that there are no illegal characters in the path and that the path could be used to store a file. If the target doesn't exist, Test-Path won't care in this instance -- it's only being asked to test if the provided path is potentially valid.
Both most popular answers don't work correctly on paths on not existing drives.
function NormalizePath($filename)
{
$filename += '\'
$filename = $filename -replace '\\(\.?\\)+','\'
while ($filename -match '\\([^\\.]|\.[^\\.]|\.\.[^\\])[^\\]*\\\.\.\\') {
$filename = $filename -replace '\\([^\\.]|\.[^\\.]|\.\.[^\\])[^\\]*\\\.\.\\','\'
}
return $filename.TrimEnd('\')
}
Check if the file exists before resolving:
if(Test-Path .\newdir\newfile.txt) { (Resolve-Path .\newdir\newfile.txt).Path }

How to convert absolute path to relative path in PowerShell?

I'd like to convert a path to a relative path in a PowerShell script.
How do I do this using PowerShell?
For example:
Path to convert: c:\documents\mynicefiles\afile.txt
Reference path: c:\documents
Result: mynicefiles\afile.txt
And
Path to convert: c:\documents\myproject1\afile.txt
Reference path: c:\documents\myproject2
Result: ..\myproject1\afile.txt
I found something built in, Resolve-Path:
Resolve-Path -Relative
This returns the path relative to the current location. A simple usage:
$root = "C:\Users\Dave\"
$current = "C:\Users\Dave\Documents\"
$tmp = Get-Location
Set-Location $root
Resolve-Path -relative $current
Set-Location $tmp
Using the built-in System.IO.Path.GetRelativePath is simpler than the accepted answer:
[System.IO.Path]::GetRelativePath($relativeTo, $path)
There is a good answer here, but it changes your current directory (it reverts back), but if you really need to isolate that process, code example below can help. It does the same thing, just in a new PowerShell instance.
function Get-RelativePath($path, $relativeTo) {
$powershell = (Get-Process -PID $PID | Get-Item)
if ([string]::IsNullOrEmpty($powershell)) {
$powershell = "powershell.exe"
}
& $powershell -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "& { Set-Location `"$relativeTo`"; Resolve-Path `"$path`" -Relative}"
}
It's really slow though, you should really use the other version unless you absolutely have to use this.
Sometimes I have multiple "roots" from which I want to generate relative file paths. I have found Resolve-Path -Relative unusable in this kind of situation. Changing a global setting, current location, in order to generate a relative file path seems error-prone and (if you're writing parallel code) possibly not thread-safe.
The following should work in early or recent versions of Powershell and Powershell Core, doesn't change your current directory, even temporarily, and is OS-independent and thread-safe.
It doesn't address the second example from OP (inserting .. as required.)
function Get-RelativePath {
param($path, $relativeTo)
# strip trailing slash
$relativeTo = Join-Path `
(Split-Path -Parent $relativeTo) `
(Split-Path -Leaf $relativeTo)
$relPath = Split-Path -Leaf $path
$path = Split-Path -Parent $path
do {
$leaf = Split-Path -Leaf $path
$relPath = Join-Path $leaf $relPath
$path = Split-Path -Parent $path
} until (($path -eq $relativeTo) -Or ($path.Length -eq 0))
$relPath
}
An example:
PS> $configRoot = 'C:\Users\P799634t\code\RMP\v2\AWD'
PS> $queryPath = 'C:\Users\P799634t\code\RMP\v2\AWD\config_queries\LOAD_UNQ_QUEUE_QUERY2.sql'
PS> Write-Host (Get-RelativePath $queryPath $configRoot)
config_queries\LOAD_UNQ_QUEUE_QUERY2.sql
It behaves reasonably when one file path is not a sub-path of the other:
PS> $root = 'C:\Users\P799634t\code\RMP\v2\AWD'
PS> $notRelated = 'E:\path\to\origami'
PS> Write-Host (Get-RelativePath $notRelated $root)
E:\path\to\origami
A quick and easy way would be :
$current -replace [regex]::Escape($root), '.'
Or if you want the relative path from your actual current location
$path -replace [regex]::Escape((pwd).Path), '.'
This assumes all your paths are valid.
Here is an alternative approach
$pathToConvert1 = "c:\documents\mynicefiles\afile.txt"
$referencePath1 = "c:\documents"
$result1 = $pathToConvert1.Substring($referencePath1.Length + 1)
#$result1: mynicefiles\afile.txt
And
$pathToConvert2 = "c:\documents\myproject1\afile.txt"
#$referencePath2 = "c:\documents\myproject2"
$result2 = "..\myproject" + [regex]::Replace($pathToConvert2 , ".*\d+", '')
#$result2: ..\myproject\afile.txt
Note: in the second case ref path wasn't used.