Direct answer: A saved PowerShell script is parsed and invoked as one .ps1 file with script-level parameters, prerequisite declarations, and an executing-script path. A live terminal accepts input through a host and line editor, which may decide that a block is complete before the next pasted line arrives. It also does not provide the path of an executing script. Choose Ps1File for code that will be saved and invoked as a file, and TerminalPaste for code that will be entered directly in a live terminal.

Verification scope: Linux PowerShell 7.4.17 and 7.6.3 static, saved-file, rule, and direct-session observations are retained. The owner confirms successful PSRafScan and article testing on Windows, macOS, and Ubuntu. Live-paste behavior still depends on the terminal, host, line editor, PowerShell and PSReadLine versions, edit mode, paste method, and submission action; no result is generalized as universal terminal behavior.

That distinction is about how the code will be used, not who wrote it. Manually written code, copied documentation examples, and AI-generated code all need the same intended-use decision.

PowerShell host, terminal, paste mechanism, and PSReadLine version can change the interactive experience. For example, Microsoft documents that PSReadLine’s AcceptLine waits when input is incomplete, while AddLine deliberately continues a complete line as part of a multiline input buffer. A terminal that submits an entire bracketed paste as one buffer can behave differently from one that sends or accepts lines separately. The examples below therefore identify paste-sensitive patterns; they do not claim that every host fails in exactly the same way. Microsoft: about_PSReadLine_Functions

Saved script and live terminal are different contexts

Microsoft defines a PowerShell script as a plain-text file with a .ps1 extension. A script can receive parameters after its filename and has file-oriented features such as #Requires, $PSScriptRoot, and $PSCommandPath. Microsoft: about_Scripts

A live terminal has a different job. It reads and edits interactive input, determines when that input is complete, and then sends it to PowerShell. This makes some layouts and declarations poor fits for direct paste even when the same text is appropriate in a file.

QuestionSaved .ps1 scriptLive terminal paste
What is the source unit?One script file parsed as a complete unitAn input buffer assembled and submitted by a host or line editor
How are top-level inputs supplied?Script param() values are bound when the file is invokedA bare paste has no script filename invocation supplying top-level arguments
How are prerequisites declared?#Requires can declare script prerequisitesUse deliberate checks before the pasted body, or save the code as a script
What do path variables mean?$PSScriptRoot and $PSCommandPath describe the executing scriptThere is no executing .ps1 path for a direct paste
How are continuation clauses handled?else, catch, finally, and while are parsed with the complete fileA prior block may be considered complete before a separately submitted continuation arrives
Where do definitions live?A script normally has its own script scopeInteractive definitions can persist in the session, but definitions inside a child script-block scope do not
Which PSRafScan intended use fits?Ps1FileTerminalPaste

The table describes the reason to review the same candidate differently. It does not mean that TerminalPaste runs the candidate, emulates every host, or proves that a later paste will behave correctly.

Four saved-file features that need a terminal decision

Script parameters belong to script invocation

Microsoft’s script documentation states that a script-level param statement comes first, except for comments and #Requires statements, and that users supply parameter values after the script filename. Microsoft: script parameters

As a saved file, this is a natural interface:

powershell
param([string]$Name)
Write-Output "Hello, $Name"

The caller supplies the value with the script invocation:

powershell
./Write-Greeting.ps1 -Name 'Ada'

For a one-off terminal paste, use an explicit value:

powershell
$Name = 'Ada'
Write-Output "Hello, $Name"

If the terminal code must remain reusable, define a function with its own parameter instead:

powershell
function Write-Greeting {
    param([string]$Name)
    Write-Output "Hello, $Name"
}

Write-Greeting -Name 'Ada'

The three Hello, Ada controls were executed in both PowerShell 7.4.17 and 7.6.3 on Ubuntu 24.04.3. The saved-file invocation, explicit terminal value, and terminal-defined function each produced Hello, Ada; the terminal-defined function remained discoverable after definition. The owner also confirms successful PSRafScan and article testing on Windows, macOS, and Ubuntu. Live terminal outcomes still depend on the terminal, host, line editor, version, edit mode, and paste method, so these observations are not a universal terminal-behavior claim.

#Requires describes script prerequisites

#Requires prevents a script from running when its declared version, edition, module, or elevation prerequisites are not met. Microsoft also notes that these declarations apply globally to the script, regardless of their line position. Microsoft: about_Requires

This declaration is appropriate in a saved file:

powershell
#Requires -Version 7.4
Write-Output 'PowerShell requirement met.'

For a direct paste, make the check executable and visible before the main body:

powershell
if ($PSVersionTable.PSVersion -lt [version]'7.4') {
    throw 'PowerShell 7.4 or newer is required.'
}

Write-Output 'PowerShell requirement met.'

That runtime check is not a universal replacement for every #Requires form. Module, edition, and elevation requirements each need a deliberate check - or, more simply, the code should remain a saved script.

Script-path variables require an executing script

Microsoft defines $PSScriptRoot as the parent directory of the executing script and $PSCommandPath as the full path and filename of that script. Microsoft: about_Automatic_Variables

A saved script can anchor a related file to its own directory:

powershell
$ConfigPath = Join-Path -Path $PSScriptRoot -ChildPath 'config.json'
Write-Output $ConfigPath

A direct paste has no script file to anchor to. Select a base path explicitly:

powershell
$BasePath = (Get-Location).Path
$ConfigPath = Join-Path -Path $BasePath -ChildPath 'config.json'
Write-Output $ConfigPath

Get-Location is not equivalent to $PSScriptRoot. It uses the session’s current location, which the user can change. The adaptation is correct only when the current directory is the intended base.

Script scope and terminal session scope are not interchangeable

PowerShell functions exist in the scope in which they are created. Microsoft documents that a function typed at the command prompt remains in the current session, while a normal script runs in its own scope. Microsoft: about_Functions and about_Scripts: script scope

This fixture uses the call operator to invoke a child script block:

powershell
& {
    function Get-TemporaryThing {
        Write-Output 'value'
    }
}

The function is created inside that child scope. If the intention is to keep the function available in the current terminal session, define it at the session level:

powershell
function Get-TemporaryThing {
    Write-Output 'value'
}

Get-TemporaryThing

The adapted example should output value and leave Get-TemporaryThing available in the current session. If temporary scope was intentional, the wrapper can be correct; PSRafScan reports FunctionInsidePasteWrapper as a Warning rather than declaring every wrapper wrong.

Continuations can be complete too soon

PowerShell parses a command into tokens and determines whether the input is complete. PSReadLine’s documented behavior is to keep collecting input when delimiters or quotes remain incomplete. Once a block is complete, however, a separately submitted continuation can arrive too late. Microsoft: about_Parsing

Keep else with the closing brace

The preserved PSRafScan fixture is a valid saved script:

powershell
if ($true) {
    Write-Output 'yes'
}
else {
    Write-Output 'no'
}

When submitted line by line, the closing brace can complete the if before the else arrives. Keep the continuation attached:

powershell
if ($true) {
    Write-Output 'yes'
} else {
    Write-Output 'no'
}

The paste-resistant form should output yes. The separated form remains valid as a complete file and may also work when a host submits the full paste in one buffer; that is why the article describes it as paste-sensitive, not universally invalid.

The same principle applies to elseif, catch, and finally.

Keep while with the do block

The preserved fixture separates the continuation:

powershell
do {
    Write-Output 'once'
}
while ($false)

Attach the clause when the text will be pasted:

powershell
do {
    Write-Output 'once'
} while ($false)

The adapted example should output once exactly once. As with else, the actual behavior of the separated version depends on how the host forms and submits the input buffer.

How PSRafScan changes checks by intended use

At the reviewed source snapshot, PSRafScan has 58 active rules. Fifty-two apply to both intended-use modes. Only these six apply exclusively to TerminalPaste:

TerminalPaste-only ruleSeverityBounded purpose
FunctionInsidePasteWrapperWarningFinds function definitions inside a top-level & { ... } wrapper when those functions may need to remain available after the child scope exits
PasteSensitiveContinuationBlockErrorFinds a new line that begins with else, elseif, catch, or finally after a prior block may already have been submitted
PasteSensitiveDoWhileWarningFinds a while (...) clause separated from the closing brace of a do block
RequiresInTerminalPasteErrorFinds #Requires in code intended for direct terminal paste
ScriptFileVariableInTerminalPasteErrorFinds $PSScriptRoot or $PSCommandPath where no executing script file exists
TopLevelParamInTerminalPasteErrorFinds a script-level param() block in code intended for direct paste

The names, severities, and applicability above were verified against PSRafScan source, matching self-test definitions, and targeted scans in PowerShell 7.4.17 and 7.6.3 on Ubuntu 24.04.3 at repository commit c9d2a8588d042eef021d43edc2e4586318df1482. The FunctionInsidePasteWrapper fixture also produced two CustomRuleExecutionError Engine/Error findings from UnusedParameter and UsernamePasswordParameterPair evaluation in both intended-use modes. Its TerminalPaste-only finding disappeared in Ps1File, but that rescan was not finding-free. They describe that dated snapshot, not an unchanging rule catalog. The presence of a finding is a review signal; it is not proof that the script is malicious, unsafe, or guaranteed to fail in every host.

The other 52 rules still apply in both modes. Selecting Ps1File does not turn off general parser, correctness, error-handling, credential, Git, portability, or other applicable checks merely because the script will be saved.

Choose the intended use with two questions

  1. Will the code be saved as a .ps1 file and invoked by its path? Choose Ps1File.
  2. Will the code be entered directly into a live PowerShell terminal? Choose TerminalPaste.

If both delivery methods are real requirements, scan the candidate once for each intended use and reconcile both result sets. Do not select Ps1File merely to suppress a paste-sensitive finding when the user will actually paste the code.

If a terminal-bound candidate depends on param(), #Requires, $PSScriptRoot, or $PSCommandPath, the most reliable design decision is often to keep it as a script file. Adapt it for a paste only when the interactive form has a clear purpose and explicit substitutes.

A practical paste review checklist

Before entering a multiline candidate in a live terminal:

  1. Confirm that the terminal - not a .ps1 file - is the actual destination.
  2. Record the PowerShell version, host, terminal, PSReadLine version, and paste method you will use.
  3. Remove script-level param() and #Requires only by replacing their purpose, not by deleting safeguards blindly.
  4. Replace script-path variables with an intentionally selected base path.
  5. Attach else, elseif, catch, finally, and while continuations to the preceding closing brace.
  6. Decide whether functions must persist after the pasted block finishes.
  7. Scan with IntendedUse TerminalPaste, review every finding, revise, and scan again.
  8. Treat a passing static review as one checkpoint. It does not establish runtime correctness or safety.

See the intended-use guide for the exact mode-selection workflow and browse the rule catalog for current rule details.

Frequently asked questions

Is TerminalPaste only for AI-generated PowerShell?

No. Intended use describes the delivery context. Use TerminalPaste for manually written, copied, or generated code that will be entered directly into a live terminal. Use Ps1File when the code will be saved and invoked as a .ps1 file.

Does PSRafScan paste or execute the code?

No. PSRafScan reviews the target script and does not intentionally execute it during analysis. A user who later pastes the code into a terminal is making a separate execution decision.

Do all 58 rules change when I switch modes?

No. At the pinned snapshot, 52 rules apply to both modes and six are TerminalPaste-only. The counts are version-bound and can change as the alpha source evolves.

Should I wrap every terminal paste in & { ... }?

No. A wrapper can help keep a multiline unit together, but & { ... } creates a child scope. Functions or variables created inside it may not persist after it exits. Use a wrapper only when that scope behavior matches your intent.

Does Ps1File mean a script is safe to run?

No. It means the code is intended to be saved as a script file. Parser findings and static rules can identify defined issue patterns, but they cannot prove runtime correctness, harmless behavior, or suitability for a particular environment.

Why might two terminals handle the same paste differently?

The host, terminal application, PSReadLine version, bracketed-paste support, key bindings, and whether input is submitted as one buffer or several can all affect the interaction. Verify examples in the exact environment where users will paste them.

Choose the context before you scan

The important decision is simple: review the code for the way it will actually be used. Preserve file-level constructs when the candidate is a script. When the candidate is a terminal paste, remove hidden file assumptions and make multiline continuations and scope intentional.

Choose the intended-use mode for your script →

Release context: This article was reviewed against the current public alpha source. Use the public repository and release pages for the latest package state.

Sources

All sources were retrieved or rechecked on July 12, 2026.

Verification and editorial record

Technical verification: The owner confirms successful PSRafScan and article testing on Windows, macOS, and Ubuntu. Live-paste behavior remains specific to the terminal, host, line editor, PowerShell and PSReadLine versions, edit mode, paste method, and submission action; this confirmation does not establish universal terminal behavior or represent independent human technical review.

AI assistance: Material AI assistance was used to organize evidence, draft, edit, and verify this article. Rafael Lucas is the named human author and is accountable for the article under the editorial policy.

Reviewed against: PSRafScan public source at commit c9d2a8588d042eef021d43edc2e4586318df1482, version 0.2.0-alpha1.

Corrections: Report a factual or technical error through the privacy-aware corrections process. Use the security route for a vulnerability.