I started learning Powershell yesterday.
I want to write a script that run batches in multiple folders, however only if subfolder contain work_folder.
I have the following folder structure
*folder1
--work_folder
--script.bat
*folder2
--script.bat
*folder3
--script.bat
*test.ps1
powershell
cd C:\a\test\
Get-ChildItem -Path $PWD\*\script.bat | ForEach-Object {
if ( -not ( Test-Path $PWD\*\work_folder -PathType Container ) )
{
Write-host "Not exist"
return
} else {
Write-host "Exist"
}
& $_
}
If I use
powershell
$myPath = "$PWD\*\"
it will not work as I want.
Please give me a hint or example.
There's a couple problems with your code. Because you use the return keyword if the work_folder directory doesn't exist, your script ends entirely on the first folder it spots like this. I think you meant to use the continue keyword, which would skip to the next iteration of the loop.
Also, you were iterating on every instance of script.bat, when you should have been iterating on every directory containing script.bat.
You can write your code a lot cleaner if you do it this way:
(Get-ChildItem -Path $PWD -Directory).FullName | ForEach-Object {
if (Test-Path "$_/work_folder" -PathType Container)
{
Write-host "Exist"
& "$_/script.bat"
} else {
Write-host "Not exist"
}
}
How can you use Test-Path (or any other function) to test if a folder exists - ignoring any trailing file names?
I need this to test if the target folder exists in order to write an output file to it. My script accepts a "full path" parameter like "C:\Temp\myexport.csv", and I only need to know that C:\Temp exists in order to be able to create the file.
Thank you
try this :
$OutFile = "C:\Temp\myexport.csv"
$Dir=[System.IO.Path]::GetDirectoryName($OutFile)
if(!(Test-Path $Dir)) {
Write-Host "Invalid path"
}
Nevermind.. I didn't Google hard enough:
$OutFile = "C:\Temp\myexport.csv"
if(!Test-Path -Path (Split-Path -Path $OutFile)) {
Write-Host "Invalid path"
}
SourceFile.txt has the below
<somepath>\filename_$tminus1.csv
The below is the script I've written.
$date = [DateTime]::ParseExact("21-Aug-18", "dd-MMM-yy", $null)
if ($date.DayOfWeek -eq "Monday") {
$tminus1 = $date.AddDays(-3).ToString('yyyyMMdd')
$tminus2 = $date.AddDays(-4).ToString('yyyyMMdd')
Write-Host "Date vaulues set for Monday"
} else {
$tminus1 = $date.AddDays(-1).ToString('yyyyMMdd')
$tminus2 = $date.AddDays(-2).ToString('yyyyMMdd')
Write-Host "Date vaulues set for Non-Monday"
}
$files = Get-Content -Path $PSScriptRoot\SourceFile.txt
foreach ($file in $files) {
Convert-Path $file
if (-not (Test-Path $file)) {
echo ""
Write-Host "$file doesn't exist"
} else {
echo ""
Write-Host "$file exists"
}
}
The Test-Path cmdlet doesn't get the variable value substituted while looking for the file. It prints the below.
<somepath>\filename_$tminus1.csv doesn't exist
I expect the $tminus1 value gets substituted as 20180823 so that the else part prints the below.
<somepath>\filename_$tminus1.csv exists
The problem is when you pull the text in from your CSV it is treated as a literal, as if it as was a single quoted string. You can force PowerShell to re-evalueate the string like so:
$file = $ExecutionContext.InvokeCommand.ExpandString($file)
Or an alternative method would be to change the text in the CSV to this:
<somepath>\filename_{0}.csv
You can then use the format string to apply the variable like so:
$file = $file -f $tminus1
When you read strings from a file, PowerShell does not perform the normal interpolation it does for strings in your code. One way to get around this is to use Invoke-Expression.
Say you have a file (paths.txt) with these paths in it:
C:\Data\$dir1
C:\Data\$dir2
You can get the real paths like this:
$dir1 = "Directory1"
$dir2 = "Directory2"
Get-Content paths.txt |
ForEach-Object {
Invoke-Expression """$_"""
}
This will output:
C:\Data\Directory1
C:\Data\Directory2
Obviously, you can capture these to a variable, send them along the pipeline, etc, instead of just outputting them.
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.
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 }