I am using PowerShell "(Get-Date).AddDays(-7).ToString('ddMMyyyy')" in batch script.
I want to use a variable instead of Get-Date function. Is it possible?
ADate is the variable name!
Edited:
As suggested, my script is:
For /F UseBackQ %%A In (
`PowerShell "(Get-Date).AddDays(-7).ToString('ddMMyyyy')"`
) Do Set "Freq=%%A"
Adate is simple string which comes from the file name, and has a value like 16112016.
You need to use a for loop to get the output of an external command in a batch variable:
#echo off
for /f "usebackq tokens=*" %%d in (`powershell "..."`) do set "adate=%%d"
echo %adate%
The usebackq and backticks are just so you don't need to escape the nested single quotes in your command string.
Ok. I got my crystal ball and asked it: "What is the solution here?", and it replied: "Change
PowerShell "(Get-Date).AddDays(-7).ToString('ddMMyyyy')"
by
PowerShell "(Get-Date -Date '!Adate:~4!-!Adate:~2,2!-!Adate:~0,2!').AddDays(-7).ToString('ddMMyyyy')"
", but I have no idea what it is talking about! ;)
You can store the output of the powerShell command into a file and then read that file after that delete that temporary file.
PowerShell "(Get-Date).AddDays(-7).ToString('ddMMyyyy')" >temp.txt
set /p myVarDate= < date_Shell.txt
echo Date from Shell %myVarDate%
del temp.txt
The following takes the last modified time from a known file's properties and creates a variable with a date seven days earlier, (obviously changing C:\Test\TestFile.ext as necessary):
For /F UseBackQ %%A In (
`PowerShell "((gi 'C:\Test\TestFile.ext').LastWriteTime).AddDays(-7).ToString('ddMMyyyy')"`
) Do Set "ADate=%%A"
Edit
The following example takes a date string with a known format, (in this case provided in two variables). It then converts that string to a date object, subtracts seven days and sets it back to a string in the new %ADate% variable:
#Echo Off
Set "DateStr=16112016"
Set "DFormat=ddMMyyyy"
For /F UseBackQ %%A In (`Powershell^
"([datetime]::ParseExact('%DateStr%','%DFormat%', [System.Globalization.CultureInfo]::CurrentCulture)).AddDays(-7).ToString('%DFormat%')"
`) Do Set "ADate=%%A"
Echo(%ADate%
Timeout -1
Related
I have to get the creation time of image files saved on a folder (around 4500 files) in milliseconds to evaluate errors and delays in a telescope instrumentation set up. The normal file attributes give out the time only in seconds, but WMIC gives a more precise time. I can get the creation time of a single file using
wmic datafile where "Name=''C:\\Users\\...\\test\\test.fit'" get creationdate
But when I change the where clause to get creationdate of all files, I get either invalid query or no instances found. I use the following command:
wmic datafile where "path='C:\\Users\\...\\test\\'" get creationdate
The path does not have spaces in it. What is the source of 'No instances available' error when using Where clause with path?
I tried changing the double and single quotation marks, and testing on a shorter test path and different file formats. But it did not change the result.
To elaborate on my comment:
Also the default format doesn't output milliseconds while they are of course present, just use a format string to include them
| Select-Object #{n='CreationTime';e={$_.CreationTime.ToString('yyyyMMdd HH:mm:ss.fff')}}
wmic outputs a different time stripped off the DST (daylight savings time) information.
> Get-Item C:\test\foo.zip| Select-Object Name,CreationTime
Name CreationTime
---- ------------
foo.zip 2019-07-29 19:18:00
> wmic datafile where "Name='C:\\test\\foo.zip'" get creationdate
CreationDate
20190729181800.928368+060
> gi C:\test\foo.zip| Select #{n='CreationTime';e={$_.CreationTime.ToString('yyyyMMddHHmmss.ffffffzzz')}}
CreationTime
------------
20190729191800.928368+02:00
This Batch script is just used to create a .csv file with the delimiter ; with those informations :
"File name";"File path";"The date of creation of this file"
#echo off
mode con cols=70 lines=3 & Color 0A
Title Get FilePath with their CreationDate by Hackoo 2019
Set "Target_dir=%userprofile%\Pictures\AutoSaveScreenShot\"
REM IMPORTANT Don't forget the final backslash in the path of your folder\
Call :Check_backslash %Target_dir% Target_dir
Set "LogFile=%~dpn0.csv"
If Exist "%LogFile%" Del "%LogFile%"
SetLocal EnableDelayedExpansion
#For /f "tokens=1,2 delims=:" %%a in ('Dir "%Target_dir%" /s /b /o:n /ad') do (
REM Set "Drive=%%a"
REM IMPORTANT Don't forget the final backslash in the path of your folder\
Set "Folder_Path=%%b\"
Call :Check_backslash !Folder_Path! Folder_Path
Call :Add_backSlash !Folder_Path! Folder_Path
#For /f "Tokens=1,2 Skip=1 Delims= " %%a in ('WMIC DATAFILE WHERE "PATH='!Folder_Path!'" GET CreationDate^,Caption') do (
REM The output of WMIC is unicode !
REM The trailing <CR> can be removed by passing the value through another FOR /F loop.
REM This also removes the phantom "blank" line (actually a <CR>) ie The second for /f loop
#For /f "delims=" %%f in ("%%a") do (
Set "FileName=%%~nxa"
Set "FilePath=%%a"
set "DT=%%b"
REM Convert ISO date to Date
set "DT=!DT:~0,4!_!DT:~4,2!_!DT:~6,2!_!DT:~8,2!-!DT:~10,2!-!DT:~12,2!"
Call :Scanning "!FileName!"
echo "!FileName!";"!FilePath!";"!DT!">>"!LogFile!"
)
)
)
If Exist "!LogFile!" Start "" /MAX Notepad "!LogFile!" & Exit
::---------------------------------------------------------------------
:Add_backSlash <String> <Var to Set>
Rem Subroutine to replace the simple "\" by a double "\\" into a String
Set "MyString=%1"
Set "String=\"
Set "NewString=\\"
Call Set "%2=%%MyString:%String%=%NewString%%%"
Exit /b
::---------------------------------------------------------------------
:Check_backslash
Set "datapath=%1"
IF NOT "%datapath:~-1%"=="\" SET "%2=%datapath%\"
Exit /b
::---------------------------------------------------------------------
:Scanning <file>
Cls & Color 0A
echo(
echo "%~1" ...
goto :eof
::---------------------------------------------------------------------
So, the Output file is like this one :
"Capture_06_04_2018_08_38_28.gif";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_08_38_28.gif";"2018_04_06_08-38-28"
"Capture_06_04_2018_08_39_29.png";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_08_39_29.png";"2018_04_06_08-39-29"
"Capture_06_04_2018_08_41_22.jpeg";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_08_41_22.jpeg";"2018_04_06_08-41-22"
"Capture_06_04_2018_08_44_36.png";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_08_44_36.png";"2018_04_06_08-44-51"
"Capture_06_04_2018_14_43_09.jpg";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_14_43_09.jpg";"2018_04_06_14-43-09"
"Capture_06_04_2018_14_46_53.png";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_14_46_53.png";"2018_04_06_14-47-02"
"Capture_06_04_2018_14_47_12.png";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_06_04_2018_14_47_12.png";"2018_04_06_14-47-19"
"Capture_07_04_2018_02_44_53.jpg";"c:\users\hackoo\pictures\autosavescreenshot\2018-04\capture_07_04_2018_02_44_53.jpg";"2018_04_07_02-44-53"
How can I return multiple values from a powershell script to the batch file that is calling it?
I have a powershell script that returns multiple values. I want to call it from a batch file and have each individual value go into an individual variable in the batch file.
Only been able to return one value
The powershell code (pstest.ps1):
$p1=11
$p2=22
$p3=33
exit
The batch file:
powershell .\pstest.ps1
:: now I'd like to get those 3 returned values
:: into 3 individual variables so that I can do something like this:
#echo First is %p1%, Second is %p2%, Third is %p3%
So, it should display this:
First is 11, Second is 22, Third is 33
In the exact same way you get several values from any application: one per line...
#echo off
setlocal EnableDelayedExpansion
set "i=0"
for /F "delims=" %%a in ('powershell "$p1=11; $p2=22; $p3=33; $p1; $p2; $p3"') do (
set /A i+=1
set "p!i!=%%a"
)
echo First is %p1%, Second is %p2%, Third is %p3%
I suggest you to read this answer about arrays...
For a more flexible solution using delayed expansion that doesn't require making the PowerShell script output all values on a single line and works with a variable number of outputs, see Aacini's helpful answer.
Assuming that the number of values is fixed and known in advance:
Make the PowerShell script output a single line containing all the values with a known separator.
In the batch file, use for /f "delims=... tokens=..." to capture the values in discrete loop variables, which you can assign to regular variables.
A simplified example that uses a PowerShell command to produce a single-line output with 3 values separated by | (to apply this to your scenario, use -File with your *.ps1 file instead of -c (-Command) with a command string):
#echo off
for /f "delims=| tokens=1,2,3" %%a in ('powershell -c " '11|22|33' "') do set "p1=%%a" & set "p2=%%b" & set "p3=%%c"
:: echo the values of the newly created variables
echo p1: [%p1%]
echo p2: [%p2%]
echo p3: [%p3%]
The above yields:
p1: [11]
p2: [22]
p3: [33]
Apart from using environment variables, you could try something like this base on SachaDee's answer here:
PS:
function get-multiplereturnvalues {
"11"
"22"
"33"
}
get-multiplereturnvalues
BATCH/CMD
#echo First is %p1%, Second is %p2%, Third is %p3%
for /f "delims=" %%a in ('powershell .\multi.ps1') do #echo "$Value=%%a"
Which outputs:
"$Value=11"
"$Value=22"
"$Value=33"
Within the PowerShell script I‘d create a temp file with the results. Within the batch file, after calling the PowerShell script, I‘d parse that temp file with FOR /F in order to get the results and set environment variables as required.
I'm currently trying to replace a powershell script with a cmd script as it's more suitable for what is trying to be done.
In Powershell I'm using this bit of code to return a list of personal folder directories on the computer
$Name = [Environment]::UserName
get-item HKCU:\software\Microsoft\Office\15.0\Outlook\Search\Catalog - ErrorAction SilentlyContinue | select -expandProperty property | Out-File Z:\global\pst\PowershellOutput\$Name.txt -append
This does what I want and outputs a list of directories like so
H:\PST\My Outlook Data File 1.pst
H:\PST\My Outlook Data File 2.pst
C:\PST\My Outlook Data File 3.pst
However when I run this line to extract the registry key
regedit.exe /e Z:\global\battest\%username%.txt "HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search\Catalog"
I get an output with lots of unnecessary data
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search\Catalog]
"H:\\PST\\My Outlook Data File 1.pst"=hex:0c,01,00,00,00,00,00,00
"H:\\PST\\My Outlook Data File 2.pst"=hex:f8,00,00,00,00,00,00,00
"C:\\PST\\My Outlook Data File 3.pst"=hex:ac,02,00,00,00,00,00,00
This data is passed onto another program* so a work-around could be to use data within the "" marks however it also has the double backslashes which makes the data awkward to pass on.
Is there a better way to grab these values within CMD or perhaps a parameter which I've missed which just shows the directories?
-Sorry for not including this before however this program is not a CMD program, it's visual basic
While I don't have Office installed to test, this should get the work done
#echo off
setlocal enableextensions disabledelayedexpansion
set "HKCU=&H80000001"
set "subKey=software\Microsoft\Office\15.0\Outlook\Search\Catalog"
>> "Z:\global\pst\PowershellOutput\%username%.txt" (
for /f "tokens=2 delims={" %%a in ('
wmic
/NameSpace:\\root\default
Class StdRegProv
Call EnumValues
hDefKey^="%HKCU%"
sSubKeyName^="%subkey%"
2^>nul
^| find "sNames = {"
') do for %%b in (%%a) do (
for /f delims^=^" %%c in ("%%~b") do echo(%%~fc
)
)
It uses wmic to retrieve the list of values defined under the indicated key and filter the output to only retrieve the line with the names of those values in the form sNames = {"v1", "v2", "v3"}.
The { is used to separate the start of the line from the list of values (%%a) , and this list is iterated (%%b) to get each value. The last element in the list includes an ending } that needs to be removed, this is handled by %%c using the quotes in the value as delimiters.
The equivalent vbs version could be
Option Explicit
Const HKEY_CURRENT_USER = &H80000001
Const ForAppending = 8
Const SUB_KEY = "software\Microsoft\Office\15.0\Outlook\Search\Catalog"
Const OUTPUT_PATH = "Z:\global\pst\PowershellOutput"
Dim fso, shell
Set fso = WScript.CreateObject("Scripting.FileSystemObject")
Set shell = WScript.CreateObject("WScript.Shell")
Dim values
Call GetObject( _
"winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv" _
).EnumValues( _
HKEY_CURRENT_USER, SUB_KEY, values _
)
Dim outputFile
Set outputFile = fso.OpenTextFile( _
fso.BuildPath( _
OUTPUT_PATH, shell.ExpandEnvironmentStrings("%username%") & ".txt" _
) _
, ForAppending _
, True _
)
Dim value
If Not IsNull(values) Then
For Each value In values
Call outputFile.WriteLine(fso.GetAbsolutePathName( value ))
Next
End If
Call outputFile.Close()
You would probably need to use the REG QUERY command. In a single bat file it would like like:
#echo OFF
for /f "usebackq tokens=1-3" %%A in (`REG QUERY "HKCU\Software\Microsoft\Office\15.0\Outlook\Search\Catalog"`) do (
set ValueName=%%A
)
echo %ValueName% > Z:\global\battest\%username%.txt
Got this from How can I get the value of a registry key from within a batch script?
Here is a batch code to get the list of *.pst files (not directories) which work independent on number of *.pst files and their file names as long as the file names end with .pst.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "skip=1 delims=" %%# in ('%SystemRoot%\System32\reg.exe query HKCU\Software\Microsoft\Office\15.0\Outlook\Search\Catalog 2^>nul') do (
set "RegData=%%#"
set "DelData=!RegData:*.pst=!"
if not "!DelData!" == "!RegData!" call :OutputFileName
)
endlocal
goto :EOF
:OutputFileName
set "RegData=!RegData:%DelData%=!"
echo !RegData:~4!
goto :EOF
*.pst files are Outlook Personal Storage files which are container files for emails, contacts, ...
The command FOR runs console application REG to query all values of registry key:
HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search\Catalog
The output on Windows Vista and later Windows versions is for the example:
HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search\Catalog
H:\PST\My Outlook Data File 1.pst REG_SZ whatever the
H:\PST\My Outlook Data File 2.pst REG_SZ data value is
C:\PST\My Outlook Data File 3.pst REG_SZ of each value
There is output the registry key and next the registry values each indented with 4 spaces and with 4 spaces between value name and value type and 4 spaces between value type and data value.
But on Windows XP the output is for the example:
! REG.EXE VERSION 3.0
HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\Search\Catalog
H:\PST\My Outlook Data File 1.pst REG_SZ whatever the
H:\PST\My Outlook Data File 2.pst REG_SZ data value is
C:\PST\My Outlook Data File 3.pst REG_SZ of each value
So there are first two header lines before the registry key. The registry values are also indented with 4 spaces. But there are 1 or more tabs instead of spaces between the value name and the value type and there are also 1 or more tabs instead of spaces between value type and data value.
Note: I don't know if REG_SZ is the right registry value type. I don't have this registry key at all in my Windows registry and therefore just added the 3 registry values as string values with the dummy strings.
So the tricky part is how to get the registry value name containing also spaces to get just the name of the *.pst files independent on file name.
This is done by assigning each output line with the exception of first line because of skip=1 to environment variable RegData and get next from this line everything after .pst assigned to environment variable DelData.
If the line contains .pst in any case at all resulting in value of DelData being not equal value of RegData, the subroutine OutputFileName is called for further processing and printing the file name of the *.pst file.
In the subroutine OutputFileName first the string right of *.pst file name is removed from RegData using a string substitution. Next *.pst file name is output without the 4 indent spaces at beginning.
If the number of indent spaces would not be always exactly 4 spaces, the line echo !RegData:~4! could be replaced by following code to work independent on number of indent spaces/tabs.
for /F "tokens=1*" %%I in ("%RegData%") do (
if "%%J" == "" (
echo %%I
) else (
echo %%I %%J
)
)
The usage of REG instead of REGEDIT has the advantage that no elevated privileges of an administrator is necessary for running this batch code.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
for /?
goto /?
reg /?
reg query /?
set /?
setlocal /?
Nothing is output if the registry key does not exist. The error message output to handle STDERR is suppressed here by redirecting it with 2^>nul to device NUL. See the Microsoft article Using command redirection operators for details. The redirection operator > must be escaped here with caret character ^ to be interpreted as literal character on parsing the FOR command line and being interpreted as redirection operator on execution of the REG command line by FOR.
I want to create a batch to check if the file have been modified to today's date, what i did was to "bring in a system's date and compare it with the modified date, if they match, then trigger something. My batch file works well and displays two right dates, but the IF statement saying the date mismatch.
#ECHO OFF
for /f "tokens=1,2,3,4 delims=. " %%i in ('date /t') do set date=%%k%%j
echo %date%
pause
FOR %%a IN (D:\MyFile.txt) DO SET FileDate=%%~ta
set DATEONLY=%FileDate:~0,10%
echo %DATEONLY%
pause
if DATEONLY==date (
echo date ok
)
else (
cls
ECHO Wrong
)
PAUSE
There are the following problems:
do not use variable name date as this is a built-in variable containing the current date (type set /? for help);
the first for statement is useless, because %date% is already available;
the strings DATEONLY and date are compared literally in your if statement, you need to state %DATEONLY%==%date% instead;
the else statement must be in the same line as the closing parenthesis of the if body (type if /? for help);
So try this:
#ECHO OFF
echo %date%
pause
FOR %%a IN (D:\MyFile.txt) DO SET FileDate=%%~ta
set DATEONLY=%FileDate:~0,10%
echo %DATEONLY%
pause
if %DATEONLY%==%date% (
echo date ok
) else (
ECHO Wrong
)
PAUSE
Note: Regard that all those dates in the batch file are locale-dependent.
Here is a completely different approach:
forfiles /P . /M MyFile.txt /D +0 /C "cmd /C echo #fdate #file"
The forfiles command is capable of checking the file date. In the above command line, it:
walks through the current directory (.),
lists all files named MyFile.txt (of course there is onlyone),
but only if it has been modified +0 days after today,
and then executed the command line after the /C switch.
If MyFile.txt has been modified today (or even in future), the given command line is executed;
if it has been modified earlier than today, an error message is displayed and ERRORLEVEL is set to 1.
Notice that forfiles is not a built-in command and might not be available on your operating system.
I am writing a batch script which I wish to open a file and then change the second line of it. I want to find the string "cat" and replace it with a value that I have SET i.e. %var% . I only want this to happen on the second line (or for the first 3 times). How would you go about doing this?
I just solve it myself. It will lookup var on line two only.
#echo OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET filename=%1
set LINENO=0
for /F "delims=" %%l in (%filename%) do (
SET /A LINENO=!LINENO!+1
IF "!LINENO!"=="2" ( call echo %%l ) ELSE ( echo %%l )
)
But I prefer using cscript (vbscript or even jscript).
First of all, using a batch file to achieve this, is messy (IMHO). You will have to use an external tool anyway to do the string replacement. I'd use some scripting language instead.
If you really want to use a batch, this will get you started.
This would be ugly to do with native batch scripting. I would either
Do this in VBScript. If you really need this in a batch file, you can call the VBScript file from the batch script. You can even pass in %var% as an argument to the VBScript.
Use a sed script. There are windows ports of Unix commands like GnuWin32, GNU Utilities for Win32 (I use these), or Cygwin.
I would create a script that would:
scan the input file
write to a second output file
delete the input
rename the output
As far as the dos commands to parse, I did a Google Search and came up with a good starting point:
#echo off
setlocal enabledelayedexpansion
set file=c:\file.txt
set output=output.txt
set maxlines=5000
set count=0
for /F "tokens=* usebackq" %%G in ("%file%") do (
if !count!==%maxlines% goto :eof
set line=%%G
set line=!line:*000000000000=--FOUND--!
if "!line:~0,9!"=="--FOUND--" (
echo %%G>>"%output%"
set /a count+=1
)
)
(Stolen from teh Intarwebnet)