I want to write a function that creates a folder (if it doesn't exist) with specified name inside specified folder.
It's turned out that depending on calling New-Item
function return different values. And I can't figure out how it's related to New-Item
actually.
$folderPath = "C:\tmp"
function CreateFolder([string] $name, [string] $parentFolder)
{
$path = "$parentFolder\$name"
if(!(Test-Path $path))
{
New-Item -path $parentFolder -name $name -itemtype Directory
}
return $path
}
$FOLDER_NAME = "folder1"
$destination = CreateFolder $FOLDER_NAME $folderPath
echo $destination.GetType()
If folder1 doesn't exist it'll return:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Otherwise:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
It wouldn't be an issue unless Move-Item
supported Object[]
as -destination
parameter.
echo $destination
returns:
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\tmp\folder1
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\tmp
PSChildName : folder1
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Name : folder1
Parent : tmp
Exists : True
Root : C:\
FullName : C:\tmp\folder1
Extension :
CreationTime : 13.08.2015 10:53:11
CreationTimeUtc : 13.08.2015 7:53:11
LastAccessTime : 13.08.2015 10:53:11
LastAccessTimeUtc : 13.08.2015 7:53:11
LastWriteTime : 13.08.2015 10:53:11
LastWriteTimeUtc : 13.08.2015 7:53:11
Attributes : Directory, NotContentIndexed
BaseName : folder1
Target :
LinkType :
Mode : d-----
The only solution I found is not use the function:
$folderPath = "C:\tmp"
$FOLDER_NAME = "folder1"
$destination = "$folderPath\$FOLDER_NAME"
if(!(Test-Path $destination))
{
New-Item -path $folderPath -name $FOLDER_NAME -itemtype Directory
}
Move-Item -path "C:\tmp\file1" -destination $destination
If folder1 doesn't exist:
Каталог: C:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 13.08.2015 11:06 folder1
MemberType : TypeInfo
DeclaringType :
DeclaringMethod :
ReflectedType :
StructLayoutAttribute : System.Runtime.InteropServices.StructLayoutAttribute
GUID : 296afbff-1b0b-3ff5-9d6c-4e7e599f8b57
Module : CommonLanguageRuntimeLibrary
Assembly : mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
TypeHandle : System.RuntimeTypeHandle
FullName : System.String
Namespace : System
AssemblyQualifiedName : System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
BaseType : System.Object
TypeInitializer :
IsNested : False
Attributes : AutoLayout, AnsiClass, Class, Public, Sealed, Serializable, BeforeFieldInit
GenericParameterAttributes :
IsVisible : True
IsNotPublic : False
IsPublic : True
IsNestedPublic : False
IsNestedPrivate : False
IsNestedFamily : False
IsNestedAssembly : False
IsNestedFamANDAssem : False
IsNestedFamORAssem : False
IsAutoLayout : True
IsLayoutSequential : False
IsExplicitLayout : False
IsClass : True
IsInterface : False
IsValueType : False
IsAbstract : False
IsSealed : True
IsEnum : False
IsSpecialName : False
IsImport : False
IsSerializable : True
IsAnsiClass : True
IsUnicodeClass : False
IsAutoClass : False
IsArray : False
IsGenericType : False
IsGenericTypeDefinition : False
IsConstructedGenericType : False
IsGenericParameter : False
GenericParameterPosition :
ContainsGenericParameters : False
IsByRef : False
IsPointer : False
IsPrimitive : False
IsCOMObject : False
HasElementType : False
IsContextful : False
IsMarshalByRef : False
GenericTypeArguments : {}
IsSecurityCritical : False
IsSecuritySafeCritical : False
IsSecurityTransparent : True
UnderlyingSystemType : System.String
Name : String
CustomAttributes : {[System.SerializableAttribute()], [System.Reflection.DefaultMemberAttribute("Chars")], [System.Runtime.InteropServices.ComVisibleAttribute(
(Boolean)True)], [__DynamicallyInvokableAttribute()]}
MetadataToken : 33554536
Otherwise:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Working example after Ansgar's recommendations:
$folderPath = "C:\tmp"
function GetDestination {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter(Mandatory=$true)]
[string]$ParentFolder
)
$path = Join-Path $ParentFolder $Name
if(Test-Path -LiteralPath $path) {
Get-Item -LiteralPath $path
} else {
New-Item -Path $ParentFolder -Name $Name -ItemType Directory
}
}
$FOLDER_NAME = "folder1"
$destination = GetDestination $FOLDER_NAME $folderPath
Move-Item -LiteralPath "C:\tmp\file1" -Destination $destination
New-Item
returns the created object, and PowerShell functions return all non-captured output, not just the argument of the return
keyword.
In Windows PowerShell, the results of each statement are returned as output, even without a statement that contains the Return keyword. Languages like C or C# return only the value or values that are specified by the Return keyword.
This means that if the path already exists your function returns just the path string. Otherwise it returns an array with the new DirectoryInfo
object and the path string.
Depending on whether you want a string or a DirectoryInfo
object returned you can either suppress the output of New-Item
:
if (!(Test-Path $path)) {
New-Item -Path $parentFolder -Name $name -ItemType Directory | Out-Null
}
return $path
or remove the return
statement and instead add an else
branch where you call Get-Item
on the path:
if (!(Test-Path $path)) {
New-Item -Path $parentFolder -Name $name -ItemType Directory
} else {
Get-Item $path
}
On a more general note, I'd recommend some modifications to your code:
-LiteralPath
parameter with Test-Path
, so you don't run into problems when the path contains special characters like square brackets.Join-Path
for constructing a path, as that will automatically take care of path separators.Example:
function New-Folder {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$Name,
[Parameter(Mandatory=$true)]
[string]$ParentFolder
)
$path = Join-Path $ParentFolder $Name
if (-not (Test-Path -LiteralPath $path)) {
New-Item -Path $ParentFolder -Name $Name -ItemType Directory
} else {
Get-Item -LiteralPath $path
}
}