Using output from a PowerShell command in a windows batch file - powershell

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"

Related

How to set a variable in cmd which is a string from powershell command result?

I want to store the result of powershell command in cmd variable as String : powershell -com "(ls | select -Last 1).FullName". How to do this?
CMD does not have a straightforward way of assigning command output to a variable. If the command produces just a single line you could use a for loop
for /f "delims=" %a in ('some_command /to /run') do #set "var=%a"
However, if the command produces multiple lines that will capture only the last line. A better approach would be redirecting the output of the command to a file and then reading that file into a variable:
set "tempfile=C:\temp\out.txt"
>"%tempfile%" some_command /to /run
set /p var=<"%tempfile%"
del /q "%tempfile%"
If you literally need only the last file in a directory you don't need to run PowerShell, though. That much CMD can do by itself:
for /f "delims=" %f in ('dir /a-d /b') do #set "var=%~ff"
Beware that you need to double the % characters when running this from a batch file.
A FOR loop can provide the path to the file. If the default directory sorting order is not the result needed, specify additional command line switches on the DIR command.
FOR /F "delims=" %F IN ('DIR /B') DO (SET "THE_FILE=%~fF")
ECHO THE_FILE is "%THE_FILE%"
In a .bat file script, double the percent characters on FOR loop variables.
FOR /F "delims=" %%F IN ('DIR /B') DO (SET "THE_FILE=%%~fF")
ECHO THE_FILE is "%THE_FILE%"
The .bat file scripts can also run PowerShell scripts. It is best practice to not use aliases such as ls in scripts.
FOR /F "delims=" %%F IN ('powershell -NoLogo -NoProfile -Command ^
"(Get-ChildItem -File | Select-Object -Last 1).FullName"') DO (SET "THE_FILE=%%~fF")
ECHO THE_FILE is "%THE_FILE%"
The problem with cmd here is that I want to get the full paths for
FOLDERs NOT recursive... this dir /ad /b doesn't give full paths and
this dir /ad /b /s does it recursively... – stakowerflol 2 hours ago
That's not a problem, you can return the full file path without recursing.
If you are changing directory to the path you need to check then it's stored in %CD%
If you need the path to whee the Script itself is it's stored in %~dp0
If you want to provide an argument to specify and arbitrary path and get all of the listings it will be that argument term (EG %~1)
With all three possible options you can do the same thing:
Either
Prepend the provided variable to the output of the chosen directory enumeration method
OR
Use a For loop to get the file names at that path and show the result with the full path.
IE
Jenkins_A_Dir.bat
#(
SETLOCAL
ECHO OFF
SET "_Last="
ECHO.%~1 | FIND /I ":\" > NUL && (
SET "_CheckHere=%~1"
)
IF NOT DEFINED _CheckHere (
SET "_CheckHere=C:\Default\Path\When\No Argument\Specified"
)
)
REM Use a For loop to get everything in one variable
FOR %%A IN (
"%_CheckHere%\*"
) DO (
SET "_Last=%%A"
)
ECHO.Found "%_Last%"
REM Use `FOR /F` with DIR, and append the path to Check:
SET "_Last="
FOR /F "Tokens=*" %%A IN ('
DIR /A-D /B "%_CheckHere%\*"
') DO (
SET "_Last=%_CheckHere%\%%A"
)
ECHO.Found "%_Last%"
Of course you don't NEED to have set a variable such as _CheckHere
Instead, you can just replace all of the instances of %_CheckHere% with `%~1 instead, that would work just fine in the above examples too.
Okay, what if you just wanted to check the location the script was running in.
Then either change the above to use SET "_CheckHere=%~dp0" or Replace %_CheckHere% with %~dp0 throughout.
Okay but what if you don't want to set a variable you want to it to use the current working directory.
When then, even easier:
Jenkins_Current_Dir.bat
#(
SETLOCAL
ECHO OFF
SET "_Last="
)
REM Use a For loop to get everything in one variable
FOR %%A IN (
"*"
) DO (
SET "_Last=%%~fA"
)
ECHO.Found "%_Last%"
REM Use `FOR /F` with DIR, and let it append the current working directory to the path:
SET "_Last="
FOR /F "Tokens=*" %%A IN ('
DIR /A-D /B "*"
') DO (
SET "_Last=%%~fA"
)
ECHO.Found "%_Last%"

Is there a way to capture the file path and name in the same batch function?

I am creating a batch script to perform robocopy functions. Currently I am having to call two different PowerShell selections, one for the file name and then one for the source folder, can I combine this?
Using the code below I can capture the file name, but can I capture both using one method?
echo Select your file
set pwshcmd=powershell -NoProfile -Command "&{[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null;$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog; $OpenFileDialog.ShowDialog()|Out-Null; $OpenFileDialog.SafeFileName}"
for /f "delims=" %%I in ('%pwshcmd%') do (
set "FileName=%%I"
)
echo %FileName%
pause
I wish for the user to make one selection with PowerShell and it set two variables %FileName% and %FilePath%, as this will be used in the robocopy script.
I appreciate everyone's input on this question. I had solved it shortly after posting, however I did run into the fact that robocopy wants a directory path without the ending "\". The method used to gather both paths were:
for /f "delims=" %%I in ('%pwshcmd%') do (
set "r_copy_file_source_path=%%~dpI"
set "r_copy_file_source=%%~nxI"
)
This provided me with both variables required, then I trimmed the ending "\" from the path with:
set r_copy_file_source_path=%r_copy_file_source_path:~0,-1%
Again, I appreciate the responses! Thank you!

How to pass batch file variables to PowerShell script? [duplicate]

This question already has an answer here:
Displaying SET variable
(1 answer)
Closed 3 years ago.
I am attempting to pass a number of variables processed by my batch file to a Powershell script. The problem I face is that firstly the entire results from the batch file come up in command prompt and next to the variables I intend to pass are not passed to the Powershell Script. Additionally, the variable I have to output the contents of the log file in just send the command back to the screen.
I have tried the following links and these links got me as far as I am now:
Batch file to execute a Powershell script
Pass variable from batch to powershell
Pass parameter from a batch file to a PowerShell script
Pass batch variables with spaces in them to powershell script?
Batch File side
set LOG_FILE = "GDGAGnklasj;oks;fk;dkf lkl;"
set oName = Name
set oStart = "%YYYY%%MM%%DD% %TIME%"
set oStatus = 0
set oEnd = "%YYYY%%MM%%DD% %TIME%"
set oDateRan = %YYYY%%MM%%DD%
set oLog =for /f "delims=" %%i in (%LOG_FILE%) do set content=%content% %%i
echo Updating Database >> %LOG_FILE% 2>&1
cmd /S powershell.exe -ExecutionPolicy Bypass -File "C:\Reporting\updateTool.ps1" "%oName%" "%DateRan%" "%oStart%" "%oEnd%" "%oStatus "%oLog%
PowerShell Script
param (
[string]$oName
)
"This is $oName"
My intent is to set the variables within the batch file then send them to Powershell for processing.
Be very careful of spaces.
set oName=taco
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '.\ScriptName.ps1' -oName '%oName%' "
Oh Easy-Peasy, I do this for my Power shells that we need CMD wrappers for quite a bit.
I have to run to the train so this is going to be a bit meh at the moment I will firm it up in a bit, right now just going to paste in some example code so I can make it your code
Okay, what, umm, what did you intend for this Particular code to.l do ? I can't seem to figure out what you were intending with this, is it just some dummy code?
set oLog =for /f "delims=" %%i in (%LOG_FILE%) do set content=%content% %%i
echo Updating Database >> %LOG_FILE% 2>&1
Okay on further review I think you want to read the log into a couple of sttring variables in CMD, then use one of them in your call of the script..... but, why?
The strings will append to each other and you will be limited to 8191 characters max, and PowerShell can easily read the content of the log file because you pass the name to Powershell.
That seems like a better plan, no?
All your code where you have YYYY MM DD those are variables you will need to define before using, not sure if that is understood if so all good.
.CMD Script:
#(
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_PSScript=C:\Reporting\UpdateTool.ps1"
REM SET "_DebugPreference=Continue"
SET "_DebugPreference="SilentlyContinue"
SET "_LOG_FILE=GDGAGnklasj;oks;fk;dkf lkl;"
SET "_oName=Name."
SET "_oStart=%YYYY%%MM%%DD% %TIME: =0%"
SET /a "_Status=0"
SET "_oEnd=%YYYY%%MM%%DD% %TIME: =0%"
SET "_oDateRan=%YYYY%%MM%%DD%"
)
SET "_PSCMD=Powershell "%_PSScript%" -DebugPreference "%_DebugPreference%" -LOG_FILE "%_LOG_FILE%" -oName "%_oName%" -oStart "%_oStart%" -Status %_Status% -oEnd "%_oEnd%" -oDateRan "%_oDateRan%" "
%_PSCMD% 2>&1 >> "_LOG_FILE"
PS1:
## Script: UpdateTool.ps1
#
param(
[String]$LOG_FILE = 'c:\admin\default.log',
[String]$oName = 'default name'
[String]$oStart = $(Get-date -F "yyyyMMdd HH:mm:ss.ms"),
[Int]$oStatus = 0,
[String]$oEnd = $(Get-date -F "yyyyMMdd HH:mm:ss.ms"),
[String]$oDateRan = $(Get-date -F "yyyyMMdd"),
$DebugPreference = "SilentlyContinue"
)

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

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)

Command Prompt "dir /b /s > file.txt" need to remove directories from results

I am using the following command to dump the complete file listings recursively from a directory.
dir /b /s c:\myfolder > c:\mylist.txt
This works fine but it is display the results with the full path as well, beacuse I am using a regex expression on the results I need them to display only the filenames.
Anyone any ideas?
Kind of an old question but if someone stumbles across this hoping for an answer, perhaps this will help them out.
Running this from the windows command line (CMD.exe) use:
setlocal enabledelayedexpansion
for /f "delims=" %a in ('dir /b /s c:\myfolder"') do (#echo %~nxa >>c:\mylist.txt)
endlocal
Running this from a windows .BAT script use:
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('dir /b /s c:\myfolder"') do (#echo %%~nxa >>c:\mylist.txt)
endlocal
The output might look something like this depending on what files are in the folder you're running the code in:
file1.fil
file2.fil
file3.fil
UNDERSTANDING WHAT THE CODE IS DOING
for /f
means to run a loop through files in this case using the dir /b /s command to help get those files names from directories (folders) and subdirectories (subfolders). As stated in the question, this will give you complete paths to the files. So instead of file.txt you will get C:\folder\file.txt.
"delims="
in this case tells the for loop that it wants the variable %a or %%a
to only have 1 folderpath and filename for every loop.
%a (CMD.exe) %%a (.BAT)
as mentioned above is a variable that changes with each loop. so
everytime the command dir /b /s finds a new filename the variable
%%a changes to the filename.
example:
Loop 1: %%a = c:\folder\file1.fil
Loop 2: %%a = c:\folder\file2.fil
dir /b /s
is the command to print out the files of a directory (folder). By
using the /b and /s the questioner is adding additional criteria.
the command dir not only prints out the files and directories
(folders) in that directory (folder) but also some additional
information about the directory.
the /b tells the command dir that it doesn't want the additional
information.. just the filenames.
The /s tells the command dir to include all the files and
subdirectories (subfolders) in that folder.
do
is the part of the loop that tells what to do during that particular
loop. So in this case it is only doing this one command every loop
(#echo %%~nxa >>c:\mylist.txt)
#echo
is a simple command that prints out whatever you want either to your
computer screen or in this case to a txt file by using #echo %%~nxa
>>c:\mylist.txt
the >> before c:\mylist.txt is especially important. Every time a
loop happens it starts a new line in the txt file and writes the
variable to that line. If only one > is specified it will overwrite
the line in the txt file everytime the loop happens. Which will
defeat the purpose of what this script is designed to do.
%~nxa (CMD.exe) %%~nxa (.BAT)
is the variable %%a as mentioned above except it is parsed (edited)
out the way the questioner #fightstarr20 asked for. Instead of
printing out the variable as C:\myfolder\myfile.fil the variable
will print out as myfile.fil
the ~ in %%~nxa tells the program you want to modify the variable
%%a. In this case by adding n and x.
the n in %%~nxa tells the program you want to modify the variable %%a by
excluding the path from the variable.
example.
-variable %%a = C:\folder\filename.fil
-variable %%~na = filename.
-If you notice however that it leaves the extension .fil off of the filename.
the x in %%~nxa tells the program you want to modify the variable %%a
by excluding the path and the filename from the variable, so all you will get is the extension of the filename.
example.
-variable %%a = C:\folder\filename.fil
-variable %%~xa = .fil
so if you combine both of the modifiers n and x to the variable %%a
you will get the full filename including the extension.
example:
-variable %%a = c:\folder\filename.fil
-variable %%~nxa = filename.fil
setlocal enabledelayedexpansion
explained simply is a command that needs to be in the script before
the for loop in order to allow the variable %%a to be modified or "expanded".
endlocal
this turns off the setlocal enabledelayedexpansion command
To get a very helpful explanation and reference for CMD commands I recommend reading ss64.com and for a great forum to get CMD answers I'd recommend dostips.com
Change your regex to get the filename from the entire path.
If you can use powershell, look at Get-ChildItem. You can have more powerful options with it.
Use it like this
dir /b /s C:\myfolder>C:\temp.txt
echo exit>>C:\temp.txt
goto loop
:loop
set /p _x=<temp.txt
findstr /v /c:"%_x%" temp.txt>temp2.txt
type temp2.txt>temp.txt
set _x=%_x:*\=%
echo %_x%>file.txt
if "%_x%" == "exit" (
del temp.txt
del temp2.txt
exit
)
goto loop
You can use for instead of goto if you like, but it will be basicaly the same.
Sorry about the last one...
I know I'm a bit late, but it hurts me that nobody said to take away the /s
dir /b c:\myfolder > c:\mylist.txt
That should do it.
This would surely work, as it works for me.
dir D:(Path to files) /s /b >d:\filelist.txt
You can just use this code:
dir /b > A_fileslist.txt
Copy inside a notepad editor and save as "Fileslist.bat".