Lua - popen 2x '&&' chained set /p - stdout takes a vacation - windows

I'm working in an environment that has approximately zero functional ui while a lua script is running. This just isn't acceptable for the script I need to write, which relies heavily on user input. The only way I've found to circumvent this is using io.popen with some rather crafty commands.
I recognize that what I'm trying to do here is strange and very much wrong, and that I've brought this upon myself, but I can't figure out what's going wrong in this code snippet:
local a = 'f'
local p = io.popen(
'echo -~`~-,_,- Editing '..(a == 'f' and 'foreground' or 'background')..' -~`~-,_,- > con && '.. --display text to the user
'set /p f= Find block: > con < con && '.. --user input
'call echo %f% &&' .. --pass f back to the lua script
'set /p r= Replace with: > con < con && '.. --user input
'call echo %r% &&' .. --pass r back to the lua script
'pause < con', "r")
local f = p:read("*a") --read what was passed back, later parse it back into 2 variables
p:close()
What I expect to happen:
A 'command prompt window' is displayed to the user, asking for input.
The user enters 2 values.
The values are echoed back to the lua script as they are entered.
The values are read from the pipe and stored for later use.
The command line waits for a keypress, and then closes.
What actually happens:
A 'command prompt window' is displayed to the user, asking for input.
The user enters a value for f.
f is echoed back to the lua script.
The user enters a value for r.
r is echoed back to the console. (!!!)
f is read from the pipe. r is not present.
The command line waits for a keypress, and then closes.
This very similar code sample works just fine, but only returns 1 variable:
p = io.popen(
'echo What do you want to do? > con && '..
'echo G: remove girders > con && '..
'echo F: swap foreground > con && '..
'echo B: swap background > con && '..
'echo U: undo all edits > con && '..
'echo C: cancel > con && '..
'set /p a= Choose an option: > con < con && '..
'call echo %a%', "r")
a = string.lower(p:read("*a"):gsub("\n",""))
p:close()
What am I doing wrong, and how can I rewrite this to work for my purposes?
What in the world have I unleashed, and how do I put the genie back into the bottle?

After a good while googling, I found this:
(
echo some output
echo more output
)>"Your logfile
Redirecting Output from within Batch file
I had no clue you could wrap commands like that, and I've been tinkering with the Windows CLI since I first discovered it.
local a = 'f'
local p = io.popen(
'echo -~`~-,_,- Editing '..(a == 'f' and 'foreground' or 'background')..' -~`~-,_,- > con && '.. --display text to the user
'( set /p f= Find block: && '.. --user input
'set /p r= Replace with: ) > con < con && '.. --user input
'call echo %f% &&' .. --pass f back to the lua script
'call echo %r% &&' .. --pass r back to the lua script
'pause < con > con', "r")
local f = p:read("*a") --read what was passed back, later parse it back into 2 variables
p:close()
Wrapping the two set /p statements as above works - I get the expected output of f, followed by a newline, and then r, all sent back to the lua script, where they belong.
Still, if anyone can clue me in on why this was a problem in the first place, I would be very much interested in the explanation.

local p = io.popen(
-- create temporary .bat-file
'set tmpf="%TEMP%\\TMP%RANDOM%.BAT" &&'..
'cmd /c"(echo #set p=^%1&echo #set /p x= ^%p:~1,-1^%^>con&echo #call echo ^%x^%)>%tmpf%" &&'..
-- Your main program
'echo. -~`~-,_,- Editing '..(a == 'f' and 'foreground' or 'background')..' -~`~-,_,- > con &&'.. --display text to the user
'call %tmpf% "Find block:" < con &&'.. -- pass f back to the lua script
'call %tmpf% "Replace with:" < con &&'..-- pass r back to the lua script
-- delete temporary .bat-file
'call del %tmpf% > con &&'..
'pause < con', "r")
local f = p:read'*a'
p:close()

Related

Retry a command only once : when a command fails (in bash)

for ( i=3; i<5; i++)
do
execute some command 1
if command 2 is successful then do not run the command 1 (the for loop should continue)
if command 2 is not successful then run command 1 only once (like retry command 1 only once, after this the for loop should continue)
done
This is to note that command 2 is dependent on command 1 and command 2 can only be executed after command 1
for example:
for ( i=3; i<5; i++)
do
echo "i" >> mytext.txt ---> command 1
if "check the content of mytext.txt file to see if the value of i is actually added" ---> command 2
if it is not added then execute echo "i" >> mytext.txt (command 1) again and only once.
if i value is added to the file .. then exit and continue the loop
done
Since the "command 1" is quite big and not just an example echo statement here.I do not want to add "command 1" twice .. once outside and once inside the if condition. I want this logic in an optimized way with no redundancy of code.
Per a comment it sounds like the OP may need to invoke command 1 up to 2 times for a given $i value, but only wants to type command 1 once in the script.
Siddhartha's suggestion to use a function is probably good enough but depending on the actual command 1 (OP mentions that it's 'quite big') I'm going to play devil's advocate and assume there could be additional issues with passing some args to the function (eg, a need to escape some characters ... ??).
The general idea is to have an internal loop that can be executed at most 2 times, with logic in the loop that will allow for an 'early' exit (eg, after just one pass through the loop).
Since we're using pseudo-code I'll use the same ...
for ( i=3; i<5; i++ )
do
pass=1 # reset internal loop counter
while ( pass -le 2 )
do
echo "i" >> mytext.txt # command 1
if ( pass -eq 1 ) # after first 'command 1' execution
&& ( value of 'i' is in mytext.txt ) # command 2
then
break # break out of inner loop; alternatively ...
# pass=10 # ensure pass >= 2 to force loop to exit on this pass
fi
pass=pass+1 # on 1st pass set pass=2 => allows another pass through loop
# on 2nd pass set pass=3 => will force loop to exit
done
done
you can declare functions like
function command
{
your_command -f params
}
for ( i=3; i<5; i++)
do
if command ; then
echo "success"
else
echo "retry"
command
fi
done

ControlSend randomly sending wrong characters (modified and unmodified) when using SetKeyDelay, 0, 0

I'm self-answering this question because I've seen it asked all over the Internet, but with few helpful answers, and definitely no resolutions on Stack Overflow that I can find.
Example Code
Consider this code, which simply writes several lines of shell commands:
^0::
SetKeyDelay, 0, 0
myWindow = ahk_exe Notepad.exe
ControlSend, , set c=".cshrc-andrew.cheong"`n, %myWindow%
ControlSend, , set v=".vimrc-andrew.cheong"`n, %myWindow%
ControlSend, , foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER )`n, %myWindow%
ControlSend, , if ( -e $d/$c ) source $d/$c`n, %myWindow%
ControlSend, , if ( -e $d/$v ) alias vim "vim -N -u $d/$v"`n, %myWindow%
ControlSend, , end`n, %myWindow%
Return
I'm writing the commands to Notepad to show that it is not an issue limited to terminal applications like PuTTy or xterm. It's easy to think so, since these applications sometimes have laggy behavior.
Example Output
Specifically when using SetKeyDelay, 0, 0 for fast "typing," I get all kinds of weird behavior, like:
Double-quotes replaced by single-quotes. Parentheses replaced by 9's and 0's. Dollar signs replaced by 4's.
set c=".cshrc-andrew.cheong'
set v=".vimrc-andrew.cheong"
foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER )
if 9 -e 4d/4c 0 source 4d/4c
if ( -e $d/$v ) alias vim 'vim -n -u 4d/4v'
end
Dashes replaced by underscores. Case changes.
set c='.cshrc-andrew.cheong"
set v='.vimrc-andrew.cheong'
foreach d ( /userhome/andrew.cheong /home/4user /data/$USER )
if 9 -e 4d/$C ) source 4d/$c
if 9 _e $d/$v ) alias vim 'vim -N -u $d/$v"
end
Periods replaced by >'s. More case changes.
set c=".cshrc-andrew.cheong"
set v=">VIMrc-andrew.cheong"
foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER )
if 9 -e $d/$c ) source 4d/$c
if ( -e $d/$V ) alias vim "vim -N -u $d/$v"
end
It's obvious the issue has something to do with the Shift modifier, as if it's being randomly turned on or off.
Why is this happening, and how do we fix it?
Note that there are no problems when using Send and its variants. The issue specifically arises with ControlSend, which is needed to send inputs to specific controls or to an unfocused window.
Solution
Copy this into your script (from user RHCP at AutoHotkey forums):
pSendChars(string, control := "", WinTitle := "", WinText := "", ExcludeTitle := "", ExcludeText := "")
{
for k, char in StrSplit(string)
postmessage, WM_CHAR := 0x102, Asc(char),, %Control%, %WinTitle%, %WinText%, %ExcludeTitle%, %ExcludeText%
return
}
And use it like this:
^0::
myWindow = ahk_exe Notepad.exe
line .= "set c="".cshrc-acheong""`n"
line .= "set v="".vimrc-acheong""`n"
line .= "foreach d ( /userhome/andrew.cheong /home/cama /home/$USER )`n"
line .= " if ( -e $d/$c ) source $d/$c`n"
line .= " if ( -e $d/$v ) alias vim ""vim -N -u $d/$v""`n"
line .= "end`n"
pSendChars(line, "edit1", myWindow)
Return
That's it.
Note that edit1 is Notepad's name for its text control. When using this script for PuTTy, I changed it to a blank string. Use AutoHotkey's WindowSpy program to find out the control you wish to write to.
Why
This has come up a few times before. Just to add to what Lexikos
wrote, the issue is due to the fact that controlSend uses both post
message and sendInput/sendEvent to send keystrokes. This is required
as some programs will not correctly interpret the keystrokes unless
the sent modifier keys are logically down (sent via sendInput/Event).
Modifier keys (unless explicitly stated e.g. {shitft down}) are sent
via sendInput while non-modifers are sent via postMessage. Keys sent
via postmessage are sent directly to the window and so have less delay
than the other keys/messages. Consequently it's possible for keystroke
messages to arrive out of synch resulting in unexpected characters. In
your case the capitalised letters require the shift key to be sent via
sendInput/Event.
In addition to using keyDelays, you can try controlSetText and posting
WM_Char messages. If you're working with a text control i would
recommend using controlSetText.
- RHCP on 30 Sep 2013

Executing two commands in VBscript

I'm trying to write a VBScript to execute a two commands. The script is as follows:
Set objShell = CreateObject("WScript.Shell")
Set X = objShell.Exec("opcdeploy -cmd ""dbspicao -m 0703 -r 1"" -node fssflx24.fss.india")
Set Y = objShell.Exec("opcmsg a=a o=o msg_text=X severity=Normal node=fssflx24.fss.india")
strIpConfig = objScriptExec.StdOut.ReadAll
WScript.Echo strIpConfig
What I want is when the first command "X" gets executed, it's output shall used as the msg_text in the second "Y" command.
But it is not happening as when the second command gets executed it captures not the output but only word "Y".
What I'm missing.
Kindly assist.
BR,
Ramesh
Well there are a couple of problems.
First X is set to the Execobject not the output of the exec.
Second in the Y Exec you have just a string. To use a variable in a string you have to end the string after msg_text= with a " and then append the variable X and append finally the rest of the string. So the code would look like:
Set objShell = CreateObject("WScript.Shell")
Set execo = objShell.Exec("opcdeploy -cmd ""dbspicao -m 0703 -r 1"" -node fssflx24.fss.india")
X = execo.StdOut.ReadAll
Set Y = objShell.Exec("opcmsg a=a o=o msg_text=" & X &" severity=Normal node=fssflx24.fss.india")
Finally I cannot tell from your code what objScriptExec is supposed to be. It seems to be defined somewhere else so I don't know for sure how it is related...
My first command
opcdeploy -cmd ""dbspicao -m 0703 -r 1"" -node fssflx24.fss.india
it gives following alert.
Report For Database mpaydb1
Wed Mar 11 13:57:28 IST 2015
Metric UDM 0709 (Report 1)
RESOURCE_NAME CURRENT_UTILIZATION MAX_UTILIZATION LIMIT_VALU
processes 118 359 500
sessions 124 366 784
transactions 0 6 UNLIMITED
Second Command shall pick this output and generate alert using second command.
opcmsg a=a o=o msg_text=" & X &" severity=Normal node=fssflx24.fss.india
That is how it should work.
BR,
Ramesh

set var in Csh script

I'm creating a script like this
#!/bin/csh
set h=1;
while [h=1] do echo "hi"; h=2; done;
but when I execute it a get this:
===> message after : csh test.sh [h=1]: No match.
Try:
set h = 1
while ( $h == 1 )
echo "hi"
set h = 2
end
You seem to be trying to mix Bourne shell syntax into your C shell script.
Csh is generally a lousy language for scripting, try to avoid it if at all possible
http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/
UPDATE:
The csh equivalent to read h is:
set h = $<

Putting the output of a command with interaction inside a variable while using grep in bash

This program I use has it's own variables to set when you run it, so I want to set those variables and then greping the output then storing it inside a variable. However, I don't know how to go about this the correct way. The idea I have doesn't work. The focus is on lines 7 through 14.
1 #!/usr/local/bin/bash
2 source /home/gempak/NAWIPS/Gemenviron.profile
3 FILENAME="$(date -u '+%Y%m%d')_sao.gem"
4 SFFILE="$GEMDATA/surface/$FILENAME"
5 echo -n "Enter the station ID: "
6 read -e STATION
7 OUTPUT=$(sflist << EOF
8 SFFILE = $SFFILE
9 AREA = #$STATION
10 DATTIM = all
11 SFPARM = TMPF;DWPF
12 run
13 exit
14 EOF)
15 echo $OUTPUT
But I get this:
./listweather: line 7: unexpected EOF while looking for matching `)'
./listweather: line 16: syntax error: unexpected end of file
Putting together everyone's answers, I came across a working solution myself. This code works for me:
#!/usr/local/bin/bash
source /home/gempak/NAWIPS/Gemenviron.profile
FILENAME="$(date -u '+%Y%m%d')_sao.gem"
SFFILE="$GEMDATA/surface/$FILENAME"
echo -n "Enter the station ID: "
read -e STATION
OUTPUT=$(sflist << EOF
SFFILE = $SFFILE
AREA = #$STATION
DATTIM = ALL
SFPARM = TMPF;DWPF
run
exit
EOF
)
echo $OUTPUT | grep $STATION
Thanks everyone!
I'd put your program to run in a separate .sh script file, and then run the script from your first file, passing the arguments you want to pass as command line arguments. That way you can test them separately.
You could also do it in a function, but I like the modularity of the second script. I don't udnerstand exactly what you are trying to do above, but something like:
runsflist.sh:
#!/bin/bash
FILENAME="$(date -u '+%Y%m%d')_sao.gem"
SFFILE="$GEMDATA/surface/$FILENAME"
AREA = #$STATION
DATTIM = all
SFPARM = TMPF;DWPF
grep $STATION | sflist
main.sh:
#!/bin/bash
echo -n "Enter the station ID: "
read -e STATION
OUTPUT=`runsflist.sh`
echo $OUTPUT
If sflist needs interaction, I'd try something like this:
SFFILE=$(
( echo SFFILE = "$SFFILE"
echo AREA = "#$STATION"
echo DATTIM = all
echo SFPARM = TMPF;DWPF
echo run
cat
) | sflist)
Unfortunately, you have to type exit as part of the interaction.

Resources