When I start the PowerShell script from the CMD this is not setting the variables well - powershell

If I start the command from PowerShell it works perfectly instead of CMD this happens:
Imgur Image
PowerShell -NoLogo -NoProfile -Command "$MangaPage=Invoke-WebRequest -Uri %MangaLink%; $MangaName=$MangaPage.ParsedHtml.querySelector('#top-in > div.top-title > span.hideM0 > a').innerText; Write-Host $MangaName; $ChapterNumber=$MangaPage.ParsedHtml.querySelector('#combobox > option.selected').innerText; Write-Host $ChapterNumber; $PageNumber=$MangaPage.ParsedHtml.querySelector('#pageSelect > option.selected').innerText; Write-Host $PageNumber; $URLImage=$MangaPage.ParsedHtml.querySelector('#mainImg').src.replace('about:', 'https:'); Write-Host $URLImage; $FileExtension=$URLImage.SubString($URLImage.lastIndexOf('.')+1); Write-Host $FileExtension; $FileName='$.ChapterNumber'+'_'+'$PageNumber'+'_'+'$MangaName'+'.'+'$FileExtension'; $Folder=('$MangaName\$ChapterNumber'); If(!(Test-Path $Folder)) { New-Item -ItemType Directory -Force -Path $Folder }; Invoke-WebRequest $URLImage -OutFile $Folder\$FileName"

About PowerShell.exe
When the value of Command is a string, Command must be the last
parameter specified because any characters typed after the command are
interpreted as the command arguments.
The Command parameter only accepts a script block for execution when
it can recognize the value passed to Command as a ScriptBlock type.
This is only possible when running PowerShell.exe from another
PowerShell host. The ScriptBlock type may be contained in an existing
variable, returned from an expression, or parsed by the PowerShell
host as a literal script block enclosed in curly braces {}, before
being passed to PowerShell.exe.
In cmd.exe, there is no such thing as a script block (or ScriptBlock
type), so the value passed to Command will always be a string. You can
write a script block inside the string, but instead of being executed
it will behave exactly as though you typed it at a typical PowerShell
prompt, printing the contents of the script block back out to you.
A string passed to Command will still be executed as PowerShell, so
the script block curly braces are often not required in the first
place when running from cmd.exe. To execute an inline script block
defined inside a string, the call operator & can be used:
console
"& {}"
PowerShell -NoLogo -NoProfile -Command "&{ $MangaPage=Invoke-WebRequest -Uri %MangaLink%; $MangaName=$MangaPage.ParsedHtml.querySelector('#top-in > div.top-title > span.hideM0 > a').innerText; Write-Host $MangaName; $ChapterNumber=$MangaPage.ParsedHtml.querySelector('#combobox > option.selected').innerText; Write-Host $ChapterNumber; $PageNumber=$MangaPage.ParsedHtml.querySelector('#pageSelect > option.selected').innerText; Write-Host $PageNumber; $URLImage=$MangaPage.ParsedHtml.querySelector('#mainImg').src.replace('about:', 'https:'); Write-Host $URLImage; $FileExtension=$URLImage.SubString($URLImage.lastIndexOf('.')+1); Write-Host $FileExtension; $FileName='$.ChapterNumber'+'_'+'$PageNumber'+'_'+'$MangaName'+'.'+'$FileExtension'; $Folder=('$MangaName\$ChapterNumber'); If(!(Test-Path $Folder)) { New-Item -ItemType Directory -Force -Path $Folder }; Invoke-WebRequest $URLImage -OutFile $Folder\$FileName}"

Related

PowerShell function not running as expected

I have a curious case that I cannot fathom the reason for...
Please know I am a novice to PowerShell.
I am working on a PowerShell menu system to help automate building out new computers in my environment. I have a PS1 file that holds the script for an app install. When I use the script to reference this I am able to run it and have no issue. However, when I try inserting this into a function and referencing it does not.
This works:
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
# {installMS32Bit}
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
Start-Sleep -seconds 2
}
This does not:
function installMS32Bit(){
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
}
}
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
{installMS32Bit}
Start-Sleep -seconds 2}
install.ps1 file:
# Copy MS Office uninstall and setup to local then run and install 32-bit Office
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\setup.exe' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\uninstall.xml' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\32-bit\Setup.exe' -Destination 'C:\temp' -Force
Invoke-Expression ("cmd /c 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'")
Start-Process -FilePath 'C:\temp\Setup.exe'
Secondary question and a little explanation for Invoke-Expression...
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
If there is a better way to do this in PowerShell I am all ears!
{installMS32Bit}
As Mathias points out in a comment on the question, this statement doesn't call your function, it wraps it in a script block ({ ... })[1], which is a piece of reusable code (like a function pointer, loosely speaking), for later execution via &, the call (execute) operator.
To call your function, just use its name (by itself here, given that there are no arguments to pass): installMS32Bit
Invoke-Expression should generally be avoided; definitely don't use it to invoke an external program, as in your attempts.
Additionally, there's generally no need to call an external program via cmd.exe (cmd /c ...), just invoke it directly.
For instance, replace the last Invoke-Epression call from your question with:
# If the EXE path weren't quoted, you wouldn't need the &
& 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
(On Windows), Start-Process by default executes a console application in a new window (unless you specify -NoNewWindow), asynchronously (unless you specify -Wait).
You cannot pass a .ps1 script directly to Start-Process (it will be treated like a document to open rather than an executable to call), but you can pass it to PowerShell's CLI via the -File parameter:
Start-Process powershell.exe '-File install.ps1'
The above is short for:
Start-Process -FilePath powershell.exe -ArgumentList '-File install.ps1'
That is, PowerShell will execute the following in a new window:
powershell.exe -File install.ps1
[1] Since you're not assigning the script block being created to a variable, it is implicitly output (printed to the display, in the absence of a redirection); a script block stringifies by its literal contents, excluding the enclosing { and }, so string installMS32Bit will print to the display.

Process files dragged to BAT file in PowerShell

I trying to create a script that will convert Markdown files that are dropped on a processing script.
To accomplish this, I created a (process.bat) that would pass the names of the dropped files to a PowerShell script:
powershell.exe -NoProfile -File "./process.ps1" -Document %*
#pause
The PowerShell file (process.ps1) would process each file individually:
[parameter(Mandatory=$true)]
[String[]]$Document
Write-Host $args[1]
$Document | ForEach-Object {
Write-Host "Document: $_"
# convert Markdown to Html
pandoc -o ($_ -Replace '.md', '.html') -f markdown -t html $_
}
When I drop two files on the batch file:
C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown>powershell.exe -NoProfile -File "./process.ps1" -Document "C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\FOO.md"
"C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\BAR.md"
C:\Users\XXX\Documents\WindowsPowerShell\Scripts\Markdown\BAR.md
Document:
Press any key to continue . . .
The documents are not being processed.
What's the recommended approach to passing the batch file's file list %* to PowerShell?
When powershell.exe, the PowerShell CLI, is called from the outside with the -File parameter, it doesn't support arrays - only individual arguments.[1]
(Additionally, you neglected to wrap your parameter definition in a param(...) block, which effectively caused it be ignored.)
The simplest solution is to define your parameter with the ValueFromRemainingArguments option so that it automatically collects all positional arguments in the parameter variable:
param(
[Parameter(Mandatory, ValueFromRemainingArguments)]
[String[]] $Document
)
Then invoke your script via the PowerShell CLI without -Document:
powershell.exe -NoProfile -File "./process.ps1" %*
As an alternative to using a helper batch file, you can:
define a shortcut file (*.lnk) that explicitly invokes your PowerShell script as powershell.exe -File \path\to\your\script.ps1 (without additional arguments)
and then use that as the drop target.
Note: The reason that you cannot use a PowerShell script (*.ps1) directly as a drop target is that PowerShell script files aren't directly executable - instead, opening (double-clicking) a *.ps1 file opens it for editing.
You'd then need to add pause (or something similar, such as Read-Host -Prompt 'Press Enter to exit.') to your PowerShell script, to prevent it from closing the window instantly on finishing.
Alternatively, leave the script as-is and use -NoExit (placed before -File) to keep the PowerShell session open.
[1] The same applies to the PowerShell Core CLI, pwsh.

Capturing different streams in file

I'm trying to capture the Verbose, Error and other streams of a PowerShell script in a file. This to monitor the output of my script.
The following code works fine:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\TestieScript.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
However, the moment I try to put a space in one of the file paths, it's no longer working. I tried a lot of things, like double quotes, single quotes, .. but no luck.
To illustrate, the following code doesn't work:
$LogFile = 'S:\ScriptLog.log'
$ScriptFile = 'S:\Testie Script.ps1'
powershell -Command $ScriptFile *>&1 > $LogFile
One person in this thread has the same issue.
Thank you for your help.
You're trying to run a file whose name contains a space as a command without proper quoting, so you're most likely getting an error like this in your log:
The term 'S:\Testie' is not recognized as the name of a cmdlet, function, script file, or operable program.
Either add proper quoting (and the call operator &, because your path is now a string):
powershell -Command "& '$ScriptFile'" *>&1 > $LogFile
or (better) use the -File parameter, as #CB. already suggested:
powershell -File $ScriptFile *>&1 > $LogFile
which has the additional advantage that the call will return the actual exit code of the script.
Edit: If you want to run the command as a scheduled task you'll need to use something like this:
powershell -Command "& 'S:\Testie Script.ps1' *>&1 > 'S:\ScriptLog.log'; exit $LASTEXITCODE"
because the redirection operators only work inside a PowerShell process.
try using -file parameter:
powershell -file $ScriptFile *>&1 > $LogFile

passing \ in argument to powershell script causes unexpected escaping

This is my powershell script test.ps1:
Write-Output $args;
Now suppose I have a batch script that calls this powershell script with all kinds of paths. One of those is c:\:
powershell -executionpolicy Bypass -file test.ps1 "c:\"
The output is:
c:"
Is there any way to quote my arguments such that c:\ would actually be taken and stored as is in the $args[0] variable? I know I can solve this quick'dirty by passing "c:\\", but that's not a real solution.
EDIT: using named parameters in test.ps1 doesn't make any difference:
[CmdletBinding()]
param(
[string]$argument
)
Write-Output $argument;
EDIT2: using a batch file instead works fine.
My test.bat script:
echo %~1
I run it:
test.bat "c:\"
Returns nicely:
c:\
Are you sure this comes form powershell and not from the program which invokes your statement? The backslash is no escape code in powershell.
my test.ps1 is working, when run from ise.
this works for me:
powershell -executionpolicy Bypass -command "test.ps1 -argument 'C:\'"
(end with quote double-quote)
Help file for PowerShell.exe says:
File must be the last parameter in the command, because 'all characters' typed after the file parameter name are "interpreted" as the script file path followed by the script parameters.
You are against Powershell.exe's command line parser, which uses "\" to escape quotes. Do you need quotes? Not in your case:
powershell -file test.ps1 c:\
prints
c:\
Similarly, this works too
powershell -file test.ps1 "c:\ "
c:\
but then your arg has that extra space which you would want to trim. BTW, Single quotes do not help here:
powershell -file test.ps1 'c:\'
'c:\'
If you need the final backlash to be passed to the command, you can use
$ArgWithABackslashTemp = $ArgWithABackslash -replace '\\$','\\'
&$ExePath $ArgWithABackslashTemp
Or, if the exe is smart enough to handle it without the trailing backslash
&$ExePath $ArgWithABackslash.trim('\')

How to run a Powershell script from the command line and pass a directory as a parameter

PowerShell -Command .\Foo.ps1
Foo.ps1:
Function Foo($directory)
{
echo $directory
}
if ($args.Length -eq 0)
{
echo "Usage: Foo <directory>"
}
else
{
Foo($args[0])
}
Despite Foo.ps1 being in the directory from where I am calling Powershell, this results in:
The term '.\Foo.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
EDIT: Wasn't working because PowerShell was changing directory due to profile.ps1 containing cd C:\
I then tried to call it specifying the full path to the script file, but no matter what I try, I can't get it to work. I believe I have to quote the path because it contains whitespaces, as does the file name I need to pass in an argument to the script.
Best guess so far:
PowerShell -Command "'C:\Dummy Directory 1\Foo.ps1' 'C:\Dummy Directory 2\File.txt'"
Outputs error:
Unexpected token 'C:\Dummy Directory 2\File.txt' in expression or statement.
At line:1 char:136.
try this:
powershell "C:\Dummy Directory 1\Foo.ps1 'C:\Dummy Directory 2\File.txt'"
you are calling a script file not a command so you have to use -file eg :
powershell -executionPolicy bypass -noexit -file "c:\temp\test.ps1" "c:\test with space"
for PS V2
powershell.exe -noexit &'c:\my scripts\test.ps1'
(check bottom of this technet page http://technet.microsoft.com/en-us/library/ee176949.aspx )
Using the flag -Command you can execute your entire powershell line as if it was a command in the PowerShell prompt:
powershell -Command "& '<PATH_TO_PS1_FILE>' '<ARG_1>' '<ARG_2>' ... '<ARG_N>'"
This solved my issue with running PowerShell commands in Visual Studio Post-Build and Pre-Build events.
Add the param declation at the top of ps1 file
test.ps1
param(
# Our preferred encoding
[parameter(Mandatory=$false)]
[ValidateSet("UTF8","Unicode","UTF7","ASCII","UTF32","BigEndianUnicode")]
[string]$Encoding = "UTF8"
)
write ("Encoding : {0}" -f $Encoding)
result
C:\temp> .\test.ps1 -Encoding ASCII
Encoding : ASCII
Change your code to the following :
Function Foo($directory)
{
echo $directory
}
if ($args.Length -eq 0)
{
echo "Usage: Foo <directory>"
}
else
{
Foo([string[]]$args)
}
And then invoke it as:
powershell -ExecutionPolicy RemoteSigned -File "c:\foo.ps1" "c:\Documents and Settings" "c:\test"
you have type and hit enter :
PowerShell -Command