Trying to make a variable from curl wttr.in output - windows

I want to get the outside temperature into a variable.
curl wttr.in/berlin?format=%t
produces a perfect clean output of, for example +8°C (at command prompt) that I'd like. %t is for temperature. however this
curl wttr.in/berlin?format=%t>temp.txt
set /p temp=<temp.txt
produces -7┬░C which I don't like. I just wonder if I could fix this for command instead and skip the character set problem
for /f %%x in ('curl wttr.in/berlin?format=%t') do set temp=%%x
but this one suddenly produces general multirow result instead of just the simple temperature, along with error
Could not resolve host: %t
ultimately I will need multiple variables from wttr.in so it would be most efficient to extract them all at once, and set variables accordingly, for example
curl wttr.in/berlin?format="%t+%C+%w"
where %C is conditions, %w is wind. does this mean that fixing the for loop is the way to go for simplicity?

chcp 65001
for /f "tokens=1-9,*" %%a in ('"curl wttr.in/berlin?format^="%%t+%%M+%%p+%%D+%%S+%%z+%%s+%%d+%%C""') do (
set "temperature=%%a"
set "tempfeel=%%b"
set "moonday=%%c"
set "precipitation=%%d"
set "dawn=%%e"
set "sunrise=%%f"
set "zenith=%%g"
set "sunset=%%h"
set "dusk=%%i"
set "condition=%%j"
)
Unicode fixes the unicode issue, ^ fixes the for loop, along with "tokens=1-9,*" for collecting all text based variables "%%t+%%M+%%p+%%D+%%S+%%z+%%s+%%d+%%C", where condition is a phrase, collected by the *.

Related

how to replace one line in text file without removing empty lines in batch

i am trying to code a simple script in batch that can find and replace a line
so far, I've found a snippet that works perfectly fine for my purpose the only problem is that it removes empty lines
and i can't figure out why!!
I've tried to add another if statement in this for loop but I fail
also I found that there is a bat called JREPL, i tried to run few simple commands from the docs and i failed again XD
here is the snippet:
:Variables
set InputFile=t.txt
set OutputFile=t-new.txt
set _strFind= old "data"
set _strInsert= new "data";
:Replace
>"%OutputFile%" (
for /f "usebackq delims=" %%A in ("%InputFile%") do (
if "%%A" equ "%_strFind%" (echo %_strInsert%) else (echo %%A)
)
)
i was expecting that this snippet won't remove my empty lines
and i can't figure out why
I am posting this without testing, as I do not have the environment to test as we speak.
But to explain your issue, cmd will ommit empty lines as it is built that way. It is the same as setting a variable to nothing and expecting it to return a result, so we simply assign values to each line by sort of simulating a detection of line breaks (Don't know exactly how to explain that one) but nevertheless, we will add some additional characters to the lines to ensure we get line breaks, the just get rid of them once we have them, So here goes:
#echo off
setlocal enabledelayedexpansion
set inputfile=t.txt
set outputfile=t-new.txt
set _strfind=old "data"
set _strinsert=new "data";
for /f "tokens=*" %%a in ('type "%inputfile%" ^| find /v /n "" ^& break ^> "%inputfile%"') do (
set "str=%%a"
set "str=!str:*]=!"
if "!str!"=="%_strfind%" set "str=%_strinsert%"
>>%outputfile% echo(!str!
)
That should send to output file.. You can however make the output file the same as the input as it would then be the same as replacing the text inline in the original file. Once I am able to test, I will fix the answer if there are any issues with it.
As a side note, be careful of where you have additional whitespace in your variables you set. For instance:
set a = b
has 2 issues, the variable, containing a space after a will be created with the space. So it will be seen as:
%a %
The aftermath of this is that the value of the variable will start with a leading space, so when you expected b as the value, it in fact became b
Then lastly, it is alsways a good idea to enclose your variables with double quotes, simply again to eliminate whitespace, because:
set a=b
Even though you cannot see it with your naked eyes, contains a space at the end, so doing a direct match like:
if "b"=="b"
Will result in a false statement as in fact we have:
if "b"=="b "
So the correct statement would be to set variables as:
set "a=b"
if "%a%"=="b"
which will be a perfect match.
Note I posted this from my phone, so any spelling, grammar and code issues I will resolved as I go though my answer.
…and one way using JREPL
JRepl "old \qdata\q" "new \qdata\q;" /I /XSEQ /F "t.txt" /O "t-new.txt"

How can multiple dependent variables be set on the same line in a batch file?

This was inspired by another post on Arqade, on how to backup some files using a batch script.
The following block creates a variable with the current date/time string:
REM -- Get the current date/time stamp
set DS=%date%
set TS=%time: =0%
set mm=%DS:~4,2%
set dd=%DS:~7,2%
set yyyy=%DS:~10,4%
set hh=%TS:~0,2%
set min=%TS:~3,2%
set ss=%TS:~6,2%
set ms=%TS:~9,2%
set DT_STAMP=%yyyy%-%mm%-%dd%_%hh%.%min%.%ss%
As a script writer, it is often convenient to consolidate these down to a single line. However, in this case, condensing this to a single line is extremely difficult, if not impossible.
There does not seem to be a way to put multiple set commands on a single line. Separating commands with & or && works fine, but commands on the right cannot be dependent on variables that were set earlier on the same line.
Also, notice how the time variable must have spaces replaced with zeros 0. There does not seem to be a way to do both a string replacement and get a sub-string on the same line.
Is there any way to get this down to one line? The closest I can get is two lines:
set DS=%date% && set TS=%time: =0%
set DT_STAMP=%DS:~10,4%-%DS:~4,2%-%DS:~7,2%_%TS:~0,2%.%TS:~3,2%.%TS:~6,2%
Update
This has gathered some good answers; allow me to clarify what an accepted answer must have:
An ugly, but re-usable, single line solution is OK (the pretty version is already provided above)
Standard shell commands only (PowerShell and similar must be avoided; if I wanted those, I'd just do the whole thing in PowerShell)
Ability to format the date in any logical format (should be able to do anything the pretty version above can do, not just support ISO formatted dates using shorthand notation)
It is OK to ignore localization settings (really, just this one time!)
Must be on a single line! (for instance, delayed expansion is handy, but can have nasty side-effects, is not re-usable in every context, and usually requires multiple lines)
It's trivial with a FOR /F statement, but as npocmaka already said, it depends on localization.
I assume your date/time looks like
Fri 09/14/2018
8:15:46.12
Then this works
for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set DT_STAMP=%%4-%%2-%%3_%%5.%%6.%%7
But for german localization you need
for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set DT_STAMP=%%3-%%2-%%1_%%4h%%5m%%6s
Solution with a batch macro
You could build a batch macro, the use of macros is more readable, but the macros itself are a bit more complicated
Using the macro, it assigns the current timestamp to the variable myDT1:
%#AssignTimeStamp% myDT1
echo %myDT1%
The definition of the macro (delayed expansion has to be disabled while defining the macro):
REM *** Macro definition, be sure that there aren't any trailing whitespaces
set ^"LF=^
%= This creates a variable containing a single linefeed (0x0A) character =%
^"
:: Define %\M% to effectively issue a newline with line continuation
set ^"\M=^^^%LF%%LF%^%LF%%LF%^^"
set #AssignTimeStamp=for %%. in (1 2) do if %%.==2 (%\M%
%= The next line builds the timestamp =%%\M%
for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set timestamp=%%4-%%2-%%3_%%5h%%6m%%7s%\M%
%= When a variable name was give then store the timestamp there, else output the timestamp =%%\M%
for /F "tokens=1,2 delims=, " %%1 in ("#,!argv!") do ( %\M%
for %%V in (!timestamp!) do endlocal^&if "%%~2" neq "" (set "%%2=%%V") else echo ##%%V%\M%
)%\M%
) else setlocal enableDelayedExpansion^&set argv=,
This can be done as requested using the dynamic environment variables DATE and TIME with date and time being region/country dependent using the command line:
#set "DT_STAMP=%DATE:~10,4%-%DATE:~4,2%-%DATE:~7,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"
It is expected by this command line that DATE has a date string like Sat 09/15/2018 and TIME has a time string like  4:18:23,56 and is in 24 hours format.
The environment variable DT_STAMP has value 2018-09-15_04.18.23 for the given example date and time strings.
Better would be using the command line:
#set "DT_STAMP=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"
This second version has the advantage that it does not matter if date string starts with weekday or not. But date string must have the format MM/dd/yyyy (MDY) with any type of separator like / or . or -.
But most countries use dd.MM.yyyy (DMY) (with different separator) which would require a small modification:
#set "DT_STAMP=%DATE:~-4%-%DATE:~-7,2%-%DATE:~-10,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"
See also What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean?
The replacement of all spaces by 0 is done on same command line by using a second SET command which references the environment variable DT_STAMP with %% on both sides instead of just %.
The two %% are replaced by cmd.exe during parsing the command line to % before executing the first SET command. So the command line finally executed is for example:
#set "DT_STAMP=2018-09-15_ 4.18.23" & call set "DT_STAMP=%DT_STAMP: =0%"
The usage of CALL results in a second parsing of the command line part after operator & by cmd.exe. Therefore the second SET has as argument "DT_STAMP=2018-09-15_04.18.23" and the environment variable DT_STAMP gets assigned finally the wanted date/time string.
But better would be getting the current local date/time string in wanted format independent on region/country setting which is possible also with a single command line:
#for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do #set "DT_STAMP=%%I" & call set "DT_STAMP=%%DT_STAMP:~0,4%%-%%DT_STAMP:~4,2%%-%%DT_STAMP:~6,2%%_%%DT_STAMP:~8,2%%.%%DT_STAMP:~10,2%%.%%DT_STAMP:~12,2%%"
It uses WMIC to get current local date/time region independent and once again CALL and SET to reformat the date/time string never containing a space to wanted format.
See my answer on Why does %date% produce a different result in batch file executed as scheduled task? for an explanation on how WMIC output is processed by this command line.
By the way: . is used usually as separator between file name and file extension. For that reason it would be better to use also - instead of . between hour and minute and between minute and second.
One more note: The command after call can be also any other command which uses the date/time string directly for example in a file/folder name/path.
Yes you can - with delayed expansion:
setlocal enableDelayedExpansion&set "DS=%date%"&& set "TS=%time: =0%"&set "DT_STAMP=!DS:~10,4!-!DS:~4,2!-!DS:~7,2!_!TS:~0,2!.!TS:~3,2!.!TS:~6,2!"
But this a bad way to format the date time as it depends on localization settings.Why you want to do this on one line? For settings independent approach check this.
It appears that you want an ISO-8601-like formatted date. This will produce a correctly formatted date regardless of the regional/culture settings of the system. If this is used inside a .bat script file, double the % characters on the d variable.
FOR /F %d IN ('powershell -NoLogo -NoProfile -Command Get-Date -UFormat %Y-%m-%d_%H.%M.%S') DO (SET "DT_STAMP=%d")
Or, directly in PowerShell:
$DT_STAMP = Get-Date -UFormat %Y-%m-%d_%H.%M.%S

Defining a variable in a batch file not working

I have a batch file that looks like the following:
For /f "tokens=2-4 delims=/ " %a in ('date /t') do (set newdate=%c%a%b)
blat my_file_%newdate% -to test#email.com -f test_email.com
When I enter this two commands separately in a cmd window, it seems to work perfectly fine, but when placed into a batch file and ran manually, it does not work.
Open a command prompt window and run for /?. Output is the help for this command containing at top the information:
To use the FOR command in a batch program, specify %%variable instead
of %variable.  Variable names are case sensitive, so %i is different from %I.
Next I suggest to run set /? and read at least last page of output help listing the environment variable DATE.
If Command Extensions are enabled, then there are several dynamic
environment variables that can be expanded but which don't show up
in the list of variables displayed by SET.  These variable values are
computed dynamically each time the value of the variable is expanded.
If the user explicitly defines a variable with one of these names, then
that definition will override the dynamic one described below:
%CD% - expands to the current directory string.
%DATE% - expands to current date using same format as DATE command.
%TIME% - expands to current time using same format as TIME command.
%RANDOM% - expands to a random decimal number between 0 and 32767.
%ERRORLEVEL% - expands to the current ERRORLEVEL value
%CMDEXTVERSION% - expands to the current Command Processor Extensions
    version number.
%CMDCMDLINE% - expands to the original command line that invoked the
    Command Processor.
%HIGHESTNUMANODENUMBER% - expands to the highest NUMA node number
    on this machine.
So there is perhaps no need to run in a separate command process in background with cmd.exe /C the command line date /T as done by FOR with the posted command line, capture output of this command process, and process it line by line by FOR.
Well, the format of date output by date /T or on using %DATE% depends on Windows region setting. And it was not posted what is the date format on used machine with used account. But I suppose that following works also a very little bit faster.
for /F "tokens=2-4 delims=/ " %%a in ("%DATE%") do set "newdate=%%c%%a%%b"
I suppose using only string substitution works also on your machine for your account with a date format MM/dd/yyyy or dddd, MM/dd/yyyy:
set "newdate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%"
This last solution is some microseconds faster than the others.
There is also a region independent solution as explained in detail for example by the answer on Why does %date% produce a different result in batch file executed as scheduled task? But the region independent solution using WMIC is much slower in comparison to the usage of the dynamic environment variable DATE.
Batch variable need to have %% instead of only one
It appears you are looking for the output to be YYMMDD, if so try this:
For /f "tokens=1-4 delims=/ " %%a in ('date /t') do (set newdate=%%c%%a%%b)

Batch File - Read specific line, and save a specific string in that line as a variable

Is there any way to get for /f loop (or anything else) to read a specific line?
Here is the code I have so far, it reads first word of every line.
#echo off
set file=readtest.txt
for /f "tokens=1 delims= " %%A in (%file%) do (echo %%A)
pause
If someone can point me in the right direction, it'd be much appreciated.
Thanks
Additional Information: I want to make a batch file which will rename a TXT file to a string within that TXT file, located at a specific location. I have figured out how to rename files, all I need to learn to do is to retrieve a string (located at a specific location) with in the file which will go into the name of that TXT file.
Since you haven't fully defined what you mean by "a specific location", I'll make some (reasonable, in my opinion) assumptions, though the method I present is equally valid no matter what your definition turns out to be.
You can get arbitrary lines and arbitrary words on that line by using a line counter variable in conjunction with tokens.
Let's assume your text file name can be found as the second argument on the fourth line of the infile.txt file. You can get that with something like:
#setlocal enableextensions enabledelayedexpansion
#echo off
set /a "line = 0"
for /f "tokens=2 delims= " %%a in (infile.txt) do (
set /a "line = line + 1"
if !line!==4 set thing=%%a
)
endlocal & set thing=%thing%
echo %thing%
This actually uses a few "tricks" which warrant further explanation:
the line counter to ensure you only grab what you want from a specific line, though you could change the test !line!==4 into anything you need such as a line beginning with #, the fifth line containing the string xyzzy and so on.
the use of setlocal/endlocal to effectively give you a scope from which variables cannot leak. This is good programming practice even for a language often not normally associated with such things :-)
the use of endlocal & set to bypass that scope so that thing is the only thing that does actually leak (as it should).
the use of delayed expansion and !..! variables to ensure they're correct within the for loop. Without this, the %..% will always be expand to the value they were set to when the for loop started.
Those last two bullet points are actually related. %..% variables are expanded when the command is read rather than when it is executed.
For a for loop, the command is the entire thing from the for to the final ). That means, if you use %line% within the loop, that will be evaluated before the loop starts running, which will result in it always being 0 (the variable itself may change but the expansion of it has already happened). However, !line! will be evaluated each time it is encountered within the loop so will have the correct value.
Similarly, while endlocal would normally clear out all variables created after the setlocal, the command:
endlocal & set thing=%thing%
is a single command in the context of expansion. The %thing% is expanded before endlocal is run, meaning it effectively becomes:
endlocal & set thing=whatever_thing_was_set_to_before_endlocal
That's why the use of setlocal and endlocal & set is a very useful way to limit variables "escaping" from a scope. And, yes, you can chain multiple & set stanzas to allow more variables to escape the scope.

Determine Number of Tokens - BATCH

I'm currently working on a mass user creation script through PowerShell and Batch. At the moment the script is 95 lines and is the largest script I've ever written in Batch.
I want the script to be as automated as possible and plan to give it to clients that need help creating a mass number of users. To do this, I have a 21 line settings file and one of the variables that I need is the full domain name (This is needed for dsadd)
The problem with this is that users may have any number of variables for this - anywhere from two in testlabs to four in places like schools. I am so far able to separate the tokens however I need them all stored as variables like %%a, %%b and not to store everything as %%a. The number of tokens will be dynamic so I need some sort of solution to this. Something like this (Yes I know this is not the correct syntax):
if number of tokens=4 (
dsadd "cn=%%a,ou=%%b,dc=%DSuffix1%,dc=%DSuffix2%,dc=%DSuffix3%,dc=%Dsuffix4% )
In that line %%a and %%b are variables in another for loop later in the code that reads from a user list excel file. I would need something like that for anything from two tokens to four tokens. I don't mind if the solution to this is not purely BATCH however that is my preferred option.
Thanks.
EDIT: Here is the for loop I have at the moment, this is nested in another larger for loop that adds the users:
for /f "tokens=1,2,3,4 delims=." %%a in (Settings.ini) do (
set L=22
if !L!=22 set DSuffix1=%%a&& set DSuffix2=%%b&& set DSuffix3=%%c&& set DSuffix4=%%d
)
The settings.ini file contains various settings such as the Exchange server and path to the user's home directory. The line I need to interpret is line 22 which looks like this:
DOMAINNAME.SUFFIX(.SUFFIX.SUFFIX.SUFFIX)
A real life example would be:
testlab.local
or
testlab.ghamilton.local
For a testlab the setting should only be the domainname and suffix although for others such as schools or institutions the number of domain suffixes can go up to four. I want to interpret this.
EDIT: Managed to indent code correctly, sorry.
If I understand you right, you have a string such as domain.foo.bar.baz and you want to change that to dc=domain,dc=foo,dc=bar,dc=baz.
How about this?
set domain=domain.foo.bar.baz
echo dc=%domain:.=,dc=%
That should echo this:
dc=domain,dc=foo,dc=bar,dc=baz
That %domain:.=,dc=% line is expanding the %domain% environment variable, but replacing all . with ,dc=. See http://ss64.com/nt/syntax-replace.html for more details.
To do string replacement in an environment variable, you do need the value to be in an environment variable first. Here's a script that will read line 22 from settings.ini, combined with the above technique:
setlocal enabledelayedexpansion
set L=0
for /f %%a in (settings.ini) do (
set /a L=!L! + 1
if !L! equ 22 (
set domain=%%a
echo dc=!domain:.=,dc=!
)
)
L is being used to count what line we're on. When we reach line 22, we set an environment variable to the value of the entire line (%%a), then do the string substitution as above (but with ! rather than % to take advantage of delayed expansion).
Of course instead of an echo, you would do something like
dsadd "cn=%cn%,ou=%ou%,dc=!domain:.=,dc=!"
Here's my little snippet to sort of demonstrate how to get the separate tokens (the delims are space and tab, but you can change them).
#echo off & setlocal enabledelayedexpansion
for /f "tokens=1,2,3,4" %%a in (test.txt) do (
set token1=%%a
set token2=%%b
set token3=%%c
set token4=%%d
set full=%%a%%b%%c%%d
)
set token
set full
pause>nul
test.txt contains:
first second third
The output was:
token1=first
token2=second
token3=fourth
full=firstsecondthird
So, you could judge how many tokens there is by something like
for /l %%i in (4,-1,1) do if not defined token%%i set amount=%%i
Or something along the same lines.
Hope that helps.

Resources