Discussion:
Include another script, keep variables in included script?
(too old to reply)
pschmidt
2008-08-15 21:24:05 UTC
Permalink
I want to run or include a PS script from within another PS script. But
want the values set in the 2nd one to be available in the first one.

For example:
tmp.ps1----
$Test = "Here"
$There = "There"

echo "TEST: $test"
echo "There: $there"
------------------------

Now tmp2.ps1-------------
echo "TEST: " $Test

.\tmp.ps1

echo "TEST: " $Test
echo "There: " $there
-------------
.\tmp2.ps1
TEST:
TEST: Here
There: There
TEST:
There:
--------------------------

I'd like the values set in 'tmp' to be available in tmp2. How do I do this?

Thx,
Kryten
2008-08-15 23:40:33 UTC
Permalink
This is just one possible way:-
(Both scripts are in my 'D:\Posh' directory.

tmp.ps1
-----------
$Test = "Here"
$There = "There"
---------------------

tmp2.ps1
------------
&D:\Posh\tmp.ps1
" These variables and values are from tmp:"
Write-Host "$Test & $There"
----------------------------

Nowwith your current directory set as D:\Posh do
.\tmp2.ps1
You should get:-
These variables and values are from tmp:
Here & There

Essentially just using the 'Call' Operator '&' to include the tmp.ps1

Hope this helps,
Stuart
Kryten
2008-08-15 23:46:43 UTC
Permalink
Hi,
One way..

Both tmps.ps1 and tmp2.ps1 are in my "D:\Posh" directory.

tmp.ps1 looks like:-

$Test = "Here"
$There = "There"

tmp2.ps1 looks like:-

&D:\Posh\tmp.ps1
" These variables and values are from tmp"
Write-Host "$Test & $There"

Now from PS, in the D:\Posh directory ( or whatever you are using )
do:-
.\tmp2.ps1
You should get:-

These variables and values are from tmp:
Here & There

Check out the 'Call' Operator and dot sourcing.

Hope this helps,
Stuart
Kryten
2008-08-15 23:54:57 UTC
Permalink
Hi,
One more thing I didn't mention is scope. In the
previous example, for what you are trying to do I think the
variables in tmp.ps1 will need to be defined with a global scope.
Like so:-

tmp.ps1
------------------------------
$global:Test = "Here"
$global:There = "There"
------------------------------

This makes the variables available across the entire powershell
session.

Cheers,
Stuart
Kryten
2008-08-15 23:58:05 UTC
Permalink
Sorry about the double post.
I thought Googlegroups was playing up as my first reply didn't appear
and I thought it had gone missing so I posted again. Doh!
tojo2000
2008-08-16 01:29:28 UTC
Permalink
I want to run or include a PS script from within another PS script.  But
want the values set in the 2nd one to be available in the first one.  
tmp.ps1----
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
------------------------
Now tmp2.ps1-------------
echo "TEST: " $Test
.\tmp.ps1
echo "TEST: " $Test
echo "There: " $there
-------------
.\tmp2.ps1
TEST: Here
There: There
--------------------------
I'd like the values set in 'tmp' to be available in tmp2.  How do I do this?
Thx,
In addition to Kryten's answer, you can use the dotsource operator to
essentially run the script in the same scope.

You can think of the dotsource operator as injecting the script into
the current scope as if it were pasted in at that location. Normally
when you run a script, any variables and functions created inside the
script are created in the script's scope, and disappear with the
script as soon as it is done running. The dotsource operator runs the
script in the current scope.

The dotsource operator is a dot '.'.

To use it you put a dot and a space before the script. In this
example my script has a single variable, $scriptvar, that is set to
true inside scriptvar.ps1:

C:\Users\timjohnson\Documents> .\scriptvar.ps1
C:\Users\timjohnson\Documents> $scriptvar
C:\Users\timjohnson\Documents> . .\scriptvar.ps1
C:\Users\timjohnson\Documents> $scriptvar
True

One caveat: since it will run the script in the current scope you
can't run the script inside a script block or the imported variables
will disappear when the scriptblock is exited. In that case you'll
have to declare your variables as global.

Here's a script I came up with for importing scripts that I use, where
I try to solve the problem of having hardcoded script names, but
because I call the dotsource operator from inside the function I have
to declare all of my functions and variables that I want to import in
the global scope: http://tasteofpowershell.blogspot.com/2008/07/importing-scripts-as-libraries-part.html

I'd be interested to hear what other people have done.

Notet: If you're using PowerShell v2 CTP2 rather than v1 then you
should read up on creating modules, which is a new feature that
resolves most of these issues.
Kiron
2008-08-16 03:18:56 UTC
Permalink
You can export the variables to tmp2's scope:

-< tmp.ps1 >-
param ([string[]]$exportVariable)

$Test = "Here"
$There = "There"

echo "TEST: $test"
echo "There: $there"

if ($exportVariable) {
foreach ($var in $exportVariable) {
set-variable $var (get-variable $var).value -scope 1
}
}
-< tmp.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
-< tmp2.ps1 >-
# $test echo "TEST: $Test"

# assuming both scripts are in same Dir
$cmd = join-path (split-path $myInvocation.myCommand.path) tmp.ps1
& $cmd -ex test, there

echo "TEST: $Test"
echo "There: $there"
-< tmp2.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - -
tojo2000
2008-08-16 03:51:40 UTC
Permalink
Post by Kiron
-< tmp.ps1 >-
param ([string[]]$exportVariable)
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
if ($exportVariable) {
 foreach ($var in $exportVariable) {
  set-variable $var (get-variable $var).value -scope 1
 }}
-< tmp.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
-< tmp2.ps1 >-
# $test echo "TEST: $Test"
# assuming both scripts are in same Dir
$cmd = join-path (split-path $myInvocation.myCommand.path) tmp.ps1
& $cmd -ex test, there
echo "TEST: $Test"
echo "There: $there"
-< tmp2.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
.\tmp2
--
Kiron
You might want to explain a solution like that, since variable
scoping, especially with the way PowerShell lets you do it, is a
difficult concept to get used to if you haven't dealt with it before.

When you assign a variable in PowerShell, by default the variable is
created in the current scope. If you are in a script block, then at
the end of the script block that variable will go "out of scope" and
be removed. Any script blocks or function calls after a variable is
declared, though, get access to the variable defined above.

The -scope option of Set-Variable allows you to set the scope of the
variable to a specific number of levels above the current scope. If
it is in a function, then it will set itself in the script's scope
that called it, and if it's in the script's highest scope then it will
be declared in the global scope.

The caveat with this method is that if you want to maintain that kind
of granularity then you have to know how many levels above the calling
statement you want the variable to be set to ahead of time. In
general it is a good idea to avoid declaring global variables as much
as possible because you increase your chances of having the same
variable name used twice, but this is not always practical.

BTW, if you do set the same variable twice then the innermost variable
will take precedence while it is in scope, and then you will revert to
the outer-scoped variable, since Powershell always starts with the
current scope and then works its way up when looking up a variable
name.
tojo2000
2008-08-16 08:54:30 UTC
Permalink
This post might be inappropriate. Click to display it.
Joel (Jaykul) Bennett
2008-08-16 15:22:17 UTC
Permalink
Sweet.  Thanks.  The control over scope is unprecedented in PowerShell
as far as I can tell, but the potential for confusion is unprecedented
as well.
The problem is, the solution being offered is waaay more complicated
than what you need. It's like the game of mousetrap.
All you had to do was dot-source the second script from the first,
which makes the second script execute in the first script's scope, so
any variables it defines are left behind when it's done.

For example:
## tmp1.ps1 ############
$Test = "Here"
$There = "There"

echo "TEST: $test"
echo "There: $there"
###########################

## tmp2.ps1 ############
echo "TEST: " $Test

# a dot, a space, then the path to the second script
. .\tmp1.ps1

echo "TEST: " $Test
echo "There: " $there
###########################

That's all. no need to mess with $global, or explicit scoping or any
of that.
tojo2000
2008-08-16 17:02:59 UTC
Permalink
Post by Joel (Jaykul) Bennett
Sweet.  Thanks.  The control over scope is unprecedented in PowerShell
as far as I can tell, but the potential for confusion is unprecedented
as well.
The problem is, the solution being offered is waaay more complicated
than what you need. It's like the game of mousetrap.
All you had to do was dot-source the second script from the first,
which makes the second script execute in the first script's scope, so
any variables it defines are left behind when it's done.
## tmp1.ps1 ############
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
###########################
## tmp2.ps1 ############
echo "TEST: " $Test
 # a dot, a space, then the path to the second script
. .\tmp1.ps1
echo "TEST: " $Test
echo "There: " $there
###########################
That's all. no need to mess with $global, or explicit scoping or any
of that.
Yes, as long as you dotsource it at the script: scope.
Kiron
2008-08-17 07:36:38 UTC
Permalink
Just because the code police may find the idea of importing specific variables --instead of importing every variable and define every function-- from the called script 'waaay more complicated', doesn't mean it isn't useful, and as long as someone 'gets' the concept and applies the technique, the purpose of sharing and demonstrating the powe
Kryten
2008-08-17 08:06:04 UTC
Permalink
Hi Kiron,

Actually that was going to be my next question. If I understood
properly your method just extracts the variables from the target
script.
If so thats an awesome technique. I didn't know you could do that! But
then I am still at the "I don't even know what I don't know" stage.

I'm going to have to play around with that a bit because I have a
script that generates hostnames to IP addresses and sends output to a
text file but it would be great to call that script and just yank out
the variables without that script "following through" so to speak.
That way other ppl could use it without mucking up my personal outfile
files.

Appreciated!

Stuart
tojo2000
2008-08-17 08:40:47 UTC
Permalink
Just because the code police may find the idea of importing specific variables --instead of importing every variable and define every function-- from the called script 'waaay more complicated', doesn't mean it isn't useful, and as long as someone 'gets' the concept and applies the technique, the purpose of sharing and demonstrating the power of PowerShell has been achieved.
--
Kiron
I disagree, and I'm no authority, so take it with whatever grain of
salt you need. The original question was how to call a script from
within a script and still use the variables created by that script
once it's done running. Your solution jumped to the most complicated
solution possible to the question and made several assumptions about
the problem being solved without even explaining what you were doing.
It's not that the idea of exporting a single variable is complicated
or has no value, it's that you just showed an example of a technique
that comes with a lot of caveats as a solution to a problem with a
simple solution.
Kiron
2008-08-17 09:51:14 UTC
Permalink
Post by tojo2000
I disagree, and I'm no authority
My sarcastic reply was not to you tojo, and I'm not trying to confuse anyone, just sharing what PowerShell's versatility I know of that
pschmidt
2008-08-18 16:19:00 UTC
Permalink
Excellent info - thanks.

The particular example the . invocation operator works fine. But I
definitely can seem some uses for exporting or declaring different scopes in
my 'called' script.

So both are very useful - thanks. Excellent info.

Perry
Post by Kiron
-< tmp.ps1 >-
param ([string[]]$exportVariable)
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
if ($exportVariable) {
foreach ($var in $exportVariable) {
set-variable $var (get-variable $var).value -scope 1
}
}
-< tmp.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
-< tmp2.ps1 >-
# $test echo "TEST: $Test"
# assuming both scripts are in same Dir
$cmd = join-path (split-path $myInvocation.myCommand.path) tmp.ps1
& $cmd -ex test, there
echo "TEST: $Test"
echo "There: $there"
-< tmp2.ps1 >-
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
.\tmp2
--
Kiron
Kiron
2008-08-18 18:48:33 UTC
Permalink
Post by pschmidt
Excellent info - thanks.
You're welcome Perry. There is also a similar way to call a function defined in a script without executing the entire script, or having to dot-source the script to define it in the caller's scope; the called function is not defined in the caller's scope either, it just executes in the called script's scope.
This sort of thing is available in v2 CTP2 through Modules. But if you, or anyone else, would like to do this in v1,
unknown
2008-08-17 11:28:47 UTC
Permalink
If you use the "." invocation operator instead of &, you can do this
directly. Simply change this line in tmp2:

.\tmp.ps1

to this:

. .\tmp.ps1

The (.) tells PowerShell to invoke script tmp.ps1 in the current scope,
instead of creating a new subscope.
Post by pschmidt
I want to run or include a PS script from within another PS script. But
want the values set in the 2nd one to be available in the first one.
tmp.ps1----
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
------------------------
Now tmp2.ps1-------------
echo "TEST: " $Test
.\tmp.ps1
echo "TEST: " $Test
echo "There: " $there
-------------
.\tmp2.ps1
TEST: Here
There: There
--------------------------
I'd like the values set in 'tmp' to be available in tmp2. How do I do this?
Thx,
unknown
2008-08-17 11:35:59 UTC
Permalink
Never mind; I didn't read the sub-thread that Tojo started before posting
this. ; )
Post by unknown
If you use the "." invocation operator instead of &, you can do this
.\tmp.ps1
. .\tmp.ps1
The (.) tells PowerShell to invoke script tmp.ps1 in the current scope,
instead of creating a new subscope.
Post by pschmidt
I want to run or include a PS script from within another PS script. But
want the values set in the 2nd one to be available in the first one.
tmp.ps1----
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
------------------------
Now tmp2.ps1-------------
echo "TEST: " $Test
.\tmp.ps1
echo "TEST: " $Test
echo "There: " $there
-------------
.\tmp2.ps1
TEST: Here
There: There
--------------------------
I'd like the values set in 'tmp' to be available in tmp2. How do I do this?
Thx,
c***@gmail.com
2016-06-28 23:45:51 UTC
Permalink
Post by pschmidt
I want to run or include a PS script from within another PS script. But
want the values set in the 2nd one to be available in the first one.
tmp.ps1----
$Test = "Here"
$There = "There"
echo "TEST: $test"
echo "There: $there"
------------------------
Now tmp2.ps1-------------
echo "TEST: " $Test
.\tmp.ps1
echo "TEST: " $Test
echo "There: " $there
-------------
.\tmp2.ps1
TEST: Here
There: There
--------------------------
I'd like the values set in 'tmp' to be available in tmp2. How do I do this?
Thx,
Use Linux. It works better for scripting, and about everything else.
Jürgen Exner
2016-06-29 00:59:05 UTC
Permalink
^^^^^^^^^^^^^^^
Post by pschmidt
I want to run or include a PS script from within another PS script. But
[...]
Use [...]
Do you honestly believe pschmidt has been waiting patiently all those 8
years for your reply?

At least your posting was helpful in one regard: you confirmed my
prejudices against Googliots once more.

jue

Continue reading on narkive:
Loading...