I'm gonna try to explain how my case is:
Lets say I have the number 30 and I want to make a batch script that adds 1 to that number untill its modulo divide 4
set /a number = 30 %% 4
how can I make it know to add 2 to 30 to make it mod 4 ?
Thx in advance
Is this what you need?
#echo off
set num=30
:loop
set /a num=num+1
set /a number=num %% 4
if %number% NEQ 0 goto :loop
echo %num%
Related
I need to print the following based on the variable n.
Example: if n=2
I need to print:
1. -3 0
2. 3 0
3. 0 -3
4. 0 3
If n=3
I need to print:
1. -3 0 0
2. 3 0 0
3. 0 -3 0
4. 0 3 0
5. 0 0 -3
6. 0 0 3
If n=4, I need to print:
1. -3 0 0 0
2. 3 0 0 0
3. 0 -3 0 0
4. 0 3 0 0
5. 0 0 -3 0
6. 0 0 3 0
7. 0 0 0 -3
8. 0 0 0 3
The key thing you need here is format. It's great for producing output in fixed-width text form. Now, writing the format-items for a format string is an art (very closely related to doing the same for sprintf() in C) that quite a few people lack nowadays, but something like this is what you want were you just doing the n == 4 case:
puts [format "%d. %2d %2d %2d" $count $c1 $c2 $c3]
In this case, however, you've got a more complex problem because you have a variable number of fields. That makes things trickier; you're probably best building things up piecemeal with the help of a procedure to do the formatting of a single line:
proc generateLine {n i} {
set line [format "%d." $i]
for {set x 1} {$x <= $n} {incr x} {
# Double-ternary conditional operator
set v [expr {$i == $x*2-1 ? -3 : $i == $x*2 ? 3 : 0}]
append line [format " %2d" $v]
}
return $line
}
Now that we have that, the rest of the program is simple enough:
# Assume that the n variable has been set already
for {set i 1} {$i <= $n*2} {incr i} {
puts [generateLine $n $i]
}
Very often in programming, it's easiest if you split a program into several pieces with sensible boundaries between. Knowing where to split is something that you get better at with experience, but very often the split is in the right place if you can give a sensible name to the split out piece. For example, above I saw that I wanted to do some work for each line and some work to iterate over all the lines needed; that was the obvious place to break things apart and make a procedure, generateLine. The outer part is also quite nameable (perhaps generateListOfLines?) but that isn't so important here.
TCL Script:
set a 10
while {$a < 1} {
puts $a
incr a
}
Expected output:
10
9
8
7
6
5
4
3
2
1
I am trying to print numbers from 10 to 1. But its not working (It prints nothing).
Is there a way to "decrement" the variable value (decr a)?
Thanks,
Kumar
Change the condition to $a > 1 and to decrement the value, you have to use incr a -1. Here we gave the step as -1.
same can be done by for loop also
for {set i 10} {$i > 1} {incr i -1} {
puts $i
}
I think your loop body is never executed because the condition yields false the very first time. You probably wanted to write ">" instead of "<".
I'm new to expect scripts, so please forgive my stumbling...
Below is the meat of my expect script. The intent is to scroll thru several screens of output, after each of which the user is prompted with "Continue? [y/n]".
Finally, when there are no more screens, a "% " prompt is displayed, which SHOULD cause execution to fall out of the while loop.
set more_screens 1
while {$more_screens > 0} {
sleep 10
expect {
"\[y/n]" { send "y\r"}
"% " { set more_screens 0 }
}
}
What happens instead is... it stays in the while loop forever, sending "y" over and over and over again. I have set "exp_internal 1", and from that output it "seems" like the expect keeps re-reading text that it has already matched on, and so keeps seeing "[y/n]", and keeps sending "y" when, in fact, there are only 2 screens of output, and thus only 2 "Continue? [y/n]" prompts.
(The sleep statement is probably not necessary - i just added it to maybe solve the problem - it did NOT - and to allow me to digest the debug output better.)
Bottom line... Are there any obvious blunders in my code? I'll take any suggestions at improving this and eliminating the endless looping.
EDIT BELOW added to this question, after James made a helpful suggestion.
Thanks James for your quick response, and your helpful suggestion! But...
The same problem persists with your approach (although, yours IS much more elegant, and I'll add this to my expect tool kit.)
The problem sure seems to be, as noted initially, each execution of the expect statement RE-READS TEXT THAT WAS ALREADY READ AND COMPARED. Below is output when I execute the "exp_continue" code from James, and I set "exp_internal 1" to get debug output on my screen...
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>expect: does "get dump tables\r\n\r% get dump tables\n\r\r\nIfn TableName Configured >MaxUse InUse LastDropTime\r\n3 cdm_app 100002 190 33 >\r\n3 cdm_conv 2000002 675180 4813 \r\n3 cdm_pdisc 250002 >250002 1304 01-24-2014-19:14:59\r\n3 cdm_kpi 100001 141 25 >\r\n3 cdm_qoe 500003 204918 1578 \r\n3 cdm_qoe_hd 2500003 >582993 1578 \r\n3 cdm_kpi_error_app 100001 5 2 \r\n3 >cdm_kpi_error 100001 7 2 \r\n3 asr_cache 1000000 >1000000 999995 \r\n3 asr_sess 2000000 62670 29748 \r\n3 >asr_conn 3000000 64428 31147 \r\n3 asr_sess_keys 1500000 >1015269 1009049 \r\n3 asr_conn_opts 6000000 0 0 \r\n3 >asr_events 4000000 5239 144 \r\n3 skt_table 2000000 >2000000 2000000 \r\n3 skt_trans 1000000 408020 254674 \r\n3 >ses_sip_db 5000 0 0 \r\n3 ses_gtp_mob_txn 5000 >0 0 \r\nContinue? [y/n]: " (spawn_id exp6) match glob pattern "[y/n]"? yes
>expect: set expect_out(0,string) "n"
>expect: set expect_out(spawn_id) "exp6"
>expect: set expect_out(buffer) "get dump tables\r\n\r% get dump tables\n\r\r\nIfn"
>send: sending "y\r" to { exp6 }
>expect: continuing expect
>
>
>expect: does " TableName Configured MaxUse InUse LastDropTime\r\n3 >cdm_app 100002 190 33 \r\n3 cdm_conv 2000002 >675180 4813 \r\n3 cdm_pdisc 250002 250002 1304 01-24-2014->\r\n3 cdm_kpi 100001 141 25 \r\n3 cdm_qoe 500003 >204918 1578 \r\n3 cdm_qoe_hd 2500003 582993 1578 \r\n3 >cdm_kpi_error_app 100001 5 2 \r\n3 cdm_kpi_error 100001 >7 2 \r\n3 asr_cache 1000000 1000000 999995 \r\n3 >asr_sess 2000000 62670 29748 \r\n3 asr_conn 3000000 >64428 31147 \r\n3 asr_sess_keys 1500000 1015269 1009049 \r\n3 >asr_conn_opts 6000000 0 0 \r\n3 asr_events 4000000 >5239 144 \r\n3 skt_table 2000000 2000000 2000000 \r\n3 >skt_trans 1000000 408020 254674 \r\n3 ses_sip_db 5000 >0 0 \r\n3 ses_gtp_mob_txn 5000 0 0 \r\nContinue?
>[y/n]: " (spawn_id exp6) match glob pattern "[y/n]"? yes
>expect: set expect_out(0,string) "n"
>expect: set expect_out(spawn_id) "exp6"
>expect: set expect_out(buffer) " TableName Con"
>send: sending "y\r" to { exp6 }
>^Csighandler: handling signal(2)
>async event handler: Tcl_Eval(exit 130)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
After the expect matches on "[y/n]" the first time, it then does a "expect: continuing expect" (middle of the above text output), then the block of text it reads next is, except for the first few words, THE SAME TEXT BLOCK IT ALREADY READ AND COMPARED.
Am I missing anything??? This has to be a problem if the expect statements re-reads already processed output, yeah? (I have looked at the actual output sent by the target system, and it does NOT send out the same text block a second time.)
Again, I'm new at expect scripts, but I can't see any other explanation for what the debug output above shows. (And, I apologize for having trouble formatting the output correctly - I really am trying!)
Thanxks to anyone who has the patience to read all of the above, and perhaps has an explanation or suggestion.
You need to be rescued by the exp_continue command. :-)
What that command does when encountered is stay within the expect block and try to match again, with whatever new input may come.
So you can really shorten your above code to be:
expect {
"\[y/n]" {
send "y\r"
exp_continue
}
"% " {
# Do whatever is needed here, after which program flow will continue *outside* of the expect block
}
}
Let me know if that works out for you!
EDIT - based on #feenyman99 additional info:
Ok, I see what it is. You have the wrong pattern. By using "[y/n]", a match is being produced with a single 'n' character. There's your matched string:
expect: set expect_out(0,string) "n"
expect_out(0,string) holds the matched pattern. expect_out(buffer) holds the portion of input removed from the buffer, which holds all input up to and including the matched pattern (from then on, the next expect operation will look for a match on input after the last matched pattern). As you can see, it holds the input up to and including the first literal 'n' character that is found (newlines don't count):
expect: set expect_out(buffer) "get dump tables\r\n\r% get dump tables\n\r\r\nIfn"
So what is happening is that your script is sending the "y\r" way before the yes/no prompt is presented. And, although I'm not seeing the rest of the logs, I'm guessing the next match happens shortly after, upon hitting the next 'n' character.
So you need to change your pattern matching statement to be able to match the yes/no prompt. Better make that a regexp match (-re). I tested the following, and it works (tested on Tcl 8.4.13):
expect {
-re "\\\[y/n]" { send "y\r"}
The multiple backslashes are because the backslash is also an escape character within the pattern matcher. Kind of tricky, but sometimes they are needed.
Let me know how that goes. You should be all set now.
PS: This may come in handy: http://www.tcl.tk/doc/howto/regexp81.tml
I want to "encrypt" a file. This is just for fun, not intending to store or send any sensetive data using this "encryption".
This code example is meant to ilistrate what I would like to do...
SET A=D
SET B=S
SET C=Q
SET D=G
ECHO %A%%B%%C%%D%
The text ABCD will now be displayed as DSQG insted (if I wrote something meaningfull the "encrypted" result would not mean anything).
My question is:
Can I (if so, how?) add '%' before and after every character in the file?
I searched on how to read a file using batch, found this (jeb's answer):
Batch files: How to read a file?
Is there a soulution where I could read a normal file, encrypt it and store as an encrypted version aswell?
Thanks so much for any answer!
This borrows strlen from jeb,
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET PT_FILE=plain.txt
IF NOT "%~1"=="" SET "PT_FILE=%~1"
CALL :INIT_CIPHER
FOR /F "tokens=*" %%l IN ('findstr.exe /R /N "^" "%PT_FILE%"') DO #(
REM #ECHO(
REM #ECHO( %%l
SET "LINE=%%~l"
SET "LINE=!LINE:*:=!"
REM #ECHO(!LINE!
CALL :strlen LINE_LEN LINE
REM #ECHO(Length(!LINE_LEN!^)
IF !LINE_LEN! EQU 0 (
ECHO(
) ELSE (
SET OUTLINE_E=
FOR /L %%i IN (0,1,!LINE_LEN!) DO (
SET "CHAR=!LINE:~%%i,1!"
IF "!CHAR!"==" " (
SET "OUTLINE_E=!OUTLINE_E! "
) ELSE (
#ECHO !CHAR!|findstr.exe /R "[A-Za-z]" >NUL
IF ERRORLEVEL 1 (
SET "OUTLINE_E=!OUTLINE_E!!CHAR!"
) ELSE (
SET CHAR_E=
CALL :ENC "!CHAR!" "CHAR_E"
REM #ECHO '!CHAR!' =^> E(!CHAR_E!^)
SET "OUTLINE_E=!OUTLINE_E!!CHAR_E!"
)
)
)
ECHO(!OUTLINE_E!
)
)
GOTO :EOF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:ENC
CALL SET "%~2=!%~1!"
REM ECHO E(!%~2!^)
EXIT /B
:: https://stackoverflow.com/questions/5837418/how-do-you-get-the-string-length-in-a-batch-file
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
:: Substitution Cipher
:: ABCDEFGHIJKLMNOPQRSTUVWXYZ
:: ZYXWVUTSRQPONMLKJIHGFEDCBA
:: perl -e "#a=('a'..'z');for ($i=0; $i<#a; $i++) { print('SET '.uc($a[$i]).'='.uc($a[$#a-$i]).qq(\n)); }"
:INIT_CIPHER
SET A=Z
SET B=Y
SET C=X
SET D=W
SET E=V
SET F=U
SET G=T
SET H=S
SET I=R
SET J=Q
SET K=P
SET L=O
SET M=N
SET N=M
SET O=L
SET P=K
SET Q=J
SET R=I
SET S=H
SET T=G
SET U=F
SET V=E
SET W=D
SET X=C
SET Y=B
SET Z=A
EXIT /B
Notes:
It is very slow.
It loses the case of the source plaintext.
It doesn't insert % around every character (which wouldn't work for special characters anyway), but does what it sounds like you want.
Results:
>>> type plain.txt
This is a simple file to be encrypted.
This is a second line. A blank line follows.
This is the fourth line.
This line has a colon (:) in the middle of it.
This line has quotes: "I said, 'Pass the bread, please,' as politely as possible."
>>> enc plain.txt
GSRH RH Z HRNKOV UROV GL YV VMXIBKGVW.
GSRH RH Z HVXLMW ORMV. Z YOZMP ORMV ULOOLDH.
GSRH RH GSV ULFIGS ORMV.
GSRH ORMV SZH Z XLOLM (:) RM GSV NRWWOV LU RG.
GSRH ORMV SZH JFLGVH: "R HZRW, 'KZHH GSV YIVZW, KOVZHV,' ZH KLORGVOB ZH KLHHRYOV."
>>> enc > cipher.txt
enc > cipher.txt
>>> enc cipher.txt
THIS IS A SIMPLE FILE TO BE ENCRYPTED.
THIS IS A SECOND LINE. A BLANK LINE FOLLOWS.
THIS IS THE FOURTH LINE.
THIS LINE HAS A COLON (:) IN THE MIDDLE OF IT.
THIS LINE HAS QUOTES: "I SAID, 'PASS THE BREAD, PLEASE,' AS POLITELY AS POSSIBLE."
I'm running into a dilemma. I want to send a preemptive email to users telling them that an upcoming task is about to happen. In order to do that I'm defining some variables, sending an email via blat, sleeping the batch for 5 minutes, then executing the rest of the script.
When executing %time% at 4:00PM, I get 16:00:00.00. If I add 5 minutes to it, only for display purposes in the email with the following code:
#echo on
SET /a timeminute = 00 + 5 << --- test code
::SET /a timeminute = %time:~3,2% + 5 << --- actual code in GoLive
IF %timeminute% LEQ 9 (
GOTO :resetTime
) ELSE (
GOTO :end
)
:resetTime
SET timeminute = "0%timeminute%"
:end
echo %timeminute%
pause
I get 5, not 05 like expected. Using arithmetic on time drops leading zeros, so I try to add it back in later but the later SET is within the IF statement and cannot be seen? How can I see that? Is there a such thing as an environment variable in batch?
Keep in mind this issue only happens within the first 9 minute of the hour, after that time, there are no more leading zeros.
Bonus: What happens when the minutes in a hour is 55-59? In my example, it will be 60-64, so I need a way of rounding up an hour and take care of the remaining minutes. Right now, I see that as a bug, but I do not foresee this script running at those odd times. But if it is an easy fix please let me know as I have not even tried to tackle that problem.
Thank you kindly
A more compact form to do the same thing is this:
#echo on
for /F "tokens=1-3 delims=:." %%a in ("%time%") do (
set timeHour=%%a
set timeMinute=%%b
set timeSeconds=%%c
)
rem Convert HH:MM to minutes + 5
set /A newTime=timeHour*60 + timeMinute + 5
rem Convert new time back to HH:MM
set /A timeHour=newTime/60, timeMinute=newTime%%60
rem Adjust new hour and minute
if %timeHour% gtr 23 set timeHour=0
if %timeHour% lss 10 set timeHour=0%timeHour%
if %timeMinute% lss 10 set timeMinute=0%timeMinute%
echo %timeHour%:%timeMinute%:%timeSeconds%
pause
Answered my own question with the following:
#echo on
setlocal enabledelayedexpansion
set timehour=%time:~0,2%
set timeminute=%time:~3,2%
set timeseconds=%time:~6,2%
set addTime=5
IF %timeminute:~0,1% lss 1 set timeminute=!timeminute:~1,1!
IF %timeminute:~0,1% lss 1 set timeminute=!timeminute:~1,1!
set /a timeminute=%timeminute% + %addTime%
IF %timeminute% lss 10 set timeminute=0!timeminute!
IF %timeminute% equ 60 (
set timeminute=00
set /a timehour=%timehour% + 1
)
IF %timeminute% equ 61 (
set timeminute=01
set /a timehour=%timehour% + 1
)
IF %timeminute% equ 62 (
set timeminute=02
set /a timehour=%timehour% + 1
)
IF %timeminute% equ 63 (
set timeminute=03
set /a timehour=%timehour% + 1
)
IF %timeminute% equ 64 (
set timeminute=04
set /a timehour=%timehour% + 1
)
IF %timehour% equ 25 (
set timehour=00
)
IF %timehour% lss 10 set timehour=0!timehour!
echo %timehour%:%timeminute%:%timeseconds%
pause