I am facing an issue to get some data from an API using the command lines in Windows. Basically, this is what I want to do:
1- Get a list of clients from an API (using curl and jq),
2- Save this list in a .txt (I wanted to save it in a variable but did not manage),
3- Loop through the list of clients within the .txt and send a new API request to download a csv specific to this client
The first 2 steps are working fine, but I'm stuck on the last bit to loop through the clients. Here is my code:
setlocal ENABLEDELAYEDEXPANSION
Set tok=XXXXX
Set hURL="https://api.website.com/v2/clients?token=%tok%"
Set IDPath="C:\Users\My Self\subIDs.txt"
Set cuPath=%~dp0
del %IDPath%
curl -sS %hURL% | jq ".clients[].id" > %idPath%
FOR /F %%i in (%IDPath%) do (
echo %%i
Set subID=%%i
Set rURL="https://api.website.com/v2/data?token=%tok%&subscriptionId=!subID!"
curl -sS -o cuPath!subID!.csv !rURL!
)
endlocal
pause
What looks odd to me:
- echo %%i returns: C:\Users\My
It looks like the space within the path is a problem for this loop, but it didn't cause any issue to save the file, so I'm a bit lost
- I am using !subID! as I understand it's the only way to get the variable ajusted each time however when looking at the output of the !rURL! variable, while %tok% is successfuly passed as XXXXX, !subID! remains as !subID!
- I finally get, of course, a curl: (6) Could not resolve host: rURL
I am completely new to APIs, batch or JSON and even though I feel I am getting closer, I am now stuck on this last bit. Any idea how to solve this?
Thanks,
You had some mistakes in your code which is fixed in the following code.
But since you have not specified the exact format that is returned by jq ".clients[].id" I could only guess that it returns a single token without any spaces between the characters, but it may have leading or trailing spaces.
So if this is not the case you have to setup the FOR /F options accordingly.
#echo off
setlocal DisableDelayedExpansion
Set "tok=XXXXX"
Set "hURL=https://api.website.com/v2/clients?token=%tok%"
Set "rURL=https://api.website.com/v2/data?token=%tok%&subscriptionId="
Set "IDPath=C:\Users\My Self\subIDs.txt"
:: No need to delete %IDPath%, It will be overwrriten by the below redirection
curl -sS "%hURL%" | jq ".clients[].id" > "%IDPath%"
FOR /F "usebackq" %%i in ("%IDPath%") do (
echo subID=%%i
curl -sS -o "%~dp0%%i.csv" "%rURL%%%i"
)
REM del "%IDPath%"
endlocal
pause
Some points:
You have assigned the variables with embedded surrounding quotes in their values, while this will work in your particular case, It is a best practice to not embed the quotes in variables values, So instead of Set hURL="https://..." use Set "hURL=https://..." and you can later surround the variable in quotes when actually using them: "%hURL%"
Think of the situation where you want to access the value without quotes, then you have to write additional code to remove the quotes from the value if you assigned the value by set a="value" . But by set "a=value" you can access to both quoted ("%a%" ) and unquoted (%a%) value at the same time.
You have used Set cuPath=%~dp0 without quotes, it should be Set "cuPath=%~dp0" but it is not needed either, you could just use %~dp0 directly in the loop.
And later you've used cuPath in the FOR loop without expanding it and without surrounding the cURL output file in quotes.
When reading a file contents by FOR /F, if the path to the is surrounded in quotes then you have to use the UseBackq option to change the interpretation of quotes in the IN clause of the FOR to not interpret it as literal string.
Based on what you are doing, delayed expansion is not needed at all.
But as side note you have to be more careful when using delayed expansion. when delayed expansion is enabled, any literal string that you may use in your code, including file and url paths, will get corrupted if they contain the ! character.
So It is better setup the required variables and strings while delayed expansion is off and later access them by !var! syntax when delayed expansion is enabled.
Related
In a Batch file I need to add some paths to the PATH env variable. Since its a larger numer of long paths I tried to spread them on multiple line and tried to make the bat-file as clean as I can by indenting the lines.
But it seems that the spaces at the beginning of the newline (and so in the %PATH%) are interpreted as part of the actual path.
So
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
does not work (programs are not found). Is there some trick I can use?
Because it is a medium complex batch file some indentation would be nice.
for %%x in (
"\\somewhere\Tools\strawberryperl\perl\site\bin;"
"\\somewhere\Tools\strawberryperl\perl\bin;"
"\\somewhere\Tools\strawberryperl\c\bin;"
"\\somewhere\Tools\KDiff3;"
) do call set "path=%%path%%%%~x"
this will append the extra items to the path. You'd need to initialise path to nothing first if all you want is to build the directory sequence specified.
There is no way to have PATH ignore the leading spaces. I see two possible options if you want indented lines:
Option 1 - Use undefined variable to indent, so spaces never get in the value
#echo off
SET PATH=^
% =%\\somewhere\Tools\strawberryperl\perl\site\bin;^
% =%\\somewhere\Tools\strawberryperl\perl\bin;^
% =%\\somewhere\Tools\strawberryperl\c\bin;^
% =%\\somewhere\Tools\KDiff3;^
% =%%PATH%
Option 2 - Remove the spaces afterwards
#echo off
SET PATH=^
\\somewhere\Tools\strawberryperl\perl\site\bin;^
\\somewhere\Tools\strawberryperl\perl\bin;^
\\somewhere\Tools\strawberryperl\c\bin;^
\\somewhere\Tools\KDiff3;^
%PATH%
set "PATH=%PATH:; =%"
First let me start by informing you that adding to the PATH variable in this way is ONLY for the running session. Once the cmd session is closed that variable returns to its previous value.
Here are a suggestion, append each addition one by one:
SET "ToAdd=\\somewhere\Tools\strawberryperl\perl\site\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\perl\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\strawberryperl\c\bin;"
SET "ToAdd=%ToAdd%;\\somewhere\Tools\KDiff3"
SET "PATH=%PATH%;%ToAdd%"
BTW, if you were hoping to add to the environment variable beyond the running session then it is important that you ignore anyone suggesting you use SETX instead of SET. (the variable will be truncated at 1024 bytes therefore corrupting it). Your best solutions would involve editing the registry and possibly using a built in tool such as powershell.
Edit
This shows the method mentioned in my comment and uses the same structure as Magoo's answer:
C:\MyDir\Paths.txt
\\somewhere\Tools\strawberryperl\perl\site\bin
\\somewhere\Tools\strawberryperl\perl\bin
\\somewhere\Tools\strawberryperl\c\bin
\\somewhere\Tools\KDiff3
batch file
#Echo Off
SetLocal EnableDelayedExpansion
For /F "UseBackQDelims=" %%A In ("C:\MyDir\paths.txt") Do Set "Path=!Path!;%%~A"
Echo(%Path%
EndLocal
Timeout -1
This means that you only really need to include the for loop each time instead of adding each of the paths to it.
Not even remotely bulletproof, but the Magoo's answer reminded me this. Just because someone, somewhere, could find a better usage for this construct
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "delims=" %%a in ('echo "%path:;=" "%"
"\\somewhere\Tools\strawberryperl\perl\site\bin"
"\\somewhere\Tools\strawberryperl\perl\bin"
"\\somewhere\Tools\strawberryperl\c\bin"
"\\somewhere\Tools\KDiff3"
""') do (set "path=%%~a") & call set "path=%%path:" "=;%%"
path
I have a very simple Batch script that I'm working on
I'm considering implementing an updater with wget and version files locally and hosted on my server (yet I'm using Batch for some reason)
#echo off
for /f "tokens=* delims=" %%x in (version.txt) do set version=%%x
if "%version%" == "1.0.0" (echo yes)
the text file it's referencing contains nothing but
1.0.0
Not even a new line. Echoing %version% gives me 1.0.0 in the console, but the if statement gets nothing.
Anyone wanna point out my error? I'm honestly clueless
You appear to have a naughy space on the end of the set command.
Batch is sensitive to spaces in a SET statement. SET FLAG = N sets a variable named "FLAGSpace" to a value of "SpaceN"
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned. set /a can safely be used "quoteless".
This is tricky. You have a space at the end of set version=%%x.
As a general point of scripting convention, whenever you set a variable to a string in a batch script, unless you have a specific reason not to do so, you should do set "variable=value" with the var=value pair quoted. That helps avoid problems like this. Your for /f line should end with set "version=%%x"
With the trailing space, you're basically saying if "1.0.0 " == "1.0.0", which is false.
As a side note, it is possible to fetch the content from your web server without using wget if you wish.
I'm making a batch using the current location to make some changes the problem is that this place may have the & and thus cause errors ... I wonder if there is a way to make a script that checks for the & in the variable and if you add has the ^&.
the way I am using this might be.
Set "Local_Script=%~dp0">nul 2>&1
Echo %Local_Script%> a00_Local.ini
Note: The txt file can only be with the way.
Escaping characters is fine when your code is supplying a string literal. But it is often impractical when dealing with existing strings contained within variables.
There are simpler solutions:
1) delayed expansion:
Note that you must assign batch arguments and/or FOR variables to environment variables prior to enabling delayed expansion. Otherwise values containing ! will be corrupted.
#echo off
set "local_script=%~dp0"
setlocal enableDelayedExpansion
echo !local_script!>a00_local.ini
2) transfer the quoted value to a simple FOR variable, then use the ~ modifier to safely remove the quotes:
#echo off
for %%F in ("%~dp0") do echo %%~F>a00_local.ini
3) transfer the quoted string value to a FOR /F variable, which automatically removes the quotes:
#echo off
for /f "delims=" %%F in ("%~dp0") do eho %%F>a00_local.ini
I have three variables: string, search, replace. I wish to substitute the %search% in %string% with %replace%.
This works but needs hard characters.
SET modified=%string:morning=evening%
This seems to be answer in the forums but does not work. It simply stores the entire line at %modified%
SET modified=!string:%search%=%replace%!
The ! format is doing delayed expansion--the % variables get expanded immediately, but the ! variable gets expanded only when it's needed. I believe that only works in a batch file, so if you're experimenting directly at the command line you won't get the same behaviour as if you are running a batch file.
Make sure to enable delayed expansion in your batch file before using the ! notation, like this:
SETLOCAL ENABLEDELAYEDEXPANSION
SET string=This morning
SET search=morning
SET replace=evening
SET modified=!string:%search%=%replace%!
ECHO %modified%
ENDLOCAL
This will echo This evening.
i am working on a batch script.
i want to store the count of row's in variable.
like
set var = mysql -uroot -proot -e"select count(*) from table";
i also tried to do it other way like
set var= mysql -uroot -proot -e "select count(*) from table into outfile 'F:\count.txt'";
for /f %%a in ("F:\count.txt") do (
set output = %%a
echo %output%
pause
)
In above code the variable "output" shows nothing(empty).
please help me out.
I can see at least two issues in your script:
A string in double quotes inside IN( ) is treated as a literal, not as a file path/name, unless you specify the usebackq option, which enforces different semantics, whereby either double-quoted string or non-quoted one is treated as a file name.
You are storing <space>%%a into the output<space> variable, not %%a into output.
After you've fixed those two, there will remain one (probably, just one) more issue. You are assigning a value to a variable and then evaluating the variable in the same bracketed block (which is your loop body) using immediate variable expansion (%var%). This cannot work as expected. The thing is, a bracketed block is parsed entirely as a single unit, i.e. all its commands are parsed before the first one executes. As you can guess, your %output% expression will in this case evaluate to nothing, because output is not yet assigned a value at the time of parsing. (And when it is assigned a value, it will change nothing, because the previous (empty) value will already have replaced the expression.)
You can solve this using delayed variable expansion, which, as can be guessed, uses a different timing for evaluation. First, you should enable delayed expansion by issuing the SETLOCAL EnableDelayedExpansion command, then use a slightly different syntax: !var! instead of %var%.
So, if we address all the issues mentioned above, the loop may look like this:
…
SETLOCAL EnableDelayedExpansion
FOR /F "usebackq" IN ("F:\count.txt") DO (
SET output=%%a
ECHO !output!
)
You just define var with the content mysql -uroot ... but you don't execute it!
So there shouldn't be a F:\count.txt file.
Your set-syntax is wrong, remove the spaces ( from set output = %%a), else you create a variable output<space> instead of output
Your code could look like
mysql -uroot -proot -e "select count(*) from table into outfile 'F:\count.txt'";
setlocal EnableDelayedExpansion
for /f "delims=" %%a in (F:\count.txt) do (
set "output=%%a"
echo !output%!
)
You are on the right track, but the FOR /F command you show is not proper syntax - it would give an error (I'm ignoring the IN() clause because I know it is intentionally incomplete).
The "tokens=" option is incomplete, it must be followed by at least one number or an asterisk - see the documentation for more info (type HELP FOR from the command line). In your case you don't need a tokens option - the "delims=" is all you need.
There must be at least one space between DO and the (.
If you make those fixes and complete your IN() clause, then it should work. If it doesn't then something may be wrong with your IN() clause. You should post the entire command if you want help diagnosing the problem.