Perl - system command printing to terminal? - perl

I'm writing a Perl script that allows me to change the tile of my terminal tabs, however, everytime I run it, a "-n -e" gets printed to my terminal line. If I leave those options out, just a blank line gets printed. Is there any way I can execute the system command inside perl, and not have any code remnants show up on my terminal?
1 #!/usr/bin/perl
2 #sets the title on terminal tabs (mac OSX)
3
4 #use strict;
5 use warnings;
6
7 #sets title of term window to input from user.
8 my $textline="\'echo -n -e \"\\033]0;";
9 $textline = $textline . "#ARGV" . '\007"\'';
10
11 system "\'$textline\'";
12
Thanks

I imagine -n and -e get echoed because your version of echo doesn't recognize those options and treats them as literal text to echo.
But you know what, Perl has its own tool for echoing text: print.
print("\033]0;$title\007");
You might need to flush the buffer afterwards:
use IO::Handle qw( );
STDOUT->flush();
Or just the following if you don't mind turning off buffering:
$| = 1;
PS - Your quotes (''echo ...'') makes no sense, but they collapse into nothingness, so they don't cause a problem.

You can redirect the outputs by '>' and '2>' or you can use qx.
For e.g in qx case you can write
my $output = qx( $your_command );

Related

Perl Expect.pm send control character to subprocess

I am trying to write a script that uses the bash line editor to recall a previously entered command. Here's the simple session I'm trying to automate.
$ bash --norc --noprofile
bash4.4$ echo hi
hi
bash4.4$
then type '^P^M'
bash4.4$ echo hi
hi
Here is my first attempt at scripting this using the Expect.pm module off CPAN. The two sleep 1s are in there to guard against the possibility of race conditions when invoking the line editor since I'm not sure what perl sees when that happens.
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use autodie;
use Expect;
my $timeout = 10;
my $exp = Expect->new();
$exp->spawn('bash --norc --noprofile');
# wait for first prompt
$exp->expect($timeout, '$ ');
# send echo hi
$exp->send("echo hi\n");
# wait for prompt again
$exp->expect($timeout, '$ ');
# use history recall ^P, then send ^M
sleep 1;
$exp->send("\cp\cm");
sleep 1;
print "okay done!\n";
It works up until I hit "\cp\cm" (I've also tried "\cP\cM"). According to the perl documentation, \cX introduces an ASCII control character. (http://perldoc.perl.org/perlrebackslash.html#Character-Escapes)
Control characters \c is used to denote a control character; the
character following \c determines the value of the construct. For
example the value of \cA is chr(1), and the value of \cb is chr(2),
etc. The gory details are in Regexp Quote-Like Operators in perlop. A
complete list of what chr(1), etc. means for ASCII and EBCDIC
platforms is in OPERATOR DIFFERENCES in perlebcdic.
This is what I actually get when I run the script, which strongly suggests that the control characters are not getting passed to the subprocess properly and possibly aren't passed at all.
% perl bash.pl
bash-4.4$ echo hi
hi
bash-4.4$ okay done!
What's going on here? How do I pass a control character to a process with Expect.pm?
The characters escapes you are searching are specific to Perl regex. For your purpose, I would suggest you to pass the hex value for ctrl-M and ctrl-P.
$exp->send("\x10"); # ctrl+P
$exp->send("\x0D"); # ctrl+M
Update(tested):
$exp->send("\x10"); # ctrl+P
$exp->send("\n"); # send newline
sleep 2;
# wait for prompt
$exp->expect($timeout, '$ ');
$exp->send("\x0D"); # ctrl+M
$exp->send("\n"); # send newline

perl 3 or 4 line commands via backticks

How can I use this code to create more commands. The current version does 2. How can i do 3 or 4 or more?
my $startprocess = `(echo "y" | nohup myprocess) &`
The original question answered by user DVK:
Can I execute a multiline command in Perl's backticks?
edit: thanks for the reply sebastian.
I have to run everything in one line because Im running a program within terminal and i want to make progressive commands.
e.g command 1 starts the program. command 2 navigates me to the menu. Command 3 lets me change a setting. Command 4 lets me issue a command that prompts a response that I can only get under the condition of that new setting.
To run multiple commands would keep me trapped in step one.
The line you quoted contains one command line piped together. That's not running multiple commands.
Did you consider using open?
my $pid = open my $fh, '-|', 'myprocess';
print $fh "y\n";
There is no need to run multiple commands in one (backtick) line, why not just use multiple ones?
$first = `whoami`;
$second = `uptime`;
$third = `date`;
Backticks are used to capture the output of the command, system just runs the command and returns the exit state:
system '(echo "y" | nohup myprocess) &';
All solutions allow multiple commands piped together as this is a shell feature and all commands just pass the command string to the shell (unless it's simple enough to handle it without a shell):
Bash:
$ ps axu|grep '0:00'|sort|uniq -c|wc
Perl:
system "ps axu|grep '0:00'|sort|uniq -c|wc";
$result = `ps axu|grep '0:00'|sort|uniq -c|wc`;
open my $fh, '|-', "ps axu|grep '0:00'|sort|uniq -c|wc";
Always watch the quotation marks: system "foo "bar" baz"; won't pass "bar" baz as arguments to the foo command.
Lots of common stuff in this answer: Please be more detailed in your question to get a better reply.

Simulate pressing enter key in Perl

I have following perl script which saves the output from the command into a textfile
#!/usr/bin/perl
use warnings;
use strict;
use Term::ANSIColor;
my $cmd_backupset=`ssh user\#ip 'dsmadmc -id=username -password=passwd "q backupset"' >> output.txt`;
open CMD, "|$cmd_backupset" or die "Can not run command $!\n";
print CMD "\n";
close CMD;
The output of output.txt is this:
Text Text Text
...
more... (<ENTER> to continue, 'C' to cancel)
The script is still running in the terminal and when I press enter, the output.txt file gets the extra information. However, I must press enter more than 30 times to get the complete output. Is there a way to automate the script so when the last line in output.txt is more..., it simulates pressing enter?
I have tried with Expect (couldn't get it installed) and with echo -ne '\n'
Most interactive commands, like the one you are using, accept some flag or some command to disable pagination. Sometimes, connecting their stdin stream to something that is not a TTY (e.g. /dev/null) also works.
Just glancing over IBM dsmadmc docs, I see it accepts the option -outfile=save.out. Using it instead of standard shell redirection would probably work in your case.

How to execute perl file from shell script

I have a question about how to execute the perl file inside of a shell script
I have 2 files now, "test.sh" and "test.pl", here are example of my scripts
SHELL script
#!/bin/bash
perl FILEPATH/test.pl
......
PERL script
#!/usr/bin/perl
my $a = "hello"
sub saysomething
{
print $a;
}
.....
The way I call the shell script is : under the path of shell scripts, execute "./test.sh"
All mentioned above are working under the environment
GUN bash, version 4.2.24(1)-release (i686-pc-linux-gnu) + perl (v5.14.2)
But if I put those scripts on server (which I couldn't change the bash / perl version)
GNU bash, version 4.2.10(1)-release (x86_64-pc-linux-gnu) + perl (v5.12.4), I got the followign message:
FILEPATH/test.pl: line 2: my: command not found
Does anybody know how can I solve this problem?
BTW, if I execute the perl script individually (perl FILEPATH/FILENAME.pl), it works perfectly.
In order to execute a perl script by .sh script you dont need to use perl prefix, but only:
#!/bin/sh
/somewhere/perlScript.pl
It will work without problem.
This problem is at least two-fold. One, you have to have the location of Perl in your environment PATH. Two, the location of Perl may be different on different machines. One solution to both problems that I, and others, have used for years is to make use of a "magic header" of some sort at the top of Perl programs. The header identifies itself as a sh shell script and leverages the fact that /bin/sh exists in every version/flavor of Linux/UNIX. The header's job is to fortify the PATH with various possible Perl locations and then run the Perl script in place of itself (via exec). Here is a "Hello World" example:
1 #! /bin/sh --
2 eval '(exit $?0)' && eval 'PERL_BADLANG=x;PATH="/usr/bin:/bin:/usr/local/bin:$PATH";export PERL_BADLANG;: \
3 ;exec perl -x -S -- "$0" ${1+"$#"};#'if 0;
4 exec 'setenv PERL_BADLANG x;exec perl -x -S -- "$0" $argv:q;#'.q
5 #!/bin/perl -w
6 +($0=~/(.*)/s);do(index($1,"/")<0?"./$1":$1);die$#if$#;__END__+if 0;
7 # Above is magic header ... real Perl code begins here
8 use strict;
9 use warnings;
10 print "hello world!\n";
Note: I added line numbers just to make it clear where lines start and end.
First check where perl is installed on your system, e.g. which perl and use that location in the shebang line instead of /usr/bin/perl, if it is different.
If all other recommendations fail, check the first line of the script on the machine where it is not running properly by doing this: head -1 test.pl | xxd. Does the output show the last two bytes as 0d 0a? If so, you probably copied over the file via Windows and didn't do a dos2unix conversion.
"command not found" is an error emitted by the shell. You are trying to run your Perl script by the shell, not by Perl.

How can I pipe from terminal in Perl without losing color?

I'm trying to write a perl script which takes the output of colorgcc (or any other script that prints colored text to terminal), adds/removes parts of the string, and then prints the result in the same color as the input string.
The following code will print "Hello World" in front of each line produced by the color_producing_script. The output will be all black, while the input is multicolored. How can I modified this script to conserve the original colors?
open(CMD, "color_producing_script |");
while(<CMD>) {
print 'Hello World' . $_;
}
I'm using bash terminal.
Edit
Per the excellent first comment, this is not a Perl issue per se. Just running color_producing_script | cat strips the color off. So the question can be rephrased to "How do you force color through the pipe?"
Edit 2
It looks like the latest version of gcc (1.3.2) includes the CGCC_FORCE_COLOR environment variable in the if clause, and if it's defined, colorgcc forces color:
export CGCC_FORCE_COLOR=true
Does color_producing_script change its behavior when it's used in a pipe? Try
color_producing_script | cat
at the command line. It may have an option to force color output even when it is.
The Perl script, colorgcc, is specifically checking to see if output is to a non-tty and skipping the colorization if that's the case.
# Get the terminal type.
$terminal = $ENV{"TERM"} || "dumb";
# If it's in the list of terminal types not to color, or if
# we're writing to something that's not a tty, don't do color.
if (! -t STDOUT || $nocolor{$terminal})
{
exec $compiler, #ARGV
or die("Couldn't exec");
}
Edit:
You could modify the script in one or more of the following ways:
comment out the test and make it always produce color output
add functionality to support reading an environment variable that sets whether to colorize
add functionality to support a color-always option in the ~/.colorgccrc configuration file
add functionality to support a color-always command line option that you strip before passing the rest of the options to the compiler
You could also use the expect script unbuffer to create a pseudo-tty like this:
unbuffer gcc file.c | cat
(where cat is a standin recipient).
All of this is based on using colorgcc from the command line. There should be analogs for doing similar things within a Perl script.
Many programs that generate colored output detect if they're writing to a TTY, and switch off colors if they aren't. This is because color codes are annoying when you only want to capture the text, so they try to "do the right thing" automatically.
The simplest way to capture color output from a program like that is to tell it to write color even though it's not connected to a TTY. You'll have to read the program's documentation to find out if it has that option.
The other option is to use a Pty instead of a pipe. In Perl, you can do this with IO::Pty::HalfDuplex or IO::Pty::Easy, both of which are higher-level wrappers around the low-level module IO::Pty.
The source code of ColorGCC is quite clear about this topic!
#! /usr/bin/perl -w
# ...
# from: colorgcc-4.1.2/colorgcc-4.1.2
# downloaded from: http://www.console-colors.de/index.php?n=ConsColors.Downloads
#
# Note:
#
# colorgcc will only emit color codes if:
#
# (1) Its STDOUT is a tty and
# (2) the value of $TERM is not listed in the "nocolor" option.
#
# If colorgcc colorizes the output, the compiler's STDERR will be
# combined with STDOUT. Otherwise, colorgcc just passes the output from
# the compiler through without modification.
# .....
# If it's in the list of terminal types not to color, or if
# we're writing to something that's not a tty, don't do color.
if (! -t STDOUT || $nocolor{$terminal})
{
exec $compiler, #ARGV
or die("Couldn't exec");
}
In addition to use a Pty instead of a pipe in Perl (as already pointed out by cjm) you can trick an application into thinking its stdin is interactive, not a pipe on the command line as well.
For example:
# Linux
script -c "[executable string]" /dev/null
# FreeBSD, Mac OS X
script -q /dev/null "[executable string]"
For further solutions see: bash: force exec'd process to have unbuffered stdout