Running PS1 file from batch file, same folder on thumb drive - powershell

Admittedly I'm no scripter. I piece together what already works but trying to learn.
I have a script that does a lot of the manual labor for setting up a scan user for myself and our techs. Here is a small portion of it written as a batch file. At the end before the pause I want to call a PowerShell to show what the Network type is, not to change it. At least not at this time. I did remove alot of the extra from the file to save space. Both the batch file and the PS1 file will be in the same folder on a thumb drive.
The nettype.ps1 file just has:
get-netconnectionprofile
pause
The pause of course is so the tech can see the network type.
Hope someone has a simple solution. I did look here and other websites. I may not be using the right terminology in my search or understanding what I need done.
net user Scans Scanner1 /add
net localgroup administrators Scans /add
wmic UserAccount where Name='Scans' set PasswordExpires=False
md C:\Scans
#echo off
NET SHARE Scans=C:\Scans /Grant:Scans,Full
ICACLS "C:\Scans" /Grant Scans:(OI)(CI)(F) /t /c
ICACLS "C:\Scans" /Grant Everyone:(OI)(CI)(F) /t /c
netsh advfirewall firewall set rule group="Network Discovery" new enable=Yes
netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=Yes
PowerShell.exe -File "nettype.ps1"
pause

If that is all you have inside your powershell script, don't run it as a script, delete it and just run the command directly in your batch-file:
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "Get-NetConnectionProfile"
Which could be probably be shortened to:
PowerShell Get-NetConnectionProfile

I found the answer, knew it would be simple.
Just had to use the following in the batch file:
powershell.exe -ExecutionPolicy Bypass -File ""%~dp0nettype.ps1""

You can change the powershell call to the following to find the ps1 file in the same directory:
powershell.exe -File "%~dp0nettype.ps1"
%~dp0 is a combination of %0 variable and ~d and ~p modifiers.
%0 is the full path to the current batch file.
~d when combined with %0 (e.g. %~d0) will get you drive letter portion (e.g. C:) from %0.
~p when combined with %0 (e.g. %~p0) will get you the path portion of %0 without the filename.
Combining them together, %~dp0, will get you the full path of the folder where current batch file is located.
You can find a complete list of these modifiers here: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490909(v=technet.10)?redirectedfrom=MSDN
One thing to note, is that %~dp0 modifier only works in batch files, not when you try to run on commandline directly.

Related

Open a file with a PowerShell script in explorer

I wanted to use Windows built-in table viewer to open CSV file, like this SO answer shows.
So I want that when I double click on a CSV file in Explorer, the following command is run:
Import-Csv [CSV-FILE] |Out-GridView
I ended up creating two script files. One file name "read_csv.bat" which contains:
powershell.exe -ExecutionPolicy ByPass -noexit -File %~dp0read_csv.ps1 -csvfile %1
pause
Another file name "read_csv.ps1" which contains the actual script
param (
[Parameter(Mandatory=$true)][string]$csvfile
)
Import-Csv $csvfile |Out-GridView
Is there now way to do it more efficiently, so with only one script file? If I set explorer to open the CSV file with the POwerShell script directly, a blue message appears
This app can't run on your PC
To find a version for your PC, check with your published
Note:
Since powershell.exe is ultimately called, a console window will invariably (also) open when a CSV file is opened, because powershell.exe is a console-subsystem application.
Providing an alternative, GUI-subsystem executable to avoid creating a console window is the subject of this feature request on GitHub; in the meantime, there are workarounds:
A VBScript-based solution is shown in this answer.
A script-less, but complex and potentially AV-software-triggering alternative is used in this answer.
You'll need to use an adapted version of the powershell.exe command from your batch file, because *.ps1 files are by design not directly executable.
That is, in the registry definition for the CSV file type, use the following command to define the action for the Open verb (see below):
powershell.exe -ExecutionPolicy ByPass -NoExit -File c:\path\to\read_csv.ps1 -csvfile "%1"
Be sure to substitute the real path to your *.ps1 script for c:\path\to\read_csv.ps1 (double-quote it, if necessary); you can either use a literal path, or one based on cmd.exe-style environment-variable references (e.g., "%USERPROFILE%\read_csv.ps1").
Alternatively, you can make do without a script file altogether, using the PowerShell CLI's -Command parameter:
powershell.exe -ExecutionPolicy ByPass -NoExit -Command Import-Csv \"%1\" | Out-GridView
To automate the process of configuring this command for opening CSV files via File Explorer / the desktop:
The code below modifies the registry as follows:
defines a new file type, PsCsvViewer, with an Open verb (operation) that invokes the PowerShell command defined above.
associates the .csv filename extension with that new file type.
Note: The first time you open a CSV file after the redefinition, you may be prompted to confirm the intent to use a PowerShell command from now on.
creates the definitions above for the current user only, which means that you don't need admin privileges to run the code (which writing to subkeys of HKEY_CLASSES_ROOT\ would require).
# Note the need for *3* "\", because an extra layer of escaping is
# needed for reg.exe.
$cmd = 'powershell.exe -ExecutionPolicy ByPass -NoExit -Command Import-Csv \\\"%1\\\" | Out-GridView'
# Create a new file type for the PowerShell command.
reg.exe add HKCU\Software\Classes\PsCsvViewer\Shell\Open\command /ve /d $cmd /f
# Associate .csv files with the new file type.
reg.exe add HKCU\Software\Classes\.csv /ve /d PsCsvViewer /f

How to seperate lines of .bat when encrypted from powershell

I have a 10KB powershell script that I am trying to convert to a .bat. The format of the script is
1. declare constants 2. a bunch of for each child item loops involving the sending of an email and the moving of a file. Other bat files that I have made do work.
There are 6 non-nested loops.
When the bat of each loop is run in isolation (with the declarations), everything works. When I combine 3, one group works, the other sends the email without moving the file. When all are combined, nothing works and command prompt window doesn't even pop up.
I read that the line length limit of a bat file is 127 bytes (http://support.microsoft.com/kb/69563) and I think that this is the cause of my problem. Do I need to do something in powershell to separate the lines so the bat file won't try to read it as one line?
To convert, I used the script from here (http://www.altitudeintegration.com/PowerShellToBatFile.aspx)
You can call the original PowerShell script from a batch file using
powershell.exe -ExecutionPolicy RemoteSigned -File myscript.ps1
This way you can use your script by doubleclicking on the batch file.
The ExecutionPolicy parameter can be used to allow the script to run, without altering the machine level ExecutionPolicy:
Sets the default execution policy for the current session and saves it
in the $env:PSExecutionPolicyPreference environment variable. This
parameter does not change the Windows PowerShell execution policy that
is set in the registry.
This way there is no need to convert the PowerShell scripts to batch (which may not be possible at all).
You can use powershell.exe /? from a command prompt to discover all it's options.
Note that PowerShell defaults to setting the ExecutionPolicy to Restricted (RemoteSigned in Windows Server 2012 R2). Changing the machine wide policy in machine scope requires local admin rights.
Using the -ExecutionPolicy parameter on powershell.exe sets the executionpolicy for the local scope only.
See more details on ExecutionPolicy using
Get-Help about_Execution_Policies
or look here http://technet.microsoft.com/en-US/library/hh847748.aspx
You can find the PowerShell.exe executable in
C:\Windows\System32\WindowsPowerShell\v1.0\powersell.exe
(see: Path to Powershell.exe (v 2.0) )
This is not the entire answer to your question, but I specifically wanted to address your 127-byte assertion. That may have been true for 16-bit DOS, but cmd.exe has no such limitation.
test case:
#echo off
setlocal
set "longline=############################################################################################################################################################################################################################################################################################################"
call :length %longline% len
echo longline is %len% bytes
:: end main script
goto :EOF
:length <string> <var_to_set>
setlocal enabledelayedexpansion
set "str=%~1"
for /l %%I in (1,1,1000) do (
if "!str!"=="!str:~-%%I!" (
endlocal && set "%~2=%%I"
goto :EOF
)
)
output:
longline is 300 bytes
Did you originally script or code the bat file?
If not, can you pull that file out of backup?
I know we used to encode our KiX Scripts, so you should have the magic decoder ring as it were. Personally, I would be very careful in encoding your scripts unless they include login credentials. Other than that, I would leave them as raw text.
Thanks!

How to change the cmd's current-dir using PowerShell?

I read some file using PowerShell, and change current dir accordingly, but all I can do is change the current PowerShell's current dir, not the caller's dir (the cmd.exe environment that called that ps1 file). Things I tried:
powershell ch-dir.ps1 | cd
(won't work, obviously, since CD is internal command)
powershell cd $myDir
(changes current dir in PowerShell, but when script exits, the cmd environment still in original dir)
I really hope I won't need to find the script's caller process (the cmd), and make a change in it's cur-dir by-force... (or even worse - to save the dir I want in some env-var and then cd %my_var% since it would require two lines of command)
I'm not sure if this meets your needs, but if you set it up so that the only output from your powershell script is your desired new working directory, you could do this:
c:\>for /F %i IN ('powershell -noprofile -command "write-output 'c:\users'" ') DO #cd %i
c:\Users>
The cmd prompt is hosting your powershell session, unless you can figure out a way to return an exit code to the prompt that will (on exit code 99999) change directory to (predefined values, switch?). As far as powershell is concerned they're different processes.
Heres a good example for you to try:
Open a cmd prompt.
Open task manager, find cmd.exe
In your cmd prompt type Powershell
View powershell as a different process (check the PID.)
End the powershell process. Watch what happens.
Alternatively, if you need something run from cmd in a specific directory based on logic in your powershell script, you can invoke it with a cmd /c from within Powershell.

Windows batch file does not wait for commands to complete

I have a batch file, which exists as soon as start it (run as admin) and does not execute commands that are in it, but if I specify it at the command-line, it runs fine and executes all commands.
Here's what's in it:
start /wait msiexec /x SetupServices.msi /qn /l* "SetupServices.uninstall.log"
start /wait msiexec /i SetupServices.msi /qn /l* "SetupServices.install.log"
(Corrected answer)
First, if you start .exe files in a batch, it is safer, to prefix it with "call".
Sometimes this is necessary to assure, that the batch is waiting to complete.
Using "start" is another possibility, but for this simple usecase not necessary.
You write that the commands are not executed. So, obviously, you have another problem, instead of the "does not wait.. to complete" problem.
Taking a look of your newly provided example, this is the case. In admin mode, you have to provide a full path. With my small trick below ("%~dp0", including already backslash), you can still use the current directory in batchfiles.
Most times, if such a problem occurs with admin rights, this is a problem of the "current directory" path. A batch file with admin rights is not using it in the same way as we were used to, it does not start in it's own directory (but in System32 mostly). Not relying on the CD is an important matter of writing bullet-proof batch files.
A good example batch , combining other answers here, and solving a number of possible problems in your case is:
call msiexec /i "%~dp0MySetup.msi" /qb /L*v "%~dp0MySetup.log"
echo Returncode: %ERRORLEVEL%
pause
It uses the current directory correctly, and assumes an install commandline including a logfile (works only, if you have write access in the current directory, if not specify a path for the logfile with write access like "%TEMP%\MySetup.log".
Attention: Remember to really start the batch file with admin rights (right mouse menu or opening an admin command shell before:)
Coming back to this question I think the "correct way" to do it is via PowerShell
Start-Process -Wait -FilePath msiexec -ArgumentList /i, "setup.msi", /qn, /l*v, "install.log"
Or just prefix it with PowerShell; to invoke directly from CMD
PowerShell; Start-Process -Wait -FilePath msiexec -ArgumentList /i, "setup.msi", /qn, /l*v, "install.log"
No hacks and no tricks :-)
Try taking the start /wait out for the msiexec lines, if that doesn't work create two more bat files one called uninstall.bat the other install.bat and use call to execute them in series.
It goes a little beyond the question, but an extension to my answer concerning handling the current directory: Here is my recommended beginning for every batch file conserving it's own path. The specialty is that it also works for UNC paths. "Pushd" automatically creates a new drive letter, if necessary (assumed, you have one free of 26). Of course you can use "popd" also at the end of the batch file instead immediately, but stable commands do not rely on the current directory as I mentioned, so it is better to always provide full paths.
#echo off
cls
pushd %~dp0
popd
set MYDIR=%CD%
echo Directory of this batch fil: %MYDIR%
You can then add the msi lines from the other answer like this:
call msiexec /i "%MYDIR%\MySetup.msi" /qb /L*v "%MYDIR%\MySetup.log"
echo Returncode: %ERRORLEVEL%
pause
(Remark: For the logfile path of course you are free, it has not to be necessarily in the same directory. But good for testing/debugging. In every case you have to have write access to the directory/file you are giving MSI with.)
While for normal MSI files it is not always necessary to start the batch with admin rights from the beginning, this technique is by far more safe (to start the MSI like already with admin rights) than too rely on the MSI UAC coming later (maybe).
And it works with msiexec ... /qn too, which is important (silent installs).
Add pause statement to the end of the batch, this will prevent the console window from closing and you will be able to see error messages if any. Errors could be the reason why it exits without actually running anything. What kind of error it may be? SetupServices.msi is not found — that's what comes to my mind.
Needs the "Window Title" if you use Parameter
start /wait "Window Title" "MsiExec.exe" /i SetupServices.msi /qn /l* SetupServices.uninstall.log
start /wait "Window Title" "MsiExec.exe" /i SetupServices.msi /qn /l* SetupServices.install.log

Set up PowerShell Script for Automatic Execution

I have a few lines of PowerShell code that I would like to use as an automated script. The way I would like it to be able to work is to be able to call it using one of the following options:
One command line that opens PowerShell, executes script and closes PowerShell (this would be used for a global build-routine)
A file that I can double-click to run the above (I would use this method when manually testing components of my build process)
I have been going through PowerShell documentation online, and although I can find lots of scripts, I have been unable to find instructions on how to do what I need. Thanks for the help.
From http://blogs.msdn.com/b/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx
If you're willing to sully your beautiful PowerShell script with a little CMD, you can use a PowerShell-CMD polyglot trick. Save your PowerShell script as a .CMD file, and put this line at the top:
#PowerShell -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %*);'+[String]::Join(';',(Get-Content '%~f0') -notmatch '^^#PowerShell.*EOF$')) & goto :EOF
If you need to support quoted arguments, there's a longer version, which also allows comments. (note the unusual CMD commenting trick of double #).
##:: This prolog allows a PowerShell script to be embedded in a .CMD file.
##:: Any non-PowerShell content must be preceeded by "##"
##setlocal
##set POWERSHELL_BAT_ARGS=%*
##if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
##PowerShell -ExecutionPolicy Bypass -Command Invoke-Expression $('$args=#(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join(';',$((Get-Content '%~f0') -notmatch '^^##'))) & goto :EOF
Save your script as a .ps1 file and launch it using powershell.exe, like this:
powershell.exe .\foo.ps1
Make sure you specify the full path to the script, and make sure you have set your execution policy level to at least "RemoteSigned" so that unsigned local scripts can be run.
Run Script Automatically From Another Script (e.g. Batch File)
As Matt Hamilton suggested, simply create your PowerShell .ps1 script and call it using:
PowerShell C:\Path\To\YourPowerShellScript.ps1
or if your batch file's working directory is the same directory that the PowerShell script is in, you can use a relative path:
PowerShell .\YourPowerShellScript.ps1
And before this will work you will need to set the PC's Execution Policy, which I show how to do down below.
Run Script Manually Method 1
You can see my blog post for more information, but essentially create your PowerShell .ps1 script file to do what you want, and then create a .cmd batch file in the same directory and use the following for the file's contents:
#ECHO OFF
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%MyPowerShellScript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
Replacing MyPowerShellScript.ps1 on the 3rd line with the file name of your PowerShell script.
This will allow you to simply double click the batch file to run your PowerShell script, and will avoid you having to change your PowerShell Execution Policy.
My blog post also shows how to run the PowerShell script as an admin if that is something you need to do.
Run Script Manually Method 2
Alternatively, if you don't want to create a batch file for each of your PowerShell scripts, you can change the default PowerShell script behavior from Edit to Run, allowing you to double-click your .ps1 files to run them.
There is an additional registry setting that you will want to modify so that you can run scripts whose file path contains spaces. I show how to do both of these things on this blog post.
With this method however, you will first need to set your execution policy to allow scripts to be ran. You only need to do this once per PC and it can be done by running this line in a PowerShell command prompt.
Start-Process PowerShell -ArgumentList 'Set-ExecutionPolicy RemoteSigned -Force' -Verb RunAs
Set-ExecutionPolicy RemoteSigned -Force is the command that actually changes the execution policy; this sets it to RemoteSigned, so you can change that to something else if you need. Also, this line will automatically run PowerShell as an admin for you, which is required in order to change the execution policy.
Source for Matt's answer.
I can get it to run by double-clicking a file by creating a batch file with the following in it:
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe LocationOfPS1File
you can use this command :
powershell.exe -argument c:\scriptPath\Script.ps1