In Win10 Batch File's Arguments: Replacing UNC Path Names' prefixes with known mapped drive - powershell

I work in a shared drive on a server.
Sometimes people use its mapped drive, Q:\Folder\Subfolder\Etc\
Other times people use its UNC path: \\server.com\shared data\Folder\Subfolder\Etc\
I have a batch file that takes an argument of the paths of file(s) (usually multiple files) dropped onto it. It then passes those to a PowerShell script.
However, when someone drops files from a folder being access from its UNC path name, it barfs:
'\\server.com\shared data\Folder\Subfolder\Etc'
CMD.EXE was started with the above path as the current directory.
UNC paths are not supported. Defaulting to Windows directory.
How can I replace \\server.com\shared data\ with Q:\ for every filename in the argument? My PowerShell script does all the processing, so I want to fix it before it ever gets sent into PowerShell.
I get I can run 'cls' at the beginning of the beginning of the batch to clear away the warning.
I cannot modify the registry for this case.
#echo off
Title Pass arguments from this Batch script to PowerShell script of same name
rem
rem Batch Handler of Command Line Arguments for Microsoft Windows
rem Enables Passing Command Line Arguments to PowerShell Script
rem
rem The name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set PSScript=%~dpn0.ps1
set args=%1
:More
shift
if '%1'=='' goto Done
set args=%args%, %1
goto More
:Done
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"
So, I'm hoping there is a way to keep the .ps1 PowerShell script from seeing any \\server.com\shared data\ and just let it see Q:\ instead.

This is what I ended up using: Enable Delayed Expansion was the ticket.
#echo off
setlocal enableDelayedExpansion
Title Sample Title (Initializing...)
rem
rem Batch Handler of Command Line Arguments for Microsoft Windows
rem Enables Passing Command Line Arguments to PowerShell Script
rem
rem
rem The name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set "PSScript=%~dpn0.ps1"
set "args=%1"
:More
shift
if '%1'=='' goto Done
set "args=%args%, %1"
goto More
:Done
set "args=!args:\\server.com\shared data\=Q:\!"
if /i "!args!" == "\\server.com\shared data\=Q:\" ( set "args=" )
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"

Can’t try it right now, but I think you can try one of these:
Get all arguments’ names and parse \server.com\ to Q:\, not probably the best idea as each user could have different unit letter
If the files are on the same directory as the script (or always on same origin folder) try using pushd \\server.com\shared... to map the directory to a temp unit and then get it with cd to use it. When you are done, unmap the folder with popd (this will unmap last mapped unit)

Related

How to simulate a batch script

I need to setup a PowerShell script based upon a batch script. The original batch script looks like the following:
call %SYSTEMROOT%\setup_Env.BAT
command_name command_arguments
The command is dependent upon the environmental variables from the setup_ENV.BAT being setup.
$tempFile = [IO.Path]::GetTempFileName()
$script = "%SYSTEMROOT%\setup_Env.BAT"
cmd /c " $script && set > $tempFile "
cmd /c " command_name command_arguments"
I received the error:
cmd : 'command_name is not recognized as an internal or external command,...
If there is a better way to do this in PowerShell, I am open to it.
You need to pass a single command line to cmd to make this work:
cmd /c "call %SYSTEMROOT%\setup_Env.BAT && command_name command_arguments"
As Ansgar Wiechers points out, every cmd invocation runs in a child process, and any environment modifications made in a child process are not visible to the calling process and therefore also not to future child processes.
By contrast, in the single command line above, the environment-variable modifications performed by setup_Env.BAT are visible to command_name by the time it executes.
Caveat: If command_arguments contains %...%-style references to the environment variables defined in setup_Env.BAT, more work is needed:
Change the %...%-style references to !...!-style references.
Additionally invoke cmd with /v to enable delayed variable expansion (the equivalent of setlocal enabledelayedexpansion inside a script`:
cmd /v /c "call %SYSTEMROOT%\setup_Env.BAT && command_name args_with_delayed_var_refs"
Caveat: The above may still not work as intended if command_arguments happens to contain ! chars. that should be treated as literals (and/or command_name is another batch file containing such).
In that event, the simplest approach is to simply recreate the entire batch file in a temporary file and invoke that:
# Get temp. file path
$tempBatFile = [IO.Path]::GetTempFileName() + '.bat'
# Write the content of the temp. batch file
#'
#echo off
call %SYSTEMROOT%\setup_Env.BAT
command_name command_arguments
'# | Set-Content $tempBatFile
# Execute it.
& $tempBatFile
# Clean up.
Remove-Item -LiteralPath $tempBatFile

Using output from a PowerShell command in a windows batch file

I have a path in variable (script parameter) %2.
I need to do the following:
Extract the leaf (last folder from the path) to a variable.
Run the following command: robocopy %2 \\somepath\%leaf
I was told this could be done in PowerShell (cause I've tried going with batch file alone and failed miserably) Here's a pseudocode representation of what I'd like to achieve:
set leaf = powershell -command (split-path %2 -leaf)
robocopy %2 \\somepath\%leaf
Any idea how to write this correctly?
Thank you.
Whenever you want to set a batch variable to the output of a command, use for /f. Here's an example:
#echo off
setlocal
set "psCommand=powershell -command "(split-path '%~2' -leaf)""
for /f "delims=" %%I in ('%psCommand%') do set "leaf=%%I"
echo %leaf%
But this is a terribly inefficient way to retrieve the last folder of a path. Instead of invoking PowerShell, what you should do is this:
#echo off
setlocal
for %%I in ("%~2") do set "leaf=%%~nxI"
echo %leaf%
The %%~dpnxI notation gets
d = drive
p = path
n = name
x = extension
It's traditionally intended for files, rather than directories; but it works just as well for directories anyway. See the last couple of pages of for /? in a console window for complete details.
FOR %%a IN ("%~2") DO FOR %%b IN ("%%~dpa.") DO ECHO %%~nxb
Batch one-liner. Take the parameter (second parameter here), remove any quotes and re-apply them. Select the drive and path, add '.' then select the name and extension of the result making leaf required.
Obviously, if you require this in a variable,
FOR %%a IN ("%~2") DO FOR %%b IN ("%%~dpa.") DO set "leaf=%%~nxb"

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.

Send "any key" to cmd.exe from powershell script

I have following PowerShell script:
cmd /c script1.bat
cmd /c script2.bat
script1.bat at the end of execution have "pause" command, so the execution of my PS script stops.
How can send any key cmd.exe to avoid stopping script execution?
NOTE: I can't change batch scripts - they are 3rd party.
You can pipe input to the program (cmd.exe) like this:
"X" | cmd /c script1.bat
You could put an empty file called pause.bat in the same directory. Then it would do nothing instead of pause.
The batch scripts may be 3rd party, but surely you can just a copy/backup and edit the content to remove the PAUSE command?
I sometimes put a PAUSE in if I am testing something and don't want the window to close, but otherwise I can't think of a good reason to keep that in.
I had trouble getting the accepted answer to work for me due having an expression in the bat file path.
"x" | $Env:WRAPPER_HOME\bat\installService.bat $LOGFILE
Error is "Expressions are only allowed as the first element of a pipeline."
Here's what I got working (finally):
[PS script code]
& runner.bat bat_to_run.bat logfile.txt
[runner.bat]
#echo OFF
REM This script can be executed from within a powershell script so that the bat file
REM passed as %1 will not cause execution to halt if PAUSE is encountered.
REM If {logfile} is included, bat file output will be appended to logfile.
REM
REM Usage:
REM runner.bat [path of bat script to execute] {logfile}
if not [%2] == [] GOTO APPEND_OUTPUT
#echo | call %1
GOTO EXIT
:APPEND_OUTPUT
#echo | call %1 1> %2 2>&1
:EXIT