Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New-Item changes function return value

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
like image 558
sad comrade Avatar asked Aug 13 '15 08:08

sad comrade


People also ask

In what situation does a function return a value?

If a function is defined as having a return type of void , it should not return a value. In C++, a function which is defined as having a return type of void , or is a constructor or destructor, must not return a value. If a function is defined as having a return type other than void , it should return a value.

What does it mean for a function to return a value?

A return is a value that a function returns to the calling script or function when it completes its task. A return value can be any one of the four variable types: handle, integer, object, or string. The type of value your function returns depends largely on the task it performs.

When a function does not have a return statement What does the function return when called?

If no return statement appears in a function definition, control automatically returns to the calling function after the last statement of the called function is executed. In this case, the return value of the called function is undefined.

How does a function return a value in PowerShell?

If you are inside of a function and return a value with the return keyword, the function will return that value and exit the function. The return keyword causes the function to exit after outputting the first process. PowerShell will then generate output for both processes.


1 Answers

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:

  • Use the -LiteralPath parameter with Test-Path, so you don't run into problems when the path contains special characters like square brackets.
  • Use Join-Path for constructing a path, as that will automatically take care of path separators.
  • Use advanced parameter definitions, which will allow you to make parameters mandatory, define parameter order, validate input, and lots of other things.
  • Use the PowerShell naming convention with approved verbs for your function names.

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
  }
}
like image 102
Ansgar Wiechers Avatar answered Sep 19 '22 14:09

Ansgar Wiechers