How to do escaping in this Perl code? - perl

#!/usr/bin/perl
use warnings;
system ("dialog --menu Customize 10 70 50 'Flush rules' 'Clear all the rules' 'Show rules' 'Shows the current rules' 2> /tmp/tmp.txt ")
I want to write the above code in a more readable form like this
#!/usr/bin/perl
use warnings;
system ("dialog --menu Customize 10 70 50
'Flush rules' 'Clear all the rules'
'Show rules' 'Shows the current rules'
'more options' '........' 2> /tmp/tmp.txt ")
How can I do this?

Perl provides a string concatentation operator that you can use to build up big strings:
system ( "dialog --menu Customize 10 70 50 "
. "'Flush rules' 'Clear all the rules' "
. "'Show rules' 'Shows the current rules' "
. "'more options' '........' 2> /tmp/tmp.txt ");

system can take #args (array form):
system ( 'dialog', #args );

system ( "dialog --menu Customize 10 70 50 "
. "'Flush rules' 'Clear all the rules' "
. "'Show rules' 'Shows the current rules' "
. "'more options' '........' 2> /tmp/tmp.txt ");
Dang, tadmc is fast. Yes, use the . concatenation command.
I would recommend that you create your command in a separate string and then execute that. I also recommend using the qq command to do quoting. That way, you don't have to worry about single vs. double quotes:
my $command = qq(dialog --menu Customize 10 70 50 )
. qq("Flush rules" 'Clear all the rules' )
. qq('Show rules' 'Shows the current rules' )
. qq'more options' '........' 2> /tmp/temp.$$ );
my $error = system $command;
Using qq allows you not to worry about whether I need to use double quotes to allow for variable interpolation or single quotes, or having to escape quotes. For example, I was able to mix double and single quotes, and I can use Perl variables without worrying whether or not I have to change from single to double quotes. For example, I use /tmp/temp.$$. The $$ is the process ID, so if this command is executed twice, there are two different temp files used.
By creating a separate variable for my command, I can now use it later -- like if there was an error in my system command.
By the way, you should always check the return of your system command. There's probably a good chance that if for some reason you can't execute your system command, you probably want to error out or at least note the issue.
One of the problems is that the output of the system command is the opposite of most Perl functions. In most Perl functions, a return of zero indicates a failure while a return of non-zero indicates success. However, the system function is the exact opposite. Zero means success, non-Zero means failure.
That can lead to strange if constructs:
if (system $command) {
die qq(Can't execute command "$command"\n);
};
This looks like I'm saying that if my system command succeeds, I should die, but it really means the same as this:
my $error = system $command;
if ($error) {
die qq(Can't execute command "$command"\n);
}
That syntactically makes a lot more sense.

Related

How to simulate multiple enter key press of batch file execution cmd or windows powershell?

I have batch file which I want to execute, during execution it asks for user input twice to press enter key. I want to skip this completely either by simulating the enter key press or by somehow completely overcoming it.
Edit : I can't edit the bat file to skip the program asking for user input
I have tried echo | <yourfinecommandhere> but this just simulates one enter key press. I am unable to simulate multiple enter key press
In cmd simply group multiple echos. For example this will print 2 newlines to the pipe
(echo[& echo[) | command
echo( is the most reliable way to print a new line in cmd, but in this case echo[ is probably better. Space is significant here so there must be no space after [
In PowerShell it's easier. For example to print 3 newlines use
"`n`n`n" | command
Actually the above will print 4 new lines, because there's an implicit new line after the string. But that won't affect the output. If you want exactly 3 new lines then use
Write-Output -NoNewline "`n`n`n" | command

Powershell Nested Replace

I am looping through multiple remote machines looking for a certain string to appear in a log file (other things are being collected from each device but for simplicity I've left that out). When I find them I want to return them and write to a central log, this is all working perfectly, except I want to tidy up the central log, by removing information from each string.
So I start with
**28-Jan-2021 01:31:49,TCPServer.run(),3,JMX TCP Server running on 8085
But want to save to Central Log
28-Jan-2021 01:31:349,JMX TCP 8085
And I can achieve this using the below, but surely there is a more succinct way to do this? (have played about with -Replace but no joy)
$JMXString8085 = $JMXString8085.Replace("TCPServer.run(),3,","")
$JMXString8085 = $JMXString8085.Replace("}","")
$JMXString8085 = $JMXString8085.Replace(" Server running on","")
[...] surely there is a more succinct way to do this? (have played about with -Replace but no joy)
There is, and -replace can indeed help us here. -replace is a regex operator, it performs text replacement using regular expressions - patterns we can use to describe strings that we might not be quite sure the exact contents of.
For a string like:
$string = '**28-Jan-2021 01:31:49,TCPServer.run(),3,JMX TCP Server running on 8085'
... we could describe the fields in between the commas, and use that to tell PowerShell to only preserve some of them for example:
PS ~> $string -replace '^\*\*([^,]+),[^,]+,[^,]+,([^,]+) Server running on (\d+)', '$1,$2 $3'
28-Jan-2021 01:31:49,JMX TCP 8085
The pattern I used in this example (^\*\*([^,]+),[^,]+,[^,]+,([^,]+) Server running on (\d+)) might seem a bit alien at first, so let's try and break it down:
^ # carret means "start of string"
\*\* # Then we look for two literal asterisks
( # This open parens means "start of a capture group"
[^,]+ # This means "1 or more characters that are NOT a comma", captures the timestamp
) # And this matching closing parens means "end of capture group"
, # Match a literal comma
[^,]+ # Same as above, this one will match "TCPServer.run()"
, # Same as above
[^,]+ # You probably get the point by now
, # ...
( # This open parens means "start ANOTHER capture group"
[^,]+? # The `?` at the end means "capture as few as possible", captures "JMX TCP"
) # And this matching closing parens still means "end of capture group"
Server... # Just match the literal string " Server running on "
( # Finally a THIRD capture group
\d+ # capturing "1 or more digits", in your case "8085"
) # and end of group
Since our pattern "captures" a number of substrings, we can now refer to these individual substrings in out substition pattern $1,$2 $3, and PowerShell will replace the $N references with the capture group value.
here is yet another way to do the job. [grin]
what it does ...
assigns the string to a $Var
chains .Replace() to get rid of the asterisks and the "Server" phrase
splits on the , chars
takes the 1st & 4th items from that split
joins them into one string with , [comma then space] for a delimiter
assigns that to a new $Var
displays the results
the code ...
$InString = '**28-Jan-2021 01:31:49,TCPServer.run(),3,JMX TCP Server running on 8085'
$OutString = ($InString.Replace('**', '').Replace('Server running on ', '').Split(',')[0, 3]) -join ', '
$OutString
output ...
28-Jan-2021 01:31:49, JMX TCP 8085

How to echo bare hypen (`-`) in zsh?

I'm looking for a way to work around zsh echo's apparently treating a string that is just a hyphen as if it were an empty string
echo -
# no output
echo "-"
# no output
echo '-'
# no output
Specifically, I'm splitting a string at a known character and then working with the two pieces, and either of the two pieces could be -. Like
% my_f() {
my_arr=(${(s.b.)1})
echo $my_arr[1]
echo $my_arr[2]
}
% my_f "abc"
a
b
% my_f "-bc"
# I need to know -
b
% my_f "ab-"
a
# I need to know -
%
In the particular thing I'm working on, I can rework things so that the potential - isn't echo'd on its own
my_arr=(${(qqqs.b.)1})
echo " ${(Q)my_arr[1]} "
echo " ${(Q)my_arr[2]} "
But that feels like luck and will take sprinkling a lot of qqq and Q around this script. Is there a better way?
Try echo - "-". The first dash terminates option processing, so following text is printed.
See this excellent answer for more context: https://stackoverflow.com/a/57656708/11776945
Use printf instead. (Which is generally good advice regarding any use of echo.)
my_f () {
printf '%s\n' "${(s.b.)1}"
}

Telnet Session using Expect Module fails to send command ctrl+A

I am trying to connect to switch which is signaling point switch.
I need to execute the below commands to get to login terminal.
telnet IP Port
Send CTRL+A
Get Prompt ">"
Send command login:uid=user.
Requests for Password provide password.
Gets terminal ">"
Then I have to continue executing some commands to go further, but my problem I am facing an issue while sending CTRL+A.
When I send "ctrl+A" using Expect it just prints "^A" and waits doesn't provide me the terminal.
So, I modified the script by changing the command to "ctrl+A\n" which gives me the terminal but with new line on my next terminal prompt.
Like the below output:
^A
^A
^A
>
wait's here at next line.
which fails to match my next command regular expression ">" and doesn't send the login name.
Can somebody tell me why my first command "^A" fails to get me terminal? And why the command was executed three times before I get the terminal?
I manual scenario it works fine for single ctrl+A
My Sample code:
use Expect;
my $exp = Expect->spawn("telnet 10.10.1.35 2020");
$exp->expect($timeout,
[ qr/]'./ => sub {my $exp = shift;
$exp->send("\cA\n");
} ]
);
$exp->expect($timeout,
[ qr/>/ => sub { my $exp = shift;
$exp->send("login:uid=user\n");
} ]
);
$exp->expect($timeout,
[ qr/Enter Password :/ => sub { my $exp = shift;
$exp->send("xxx\n");
} ]
);
Thank you,
Pradeep.
Try With send "\01"
Please consult for more info
http://expect.sourceforge.net/FAQ.html#q54
I like to enter most control characters literally - since the resulting script is more readable. However, your editor has to allow this. For example, suppose you want to send a Control-A. Conventionally, most editors display this as ^A, but you can't just enter ^ and A. (This will just send those two characters.) Most editors have some simple quoting mechanism that lets you enter the next character literally. For example, using emacs, I can enter ^Q^A and that will add the single character ^A.
Alternatively, Tcl provides a way of encoding using octal or hex. The octal encoding mechanism is less error-prone than hex so I'll demonstrate using octal. For example, since ^A has the octal value 1 in ASCII, to send a ^A:
send "\01"

To fix a bug in Less' command by noting no. of parameters

This question is based on this thread.
The code
function man()
{
man "$1" > /tmp/manual; less /tmp/manual
}
Problem: if I use even one option, the command does not know where is the wanted-manual
For instance,
man -k find
gives me an error, since the reference is wrong. The command reads -k as the manual.
My attempt to solve the problem in pseudo-code
if no parameters
run the initial code
if one parameter
run: man "$2"
...
In other words, we need to add an option-check to the beginning such that
Pseudo-code
man $optional-option(s) "$n" > /tmp/manual; less /tmp/manual
where $n
n=1 if zero options
n=2 if 1 option
n=3 if 2 options
....
How can you make such an "option-check" that you can alter the value of $n?
Developed Problem: to make two if loops for the situations from n=1 to n=2
How about passing all the arguments
function man()
{
man $# > /tmp/manual; less /tmp/manual
}
What is the bug in less which you mention in the title?
First, you can pass all of your function's arguments to man by using $* or $#. You can read man sh for the precise details on the difference between the two; short story is to almost always use "$#" with double quotes.
Second, the temporary file is unnecessary. You could make this a little cleaner by piping the output of man directly to less:
function man() {
man "$#" | less
}
By the way, if you're just trying to use a different pager (man uses more and you want the fancier less) there's a commonly recognized PAGER environment variable that you can set to override the default pager. You could add this to your ~/.bashrc for instance to tell all programs to use less when displaying multiple screens of output:
export PAGER=less
To answer your precise question, you can check the number of arguments with $#:
if [ $# -eq 0 ]; then
: # No arguments
elif [ $# -eq 1 ]; then
: # One argument
# etc.
You might also find the shift command helpful. It renames $2 to $1, $3 to $2, and so on. It is often used in a loop to process command-line arguments one by one:
while [ $# -gt 1 ]; do
echo "Next argument is: $1"
shift
done
echo "Last argument is: $1"