For Loop in Windows Command Line - command-line

How to get make this loop working:
set list=am gp it ol
FOR %%a IN (%list%) DO (
set Indxed= %%a
ECHO %Indxed%
)
Echo always output to :ol ol ol ol. How can I fix this issue ?

Test this:
#echo off
setlocal enabledelayedexpansion
set list=am gp it ol
FOR /f "delims=" %%a IN ("%list%") DO (
set Indxed=%%a
ECHO !Indxed!
)

Try following:
#echo off
setlocal
set list=am gp it ol
echo %list%
call :parse "%list%"
goto :eos
:parse
set list=%1
set list=%list:"=%
FOR /f "tokens=1* delims= " %%a IN ("%list%") DO (
if not "%%a" == "" call :sub %%a
if not "%%b" == "" call :parse "%%b"
)
goto :eos
:sub
echo %1
goto :eos
:eos
endlocal

Related

How to identify new line chars when file has both CR and CRLF characters

I need to identify the new line chars if any using powershell or batch file and if present remove.
In a comment you state:
each record starts with DTL
It sounds like the way to fix your file is to remove any newlines that aren't followed by verbatim DTL| (the code handles both CRLF and LF-only newlines):
# Create sample file.
#'
DTL|foo1
DTL|foo2
result of an unwanted
newline or two
DTL|foo3
'# > test.txt
# Replace all newlines not directly followed by verbatim 'DTL|'
# with a space (remove `, ' '` if you simply want to remove the newlines).
# Pipe to Set-Content -NoNewLine in order to save to a file as needed.
(Get-Content -Raw test.txt) -replace '\r?\n(?!DTL\||\z)', ' '
Output:
DTL|foo1
DTL|foo2 result of an unwanted newline or two
DTL|foo3
For an explanation of the regex used with the -replace operator above and the ability to experiment with it, see this regex101.com page.
I am afraid I don't really understand what you want. You didn't posted any input file nor specified what is the output you want from such an input. Anyway, I hope this code can help:
#echo off
setlocal EnableDelayedExpansion
rem Create a test file
set LF=^
%don't remove%
%these lines%
(
echo Line One: CR+LF
set /P "=Line Two: LF!LF!"
echo Line Three: CR+LF
) > test.txt < NUL
rem Read the file
set "acum=0"
(for /F "tokens=1* delims=:" %%a in ('findstr /O "^" test.txt') do (
if not defined line (
set "line=%%b"
) else (
set /A "len=%%a-acum-2, acum=%%a"
for %%n in (!len!) do if "!line:~%%n!" equ "" (
echo !line!
) else (
set /P "=!line!"
)
set "line=%%b"
)
)) < NUL
for %%a in (test.txt) do set /A "len=%%~Za-acum-2"
(for %%n in (!len!) do if "!line:~%%n!" equ "" (
echo !line!
) else (
set /P "=!line!"
)) < NUL
Output:
Line One: CR+LF
Line Two: LFLine Three: CR+LF
This example first create a file with three lines, but the second one is ended in LF instead of CR+LF. Then, the program identify how each line ends and remove the alone LF's
The method is based on findstr /O switch that reports the offset of the first byte of each line starting from beginning of file

How can I get a Perl script to accept parameters from both STDIN and command line arguments?

Inspired by this U&L Q&A titled: "https://unix.stackexchange.com/questions/171150/back-to-back-pipes-into-a-command". How could one parse both input via STDIN and via command line arguments to a Perl script?
For example I'd like a script that can consume input parameters from both STDIN and via command line arguments:
$ command | my_command.pl arg1 arg2
And the output of command would be
arg3
arg4
...
So while my_command.pl is running, it would be aware of the parameters arg1, arg2, arg3, and arg4.
I came up with the following Perl script which reads data from either STDIN or command line arguments, or both.
The script
$ cat command.pl
#!/usr/bin/perl -w
use strict;
if ( -t STDIN and not #ARGV ) {
print "\n" ."USAGE" . "\n";
print "=====" . "\n";
print "#1:\t $0 <arg1>..<argN>\n";
print "#2:\t <commands> | $0\n";
print "#3:\t <commands> | $0 <arg1>..<argN>\n\n";
exit 1;
}
if ( not -t STDIN ) {
print "\n" . "STDIN ARGS" . "\n";
print "==========" . "\n";
print while (<STDIN>);
print "\n";
}
if (#ARGV) {
print "\n" . "ARGV ARGS" . "\n";
print "=========" . "\n";
foreach (#ARGV) {
print "$_\n";
}
print "\n";
}
How it works
The script makes use of both #ARGV and <STDIN>, keying off of whether they're defined or not. If neither is defined then it shows usage and bails out.
Test script
To test it I put together this secondary script called tests.bash which runs the above script using a variety of ways to pass input into it. NOTE: That the tests are labelled T00 through T10.
$ cat tests.bash
#!/bin/bash
echo ""
echo ""
echo "====================================="
echo "T00: no input"
./command.pl
echo "====================================="
echo ""
# http://www.tldp.org/LDP/abs/html/subshells.html
echo "====================================="
echo "T01: 2 args w/ pipe via cmd list '(..) | ...' aka. subshell"
( echo "pipearg1"; echo "pipearg2"; ) | ./command.pl
echo "====================================="
echo ""
# http://www.tldp.org/LDP/abs/html/special-chars.html#CODEBLOCKREF
echo "====================================="
echo "T02: 2 args w/ pipe via inline group '{..} | ...' aka. code block"
{ echo "pipearg1"; echo "pipearg2"; } | ./command.pl
echo "====================================="
echo ""
echo "====================================="
echo "T03: 2 cli args 'cmd A1 A2'"
./command.pl argv1 argv2
echo "====================================="
echo ""
echo "====================================="
echo "T04: T01 + T03"
( echo "pipearg1"; echo "pipearg2"; ) | ./command.pl argv1 argv2
echo "====================================="
echo ""
echo "====================================="
echo "T05: T02 + T03"
{ echo "pipearg1"; echo "pipearg2"; } | ./command.pl argv1 argv2
echo "====================================="
echo ""
echo "====================================="
echo "T06: echo with newline: $'..\n..'"
echo $'pipearg1\npipearg2' | ./command.pl
echo "====================================="
echo ""
echo "====================================="
echo "T07: echo -e with newline: '..\n..'"
echo -e "pipearg1\npipearg2" | ./command.pl
echo "====================================="
echo ""
echo "====================================="
echo "T08: 2 cli args via HEREDOC 'cmd <<EOF ... EOF'"
./command.pl <<EOF
arghd1
arghd2
EOF
echo "====================================="
echo ""
echo "====================================="
echo "T09: 2 cli args via process substitution 'cmd < <(...cmds...)'"
./command.pl < <(echo argps1; echo argps2)
echo "====================================="
echo ""
echo "====================================="
echo "T10: T03 + T09"
./command.pl argv1 argv2 < <(echo argps1; echo argps2)
echo "====================================="
echo ""
Output from tests.bash
The tests.bash script is designed to run 11 different tests, ranging from using HEREDOCS, to subshells, to passing command line arguments. It also uses these in combinations to fully test the Perl script, command.pl.
$ ./tests.bash
=====================================
T00: no input
USAGE
=====
#1: ./command.pl <arg1>..<argN>
#2: <commands> | ./command.pl
#3: <commands> | ./command.pl <arg1>..<argN>
=====================================
=====================================
T01: 2 args w/ pipe via cmd list '(..) | ...' aka. subshell
STDIN ARGS
==========
pipearg1
pipearg2
=====================================
=====================================
T02: 2 args w/ pipe via inline group '{..} | ...' aka. code block
STDIN ARGS
==========
pipearg1
pipearg2
=====================================
=====================================
T03: 2 cli args 'cmd A1 A2'
ARGV ARGS
=========
argv1
argv2
=====================================
=====================================
T04: T01 + T03
STDIN ARGS
==========
pipearg1
pipearg2
ARGV ARGS
=========
argv1
argv2
=====================================
=====================================
T05: T02 + T03
STDIN ARGS
==========
pipearg1
pipearg2
ARGV ARGS
=========
argv1
argv2
=====================================
=====================================
T06: echo with newline: $'..\n..'
STDIN ARGS
==========
pipearg1
pipearg2
=====================================
=====================================
T07: echo -e with newline: '..\n..'
STDIN ARGS
==========
pipearg1
pipearg2
=====================================
=====================================
T08: 2 cli args via HEREDOC 'cmd <<EOF ... EOF'
STDIN ARGS
==========
arghd1
arghd2
=====================================
=====================================
T09: 2 cli args via process substitution 'cmd < <(...cmds...)'
STDIN ARGS
==========
argps1
argps2
=====================================
=====================================
T10: T03 + T09
STDIN ARGS
==========
argps1
argps2
ARGV ARGS
=========
argv1
argv2
=====================================
References
Back to back Pipes into a Command
This is rather easy to do using perl's diamond operator
push(#ARGV, "/dev/stdin");
while(<>) {
print;
}
For example:
$ echo A > a
$ echo B > b
$ echo C | perl test.pl a b
A
B
C
Unfortunately this does rely on /dev/stdin, but is still fairly portable.
You can add input from STDIN at the end of #ARGV,
push #ARGV, <STDIN> unless -t STDIN;

Perl Read YAML from a variable to hash

I have a linux command which fetches some data in yaml format from an api. I was looking for a way to populate this variable to a hash array. I was trying YAML::Tiny and the chunk of code that I wrote was
use YAML::Tiny;
my $stuff = `unix command that returns as yaml output`
my $yaml = YAML::Tiny->new;
$yaml = YAML::Tiny->new->read_string->($stuff);
but running with this code will error out as
Can't use string ("") as a subroutine ref while "strict refs" in use
$stuff variable would look like
Cluster1:
source_mount: /mnt/uploads
dir: /a /b
default: yes
destination_mount: /var/pub
Cluster2:
source_mount: /mnt/uploads
dir: /c /d /e
default: no
destination_mount: /var/pub
You should not use new twice, and there should not be -> after read_string (refer to the POD for YAML::Tiny). Change:
$yaml = YAML::Tiny->new->read_string->($stuff);
to:
$yaml = YAML::Tiny->read_string($stuff);
Here is a complete working example:
use warnings;
use strict;
use YAML::Tiny;
my $stuff = '
Cluster1:
source_mount: /mnt/uploads
dir: /a /b
default: yes
destination_mount: /var/pub
Cluster2:
source_mount: /mnt/uploads
dir: /c /d /e
default: no
destination_mount: /var/pub
';
my $yaml = YAML::Tiny->new();
$yaml = YAML::Tiny->read_string($stuff);
print $yaml->[0]->{Cluster2}->{dir}, "\n";
__END__
/c /d /e

Suppress prints from TASKKILL

I'm writing a Perl script that makes system calls to kill running processes. For instance, I want to kill all PuTTy windows. In order to do this, I have:
system('TASKKILL /F /IM putty* /T 2>nul');
For each process that's killed, however, I get a print saying
SUCCESS: The process with PID xxxx child of PID xxxx has been terminated.
which is cluttering my CLI. What's an easy way to eliminate these prints? Also note, that I'm executing these scripts in Cygwin.
Redirect sderr->stdout->nul:
system('TASKKILL /F /IM putty* /T 1>nul 2>&1');
or just simply grab output:
my $res = `TASKKILL /F /IM putty* /T 2>nul`;
TASKKILL writes to the first file descriptor (standard output), not the second.
You want to say
system('TASKKILL /F /IM putty* /T >nul');
$exec_shell='TASKKILL /F /IM putty* /T 2>nul';
my $a = run_shell($exec_shell);
#i use this function:
sub run_shell {
my ($cmd) = #_;
use IPC::Open3 'open3';
use Carp;
use English qw(-no_match_vars);
my #args = ();
my $EMPTY = q{};
my $ret = undef;
my ( $HIS_IN, $HIS_OUT, $HIS_ERR ) = ( $EMPTY, $EMPTY, $EMPTY );
my $childpid = open3( $HIS_IN, $HIS_OUT, $HIS_ERR, $cmd, #args );
$ret = print {$HIS_IN} "stuff\n";
close $HIS_IN or croak "unable to close: $HIS_IN $ERRNO";
; # Give end of file to kid.
if ($HIS_OUT) {
my #outlines = <$HIS_OUT>; # Read till EOF.
$ret = print " STDOUT:\n", #outlines, "\n";
}
if ($HIS_ERR) {
my #errlines = <$HIS_ERR>; # XXX: block potential if massive
$ret = print " STDERR:\n", #errlines, "\n";
}
close $HIS_OUT or croak "unable to close: $HIS_OUT $ERRNO";
#close $HIS_ERR or croak "unable to close: $HIS_ERR $ERRNO";#bad..todo
waitpid $childpid, 0;
if ($CHILD_ERROR) {
$ret = print "That child exited with wait status of $CHILD_ERROR\n";
}
return 1;
}

Why aren't a batch file's environment variables set when run from Perl?

I am trying to invoke a bat file from a perl script as follows:
system("call D:/SIP/run_$file_idx.bat");
However I observe that the environment variables in the bat fail to get resolved.
If I run the bat file separately from the command prompt it works.
Does system() create a new environment and execute bat file in that?
What is that I am missing?
if (($ENV{'IPSL_RUN_FLAG'}) eq "TRUE") {
my $Book = $Excel->Workbooks->Open(
"$ENV{'IPSL_TESTCASES_PATH'}IPSLFeatureLoadRunResults.xls");
# Make transparent where the IPSL tarball is installed.
# Have ControlPanel save results here.
# You can dynamically obtain the number of worksheets, rows, and columns
# through the Excel OLE interface. Excel's Visual Basic Editor has more
# information on the Excel OLE interface. Here we just use the first
# worksheet, rows 1 through 4 and columns 1 through 3.
# select worksheet number 1 (you can also select a worksheet by name)
my $count=0;
my $Sheet = $Book->Worksheets("LOADDATA");
my $tmp=0;
foreach my $row (13..776) {
foreach my $col (17..17) {
if(($Sheet->Cells($row,$col)->{'Value'} eq "Failed") ||
($Sheet->Cells($row,$col)->{'Value'} eq "No Run") ) {
$cnt_of_current_rerun_testcases++;
foreach my $col (18..18) {
# skip empty cells
next unless defined $Sheet->Cells($row,$col)->{'Value'};
my $a = $Sheet->Cells($row,$col)->{'Value'};
my $i = index($a, 'run');
$a = substr($a, 0, $i); #remove runTest*
print OUT "\n";
if($count == 0) {
print OUT "\nREM ";
print OUT "*" x 100;
print OUT "\n";
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ips32.exe";
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ipsldb.exe";
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ipsltiu.ex\n";
}
print OUT "c:\n";
print OUT "\ncd ";
$a =~ s/%I/\"%I/g;
$a=~s/H%/H%\"/g;
print OUT " $a\n";
print OUT "\n";
print OUT "CALL run_SubscribeFeatureOnHIQ.bat";
print OUT "\n";
print OUT "sleep 10\n";
print OUT "\ncd ";
print OUT " $a\n";
print OUT "\n";
print OUT "CALL ";
$i=$Sheet->Cells($row,$col)->{'Value'};
$i=~ s/%I/\"%I/g;
$i=~s/H%/H%\"/g;
print OUT $i;
#print OUT $Sheet->Cells($row,$col)->{'Value'};
print OUT "\n";
$count++;
if($count == $no_simul_tcases) {
$sleep_cnt++;
print OUT "echo Going for sleep $sleep_cnt\n";
print OUT "SLEEP 300";
print OUT "\n";
$count=0;
}
}
}
}
}
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ips32.exe";
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ipsldb.exe";
print OUT "\ntaskkill /F /FI \"USERNAME eq %USERNAME%\" /IM ipsltiu.ex\n";
print OUT "\nset IPSL_RUN_FLAG=TRUE";
close OUT ;
system(\"start $ENV{'IPSL_TESTCASES_PATH'}SIP\\run_$file_idx.bat\");
And the batch file is:
taskkill /F /FI "USERNAME eq %USERNAME%" /IM ips32.exe
taskkill /F /FI "USERNAME eq %USERNAME%" /IM ipsldb.exe
taskkill /F /FI "USERNAME eq %USERNAME%" /IM ipsltiu.ex
c:
cd "%IPSL_TESTCASES_PATH%"SIP\TestCASE\FEATURESINT\INT_cfSRS\INT_cfSRS_cfSERRNG\
CALL run_SubscribeFeatureOnHIQ.bat
sleep 10
cd "%IPSL_TESTCASES_PATH%"SIP\TestCASE\FEATURESINT\INT_cfSRS\INT_cfSRS_cfSERRNG\
CALL "%IPSL_TESTCASES_PATH%"SIP\TestCASE\FEATURESINT\INT_cfSRS\INT_cfSRS_cfSERRNG\runTest_SRS_INT_SERRNG.bat
Possible workaround:
By using exec and specifically writing the ENV into bat file gave me a workaround:
print OUT map { "set $_=$ENV{$_}\n" }
qw( path USERPROFILE USERNAME ProgramFiles ComSpec APPDATA );
The issue is still seen with system() though. I tried Sinan's suggestion in the answerers..
Child processes inherit the environment of their parent. The bat file should have the same environment as the Perl script. If you haven't setup the environment correctly, your bat file won't see it.
Can you expand your answer to show your test case, similar to what Sinan has done? Although you say in your comment that the code is simple, that is never true. Show us the complete Perl program you have and the complete bat file that you have. Don't make us guess or do a lot of work to figure out what you are doing.
Update: Now that you've provided some code, start cutting parts out of it to make the simplest test case that still shows the problem. When you are investigating these sorts of problems, stop working on the big script and write a little script that exercises the problem you think you're having. That way, you isolate it from everything else you might be messing up.
Update 2: Let's look more closely at your system call:
system(\"start $ENV{'IPSL_TESTCASES_PATH'}SIP\\run_$file_idx.bat\");
That should be syntax error and your script should not even be able to invoke the batch file. Try
system(qq{start $ENV{IPSL_TESTCASES_PATH}SIP\\run_$file_idx.bat\\});
Update 1: Looking at the code you posted, you are using start in the system call. That starts a new cmd.exe process and that cmd.exe will remain long after your script has terminated. However, I still do not observe behavior as you describe and I am going to look at your code dump more closely.
C:\Temp> cat bg.bat
#echo %YAEV%
#echo %*
C:\Temp> cat t.pl
#!/usr/local/bin/perl
use strict;
use warnings;
$ENV{YAEV} = 'Yet another environment variable';
system 'start bg.bat "This is a test"';
In a new cmd.exe window:
C:\Temp> t
Yet another environment variable
"This is a test"
The system command [contested: /function] will start your batch script in the default batch file context (which is the system-wide environment), if your parameter to the system call is a scalar. It is basically the same as starting the command using "cmd /c myfile.bat". If your argument to system() is an array [contested: /list], you will not have this problem. Please read this for more information.