none
How to get the script name of the parent script

    Вопрос

  • Hi Everyone,

    I am very new to PowerShell and am running into an issue that I hope is an easy one:

     

    The background is as follows:

     

    I have 2 .ps1 files in this situation:

    The first is ScriptLib.ps1

    The Second is TestScript.ps1

     

    Inside of ScriptLib I have some code that looks like the following:

    if ($scriptlibmode -eq "start")
    {
        $SCRIPTNAME=$Script:MyInvocation.MyCommand.Name;

    }

    In the TestScript I have the following:

    # =============================================================================
    # Script Setup
    # =============================================================================
    # Make sure that the ScriptLib is loaded before sourcing this template.
    . C:\jobsched\JobScripts\Library\Microsoft\ScriptLib start;

    Write-Host "Script Name: $SCRIPTNAME.";

    . C:\jobsched\JobScripts\Library\Microsoft\ScriptLib end;

     

    The goal is to have $SCRIPTNAME = TestScript.ps1 without having to define it within the TestScript file.

    However, the current code writes ScriptLib.ps1

    I have used this technique successfully in other scripting languages like korn.  However, I am missing something here.

     

    Can, what I am trying to accomplished be done?  Ideally without the need for globals.

    If so, how would I go about doing it?

     

    Thanks in advance.

     

    pgduke

    3 октября 2012 г. 23:12

Ответы

  • The equivalent in PowerShell of the OP's code would be something like this.

    File A.ps1:


    $ScriptName = '$MyInvocation.MyCommand.Name'
    & '.\File B.ps1'

    File B.ps1:


    invoke-expression $ScriptName
    

    Which leads to my last question: Why use the variable indirection when you can just write $MyInvocation.MyCommand.Name inside File B.ps1?

    Bill

    5 октября 2012 г. 14:19
    Модератор

Все ответы

  • Sorry - your question does not make any sense.

    $SCRIPNAME= ....

    This assigns a variable in the current context to some value. IN you example that value is $Script:MyInvocation.MyCommand.Name i sthe vlaue of teh current command.

    This can never be anything else.  How could it magincvally become some value outside of the script.

    A child can know about itself and its children.  A child cannot know what is in the parent context without the parent telling the child.

    An environment variable can be shared.  There is no possibility of telepathy.

    This same thing is tue in a Lorn shell script. Perhaps you are thinking of some other system.

    A child script cannot sen messages back to its parent magically.  A child can set an envionment carailbe at time and a child can send back a result.

    $SCRIPTNAME = .\myscript.ps1

    You can also use a file, the registry and email to send back messages from the other side.


    ¯\_(ツ)_/¯

    4 октября 2012 г. 0:51
  • My question is, why do you need to do it?

    Bill

    4 октября 2012 г. 14:19
    Модератор
  • My question is, why do you need to do it?

    Bill

    You are very curious about motives, Bill.  I am too, but I just get a kick out of answering a script question correctly, regardless of its intended use.  I say, "If you want a script to format your C drive", here it is, but you'd better know what you're doing. 

    I suppose I should put a disclaimer in my signature, but really, I just enjoy watching the fireworks!  No, that's not altogether true, I do include -Whatif wherever appropriate, which confuses some posters, thinking the script isn't working.


    Grant Ward, a.k.a. Bigteddy

    4 октября 2012 г. 14:29
  • Grant - I believe the use of asking for 'Why" is not to dtermine "motives" or ",otivation" nut rather to try and tease out what the tenchnical need is in the narrowest sense.

    If we know why this information is needed perhaps we can find a way to provide the requied impact even if the exact item requested is not possible.

    I, to, like Bill, would like to know why.  What is it that this will help the OP to accomplish in the script.  KNowing th e name of the parent script may be useful but passing and arguments may be all that is needed.

    I suspect that the question is badly worded becuse the assumption that this, as asked,  can be done in Korne Shell is not corrct.  Unix shells do not know about teh parent or caller.  A process can find it's parent process.  A shell in not a process.  Prehaps getting the parent and the commandline that ran the parent is helpful but it wil not tell us the command run by the parent.  If the parent is a shell then we can retrieve the arguments that launched the parent.

    This is the same in Unix and in Windows.

    All of this makes me want to knwo "why".


    ¯\_(ツ)_/¯

    4 октября 2012 г. 14:39
  • You are very curious about motives, Bill.

    You're right, I am. <grin> Another thing I like to say is, "what's provoking the question?"

    Sometimes the questioner has an idea about how to accomplish something, and they think it can only be accomplished using A, so they focus on A. It may be that A doesn't solve their problem, or solves some other problem. I hate wasting time on a "solution" that doesn't actually address the problem.

    But I do see your point of trying to answer it just to see how it's done. This is also valuable at times, even if it doesn't solve the problem at hand -- it may solve a different problem later.

    Bill

    4 октября 2012 г. 15:01
    Модератор
  • First off I am sorry if I did not phrase the question properly.  Nor did I explain the end goal sufficiently in terms that were not clearly stated so I will try again.  This time from the perspective of an environment I know through an example I can get at whether or not this can be accomplished in Power Shell:

    In a korn shell script I can do the following:

    File A contains:

    export SCRIPTNAME=${0} 

    SCRIPTNAME will contain the name of the currently executing script.

    File B contains:

    . ./File A

    echo  ${SCRIPTNAME}

    In the above situation the output will be:

    File B

    The question I was attempting to ask is Can this sort of arrangement be accomplished?  and if so, how?

    If this can be accomplished then I can centralize a set of variables in a common location and have them initialize to the proper values at run time.  If this cannot be accomplished then I will essentially need to run 2 sets of variables one initialized at the File A level and others at the File B level. 

    The intention of the variables is to be initialized and available at run time.  Ideally in a way that does not use globals or put variables in the script that do not need to be there.  I am attempting to design a scripting framework in which the framework will maintain a group of useful variables (i.e. ScriptName) available to the user that the user does not have to define or initialize.

    I have successfully accomplished this in other scripting languages.  I am sure I am missing a key concept here.  I hope that this clarifies the question and does not muddy the water?  

    Thanks to all of you for the help.

     

    4 октября 2012 г. 16:19
  • File A contains:

    export SCRIPTNAME=${0}

    SCRIPTNAME will contain the name of the currently executing script.

    File B contains:

    . ./File A

    echo ${SCRIPTNAME}

    In the above situation the output will be:

    File B

    Did you mean to say that the output will be 'File A'?

    Doesn't the 'export' command create an environment variable?

    If you need a variable to be visible in all scopes, you can use the Global scope modifier. See


    help about_Scopes

    for more information.

    Bill

    4 октября 2012 г. 17:49
    Модератор
  • First off I am sorry if I did not phrase the question properly.  Nor did I explain the end goal sufficiently in terms that were not clearly stated so I will try again.  This time from the perspective of an environment I know through an example I can get at whether or not this can be accomplished in Power Shell:

    In a korn shell script I can do the following:

    File A contains:

    export SCRIPTNAME=${0} 

    SCRIPTNAME will contain the name of the currently executing script.

    File B contains:

    . ./File A

    echo  ${SCRIPTNAME}

    In the above situation the output will be:

    File B

    The question I was attempting to ask is Can this sort of arrangement be accomplished?  and if so, how?

    If this can be accomplished then I can centralize a set of variables in a common location and have them initialize to the proper values at run time.  If this cannot be accomplished then I will essentially need to run 2 sets of variables one initialized at the File A level and others at the File B level. 

    The intention of the variables is to be initialized and available at run time.  Ideally in a way that does not use globals or put variables in the script that do not need to be there.  I am attempting to design a scripting framework in which the framework will maintain a group of useful variables (i.e. ScriptName) available to the user that the user does not have to define or initialize.

    I have successfully accomplished this in other scripting languages.  I am sure I am missing a key concept here.  I hope that this clarifies the question and does not muddy the water?  

    Thanks to all of you for the help.


    Like Bill, I suspect you meant to say that the desired output was "File A" (i.e. the name of the parent or calling script). If you just want each script to know its own name, that is directly available with the $MyInvocation variable.

    It is still not quite clear what it is you feel you need this to accomplish. For example, when you "centralize a set of fariabls in a common location", does this mean that you are defining them all in one script, and expecting they will be available to other scripts that might, for example, call that one? If so, you can certainly do it like this:

    in fileA.ps1:

        $var1 = 123
        $var2 = 234

    in fileB.ps1:

        . /fileA.ps1
        $sum = $var1 + $var2

    But if one of the "variables" is needed to supply the name of the script containing the variables, there is no way to get that information from that other script without knowing its name. Script fileB.ps1 "knows" the name of that other script, because it is embedded in the first line of fileB.PS1

    If that filename variable is to contain the name of fileB.ps1, fileA.ps1 cannot possibly know that, so fileB.ps1 will have to create it like this:

        $MyName = $MyInvocation.MyCommand.Name


    Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.

    4 октября 2012 г. 18:27
  • And conversely the exact same is true in Korn Shell scripts.  The ksh knows its own invocation but not the parent invocation except when thje parent has passed the informatio to eh child.

    Thisi setup was designed in WIndows to be pretty much identical to Unix. It is an OS mechanism and not a shell mechanism.  All processes habe a process environment.  By default a proess inherits a copy of the parent environment.  Cnges made to teh parent and child are not shared and not visible across process boundaries.

    PowerShel summarizes this in $MyInvocation and the passed envirinment block at "env:"

    dir env:

    - will list the current process environment.  If the parent has set a variable before creating the child then the child will get the variable.  Th echild cannot cahnge teh variable and send it back the the aprent.  The child can return an object of whatever complexity is required using 'return' or Write-Output.

    What we can do within PowerShell is to look at teh originating command:

    In a file (test.ps1) place the following:

    param(
       $myparam
    )
    Write-Host $MyInvocation.Line -fore green

    Now call the file like this:

    $stuffretruned=.\test.ps1

    You will see that the script can show the comandline that called it.   This is not a proeprty of teh parent but is just the line of code the parent used to execute teh current script.


    ¯\_(ツ)_/¯

    4 октября 2012 г. 19:06
  • In our enviroment I run the attached code as was indicated and the output is not File A.  It is File B.  It was not a typo.  I suspect that this either boils down to a difference in shells on Linux or a difference in how enviroments are handled.  At this point, it looks as though a global variable or some other technique is needed in Power Shell.
    4 октября 2012 г. 21:40
  • Why the indirection when you can just write $0 to get the current script's name?

    Bill

    4 октября 2012 г. 21:58
    Модератор
  • In our enviroment I run the attached code as was indicated and the output is not File A.  It is File B.  It was not a typo.  I suspect that this either boils down to a difference in shells on Linux or a difference in how enviroments are handled.  At this point, it looks as though a global variable or some other technique is needed in Power Shell.

    I rewally don';t think anyone understands what you are asking.  Ther eis nothing in the Unix environment that is different.  We get the same items just using different commands.

    The is no way in Unix to directly get the commandline that created the parent.  This is kind of running against modern system design which requires process isolation.


    ¯\_(ツ)_/¯

    4 октября 2012 г. 22:27
  • Global variables, aack! Any time I find myself thinking about using these widely, I remind myself that there simply *must* be a better approach - and, so far, I have always managed to find one.

    You seem to be fighting Powershell, as you find some of its "features" to be hindrances. I'd suggest you drop the idea of trying to initialize all your variables up front, and defer that until you need the values. I have done that extensively in VBScript using *GASP* a global variable to implement a persistent variable associated with a function in which the calculated value is cached and used on subsequent calls. Managing the namespace was simplified by basing the name of the variable on the name of the function. A function called, for example, homeServer would cache the value it calculated in a global variable called homeServer_cache.

    I've done similar in Powershell, but fortunately have been able to avoid globals by using variables of script level scope.


    Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.

    5 октября 2012 г. 0:04
  • Hey - global variables can be fun.  I know some people who like them so much that they make every variable a global variable.

    The problem her is that the OP seems to want to spawn a script as a separate process.  OI posted teh code that allows a script to see the comamnd line that launched the the current script but that was not what was being sought. 

    The $0 method was not n answer and the session doesn't seem to be what is being asked for.  That leaves only one thing.  The script is being launched as a separate process or in a different runspace as in a job.

    We also can use this:
    Write-host $MyInvocation.MyCommand

    Which will also write the name of the current script.

    The followong gets the invoking process.

    Get-Process -Id $pid|select *

    When one script calls another we have $MyInvocation.ScriptName which is blank when the first script is called from a prompt.

    Since none of these are acceptable answers I can only asume that the requirement is for an interprocess knowledge of the calling process which is not possible in Windows or in Unix.

    I say "not possible: but it is actually possible through the environment by choice or by callin ghe Debug API and asking for the process chain.  Tis can be don ein POwerSHell through the use of a compiled helper that will use C# or VB.Net to P-Invoke the debug aPI.

    Strt with teh current PID

    $pid is an automatic variable with the current process API.  Call the debug API and get the process structure which will contain the parent API.. and so forth.

    In Windows Tasklist utility can do this and in Unix tlist can do the same thing.

    Type "tasklist -?"

    A simple way to get just the IDs of teh proceswes and then use the list to deleop a chain is this:

    gwmi win32_process | ft processid,parentprocessid -auto


    ¯\_(ツ)_/¯




    • Изменено jrv 5 октября 2012 г. 1:11
    5 октября 2012 г. 1:05
  • If this can be accomplished then I can centralize a set of variables in a common location and have them initialize to the proper values at run time.  If this cannot be accomplished then I will essentially need to run 2 sets of variables one initialized at the File A level and others at the File B level. 

    The intention of the variables is to be initialized and available at run time.  Ideally in a way that does not use globals or put variables in the script that do not need to be there.  I am attempting to design a scripting framework in which the framework will maintain a group of useful variables (i.e. ScriptName) available to the user that the user does not have to define or initialize.

    Is this what you want?

    # a.ps1
    $scriptname = $MyInvocation.MyCommand
    $otherVar = 'a' +
        'b' +
        'c'
    
    


    $aFullPath = Get-Item .\a.ps1
    Invoke-Expression -Command ([io.file]::ReadAllText($aFullPath))
    
    Write-Host $scriptname
    Write-Host $otherVar
    


    5 октября 2012 г. 3:43
  • The equivalent in PowerShell of the OP's code would be something like this.

    File A.ps1:


    $ScriptName = '$MyInvocation.MyCommand.Name'
    & '.\File B.ps1'

    File B.ps1:


    invoke-expression $ScriptName
    

    Which leads to my last question: Why use the variable indirection when you can just write $MyInvocation.MyCommand.Name inside File B.ps1?

    Bill

    5 октября 2012 г. 14:19
    Модератор