Expect script not matching output - expect

At my wits end:
Need expect to match the following string, which is also colour coded. Quotes are the illustrate the blank space to the right.
"FuseMQ:karaf#root> "
Using just "root" sends before the > prompt is ready, in some cases, not all.
expect "root" {
0m:\u001b[36mkaraf\u001b[0m\u001b[1m#\u001b[0m\u001b[34mroot"
send: sending "osgi:install wrap:file:/apps/fuse/fuse-mq-7.1.0.fuse-047/
lib/ojdbc6-11.2.0.2.0.jar\r" to { exp4 }
I cannot find a regexp that works. The output from expect -d is as follows when it works
u001b[0m\u001b[1m#\u001b[0m\u001b[34mroot\u001b[0m> " (spawn_id
exp4) match glob pattern "root"? yes
But I cannot figure out how to match this.

Try this expect "root\\u001b\[0m> $"

You could just do something like this, where you have the strings you want to match, and allow for "stuff" in between.
set prompt {FuseMQ.*:.*karaf.*#.*root.*> $}
expect -re $prompt

Related

expect doesn't match pattern correctly

I tried to automate the easyrsa request generation via expect. I came up with that bash script:
#!/bin/bash
firstname=$1
lastname=$2
mail=$3
department=$4
password=$5
[...]
cd /VPN-CA/
/usr/bin/expect -c "
spawn ./easyrsa gen-req $mail
expect \"Enter PEM pass phrase:\"
send \"$password\r\"
expect \"Verifying - Enter PEM pass phrase:\"
send \"$password\r\"
expect \"Country Name (2 letter code) \[DE\]:\"
send \"\r\"
expect \"State or Province Name (full name) \[MyState\]:\"
send \"\r\"
expect \"Locality Name (eg, city) \[MyCity\]:\"
send \"\r\"
expect \"Organization Name (eg, company) \[MyOrganization\]:\"
send \"\r\"
expect \"Organizational Unit Name (eg, section) \[MyDepartment\]:\"
send \"$department\r\"
expect \"Common Name (eg: your user, host, or server name) \[$mail\]:\"
send \"$firstname $lastname\r\"
expect \"Email Address \[email#address.de\]:\"
send \"$mail\r\"
expect eof
"
# do somethin else
[...]
exit 0
The script works and the request will be generated correctly, but expect is very slow. With -d it shows me for every expected pattern after the second password query something like that:
expect: does "test\r\n\r\n-----\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\n" (spawn_id exp3) match glob pattern "Country Name (2 letter code) [DE]:"? no
Country Name (2 letter code) [DE]:
expect: does "test\r\n\r\n-----\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [DE]:" (spawn_id exp3) match glob pattern "Country Name (2 letter code) [DE]:"? no
expect: timed out
send: sending "\r" to { exp3 }
I don't understand why the pattern doesn't match. I mean whilst the reply is send nevertheless after the timeout it works, but it is slow as hell... (and it is crap)
Has anybody an explanation or a solution for this problem?
The problem in your code is that the shell removes the backslashes while constructing the string, so expect sees un-escaped brackets -- a command substitution. In your code, you'll have to double backslash the opening brackets.
The same problem happens here:
expect -c "
...
send \"$password\r\"
Suppose $password is "1234", then expect will see:
send "1234r" ;# <= no carriage return!
Using a double quoted string to hold the expect code leads very quickly to quoting hell. Use a here-doc instead:
/usr/bin/expect <<END_EXPECT
spawn ./easyrsa gen-req $mail
expect "Enter PEM pass phrase:"
send "$password\r"
expect "Verifying - Enter PEM pass phrase:"
send "$password\r"
expect "Country Name (2 letter code) \[DE\]:"
send "\r"
expect "State or Province Name (full name) \[MyState\]:"
send "\r"
expect "Locality Name (eg, city) \[MyCity\]:"
send "\r"
expect "Organization Name (eg, company) \[MyOrganization\]:"
send "\r"
expect "Organizational Unit Name (eg, section) \[MyDepartment\]:"
send "$department\r"
expect "Common Name (eg: your user, host, or server name) \[$mail\]:"
send "$firstname $lastname\r"
expect "Email Address \[email#address.de\]:"
send "$mail\r"
expect eof
END_EXPECT
If you use braces (expect's single quoting mechanism) you don't have to escape the brackets:
expect {Email Address [email#address.de]:}
Just to troubleshoot further, could you just check does the system is slow in general or only while running the expect command.
Sometimes because of delay in DNS name resolution also the system response slowly.
Check and remove any unnecessary entries in the /etc/resolve.conf and then try.
It seems to be a problem of the escaping of \[, the closing square bracket doesn't need to be escaped.
This fails to detect the line.
expect \"Country Name (2 letter code) \[DE\]:\"
But you can replace it with a wildcard ?
expect \"Country Name (2 letter code) ?DE]:\"
Or you could escape the opening square bracket with only 6 backslashes:
expect \"Country Name (2 letter code) \\\\\\[DE]:\"

If string does NOT contain and REGEX

I'm looking for a way to write the following javascript code in applescript: If the condition is false then I want to do something.
var regEx = /\d{5}/g;
var str = 'This string contains 12345';
if (!regEx.test(str)){
do something
}
Below is the applescript I started but it doesn't work.
set str to 'This string contains 12345'
set regEx to <NOT SURE HOW APPLESCRIPT HANDLES THIS>
if string does contains "12345" then
do something
end if
In Javascript ! = does not. What is the equivalent in applescript? and how do I handle RegEx?
My overall goal is to find out if the finder window selected DOES NOT contain any 5 digit number combination in the folder name.
tl;dr For any version of macOS that is >= OSX 10.8 you'll need to replace grep's -P option (as indicated in the "Solution" section below) with the -E option - as mentioned in the "Different grep utilities" section at the bottom of this post.
As correctly noted in the comments...
Vanilla AppleScript can't handle regex. - vadian
so you'll need to
shell out to something that does know regex - red_menace
Solution:
To meet your requirement with vanilla AppleScript in a way which is analogous to JavaScript's test() method, consider utilizing a custom AppleScript subroutine as follows:
Subroutine:
on regExpTest(str, re)
set statusCode to do shell script "grep -q -P " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
if statusCode is equal to "0" then
return true
else
return false
end if
end regExpTest
Usage:
set regExp to "\\d{5}"
set str to "This string contains 12345"
if regExpTest(str, regExp) then
display dialog "It DOES match so let's do something"
end if
Running the above script will display a dialog with the given message because there is a match between the regular expression and the specified string.
Note: AppleScript strings use the backslash as an escape character, so you'll notice that the \d metacharacter has been further escaped with an additional backslash, i.e. \\d
Inequality operators:
In Javascript != does not. What is the equivalent in applescript? and how do I handle RegEx?
AppleScript's inequality operators that are analogous to JavaScripts inequality operator (!=) are:
≠
is not
isn't
isn't equal [to]
is not equal [to]
doesn't equal
does not equal
So given your JavaScript if statement:
if (!regEx.test(str)){
// do something
}
We can achieve the same logic, (again using the aforementioned custom regExpTest subroutine), with the following code:
set regExp to "\\d{5}"
set str to "This string contains 1234"
if regExpTest(str, regExp) ≠ true then
display dialog "It DOES NOT match so let's do something"
end if
Note The str value only includes four consecutive digits, i.e. 1234.
This time running the above script will display a dialog with the given message because there is NOT a match between the regular expression and the specified string.
There are many variations that can be made to the aforementioned AppleScript if statement to acheieve the same desired logic. For example;
if regExpTest(str, regExp) is not equal to true then
...
end if
if regExpTest(str, regExp) = false then
...
end if
etc...
regExpTest subroutine explanation:
The aforementioned regExpTest AppleScript subroutine is essentially utilizing the do shell script command to run the following code that you would run directly via your macOS Terminal application. For instance in your Terminal application run the following two commands:
grep -q -P "\d{5}" <<<"This string contains 12345" 2>/dev/null; echo $?
Prints:
0
grep -q -P "\d{5}" <<<"This string contains 1234" 2>/dev/null; echo $?
Prints:
1
EDIT: Different grep utilities:
As noted in the comment by user3439894 it seems that some versions of the grep utility installed on Mac do not support the -P option which ensured the RegExp pattern was interpreted as a Perl regular expression. The reason why I opted to utilize a Perl Regular Expression is because they're more closely aligned to the regexp's used in JavaScript.
However, If you run man grep via your command line and discover that your greputility doesn't provide the -P option then change the following line of code in the regExpTest subroutine:
set statusCode to do shell script "grep -q -P " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
to this instead:
set statusCode to do shell script "grep -q -E " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
Note: The -P option has been changed to -E so the pattern is now interpreted as an extended regular expression (ERE) instead.
The shorthand metacharacter \d
You may also find that you need to change the the assignment of the regexp pattern from:
set regExp to "\\d{5}"
to
set regExp to "[0-9]{5}"
This time the shorthand metacharacter \d, (which is used match a digit), has been replaced with the equivalent character class [0-9].
As others have said, you can use the Foundation framework’s NSRegularExpression via the AppleScript-ObjC bridge.
That said, Objective-C APIs, while powerful, aren’t exactly AppleScripter-friendly, so I knocked together some “standard libraries” a few years back that wrapped a lot of that general functionality as nice native AppleScript commands.
e.g. Here’s the nearest equivalent to your JavaScript using the Text library’s search text command:
use script "Text"
set str to "This string contains 12345"
set foundMatches to search text str for "\\d{5}" using pattern matching
if foundMatches is not {} then
-- do something
end if
Couldn’t drum up much interest so I no longer do development or support. But they’re free and open (public domain as far as I’m concerned) and still work fine in the current version of macOS AFAIK, so help yourself.

TCL script only saves the bottom half of the output

I have script that logs into devices and runs a show command.
I then set this output to a variable:
set output $expect_out(buffer)
and then print the variable to a file:
puts $fileId $output
When the script is run, I can see the whole output being generated, however in the file, only the bottom half of the output is saved.
This is probably because the buffer is reaching its limit. This show command is running right after another lengthy show command.
I tried using unset expect_out(buffer) but this still does not make a difference.
I also tried this solution http://wiki.tcl.tk/2958 and it still did not work (returns an error)
How can I get the script to store all of the output?
I see in the expect man page that the pattern full_buffer will match when the size of the buffer reaches match_max bytes, so you can do something like:
match_max 16000
# ...
expect {
full_buffer {
puts $fileid $expect_out(buffer)
exp_continue
}
"whatever you are currently expecting"
}
puts $fileid $expect_out(buffer)
You can also make use of the log_file to make it simple. You can control when to save and when to stop logging.
Have a look at here to know about the same.

Expect: how to grab part of output

I'm a bit new to expect programming, so I need help.
Here's a sample session:
CMD> disable inactive
Accounts to be disabled are:
albert_a - UUID abcd-11-2222
brian_b - UUID bcde-22-3333
charley_c - UUID cdef-33-4444
Starting processing
...hundreds of lines of processing...
CMD> quit
Done.
I need to grab the username and UUIDs there (the UUIDs are not available through other means), then either save them into a file. How do I do that in expect?
Edit: the - UUID (space dash space "UUID") part of the list is static, and not found anywhere in the "hundreds of lines of processing", so I think I can match against that pattern... but how?
Assuming the answer to my question in the comments is 'yes', here's my suggestion.
First, you need to spawn whatever program will connect you to the server (ssh, telnet, or whatever), and login (expect user prompt, send password, expect prompt). You'll find plenty samples of that, so I'll skip that part.
Once you have done that, and have a command prompt, here's how I would send the command and expect & match output:
set file [open /tmp/inactive-users w] ;# open for writing and set file identifier
send "disable inactive\r"
expect {
-re "(\[a-z0-9_\]+) - UUID" { ;# match username followed by " - UUID"
puts $file "$expect_out(1,string)" ;# write username matched within parenthesis to file identifier
exp_continue ;# stay in the same expect loop in case another UUID line comes
}
-re "CMD>" { ;# if we hit the prompt, command execution has finished -> close file
close $file
}
timeout { ;# reasonably elegant exit in case something goes bad
puts $file "Error: expect block timed out"
close $file
}
}
Pls note that in the regexp I'm assuming that usernames can be composed of lowercase letters, numbers and underscores only.
If you need help with the login piece let me know, but you should be ok. There are plenty of samples of that out there.
Hope that helps!

Writing to an IO stream in Ruby instead of sanitizing user input and sending to the shell?

I was just reading a blog post about sanitizing user input in Ruby before sending it to the command line. The author's conclusion was: don't send user input it to the command line in the first place.
In creating a contact form, he said he learned that
What I should do instead is open a
pipe to the mail command as an IO
stream and just write to it like any
other file handle.
This is the code he used:
open( %Q{| mail -s "#{subject}" "#{recipient}" }, 'w' ) do |msg|
msg << body
end
(I actually added the quotes around recipient - they're needed, right?)
I don't quite understand how this works. Could anybody walk me through it?
OK, I'll explain it with the caveat that I don't think it's the best way to accomplish that task (see comments to your question).
open() with a pipe/vertical bar as the first character will spawn a shell, execute the command, and pass your input into the command through a unix-style pipe. For example the unix command cat file.txt | sort will send the contents of the file to the sort command. Similarly, open("| sort", 'w') {|cmd| cmd << file} will take the content of the file variable and send it to sort. (The 'w' means it is opened for writing).
The %Q() is an alternate way to quote a Ruby string. This way it doesn't interfere with literal quote characters in the string which can result in ugly escaping. So the mail -s command is being executed with a subject and a recipient.
Quotes are needed around the subject, because the mail command will be interpreted by the shell, and arguments are separated by spaces, so if you want spaces in an argument, you surround it with quotes. Since the -s argument is for the subject, it needs to be in quotes because it will likely contain spaces. On the other hand, the recipient is an email address and email addresses don't contain spaces, so they're not necessary.
The block is providing the piped input to the command. Everything you add to the block variable (in this case msg) is sent into the pipe. Thus the email body is being appended (via the << operator) to the message, and therefore piped to the mail command. The unix equivalent is something like this: cat body.txt | mail -s "subject" recipient#a.com

Resources