Script (originally copied from here) takes a fixed-width text file as input, rearranges the order of columns, and should output a fixed-width text file. But trailing spaces are being truncated from the variables, which means the output isn't fixed-width.
open(INPUT, "</home/ecom/tmp/citiBIG/GROUP.txt");
open(OUTPUT, ">/home/ecom/tmp/citiBIG/GROUP2.txt");
my $LINEFORMAT = "A2 A7 A14 A4 A2 A2 A4 A12 A25 A30 A26 A40 A40 A40 A25 A4 A12 A14 A2 A8 A12 A70 A8"; # Adjust to your
field widths
while(<INPUT>) {
chomp;
my($Null0, $EmpNum, $CcNumber, $Null1, $CcExpYy, $CcExpMm, $Null2, $Title, $LastName, $FirstName, $HolderName, $Ad
dress1, $Address2, $Address3, $Suburb, $State, $PostCode, $Null3, $AreaCode, $WorkPhone, $Null4, $Email, $GroupName) =
unpack($LINEFORMAT, $_);
print OUTPUT $EmpNum . " " . "~" . $LastName . "~" . $FirstName . "~" . $Title . " " . "~" .
$Address1 . "~" . $Address2 . "~" . $Address3 . "~" . $Suburb . "~" . $PostCode . "~" . $State . "~" . $AreaCode . "~"
. $WorkPhone . "~" . $CcNumber . "~" . $CcExpMm . "~" . $CcExpYy . "~" . $HolderName . "~" . $Email . "~" . $GroupNam
e . " " . "~" . "\n";
}
close INPUT;
close OUTPUT;
perldoc -f pack suggests:
o The "a", "A", and "Z" types gobble just one value, but pack
it as a string of length count, padding with nulls or
spaces as needed. When unpacking, "A" strips trailing
whitespace and nulls, "Z" strips everything after the first
null, and "a" returns data without any sort of trimming.
Maybe you could try "a" instead of "A" in the format string? Alternatively you could use printf to pad the output fields to the desired widths.
Related
I log network status (nslookup, ping, tracert) into a log file. The log file size grows and is quite difficult to work with after a while.
I am looking for a way to have a new log file created for every day. I am sure it's easy but I did not find a way.
When I set log file name to $log_file = ".\network_" + (Get-Date -f yyyy-MM-dd) + ".log" hoping it will create a new file when date changes, it does not work. Instead I get Non-authoritative answer:.
Sorry for newbie question but I really did not find any answer. Thx!
edit:
it's really simple script (shortened example below):
$log_file = ".\network_" + (Get-Date -f yyyy-MM-dd_HH-mm) + ".log"
$server = "server.com"
$gateway = (Get-wmiObject Win32_networkAdapterConfiguration | ?{$_.IPEnabled}).DefaultIPGateway
& ipconfig /all >> $log_file
while($true) {
$timestamp = "rnrn[" + (Get-Date -f yyyy-MM-dd) + " " +(Get-Date -f HH:mm:ss) + "]"
$timestamp >> $log_file
"rnrn" >> $log_file
"ping to $server)" >> $log_file
& ping $server >> $log_file
}
Here's what I usually do.
$Date = get-date -format yyyy-MM-dd
$log_file = "\\Share\folder\folder\FileName-$date.log"
I would suggest including the date/time (yyyy-MM-dd_HH-mm-ss) in the log file name (to prevent duplicate file names) and scheduling your script to run daily / hourly or whenever. Just tell the task scheduler service to end the task if it runs for X number of hours, where X is right before it is scheduled to start again. This should give you the intended result of a new file on the schedule you decide is best. This also ensures your script continues running even if the computer reboots.
If the size of the log file is your main issue, then I would suggest starting a new file based on the file size and not the date. This way you can choose exactly what size files you want to work with.
To do this, just change while($true) to while((Get-Item $log_file).Length -lt "100000") where the length is the size in bytes that you want the script to stop at.
To make your script create a new log file when the while statement is triggered, just wrap it in a function and then call the function using another while $true statement.
Here is the change:
function NetworkLogging {
$log_file = "$PSScriptRoot\network_" + (Get-Date -f yyyy-MM-dd_HH-mm-ss) + ".log"
$server = "server.com"
$gateway = (Get-wmiObject Win32_networkAdapterConfiguration | ?{$_.IPEnabled}).DefaultIPGateway
& ipconfig /all >> $log_file
while((Get-Item $log_file).Length -lt "100000") {
$timestamp = "rnrn[" + (Get-Date -f yyyy-MM-dd) + " " +(Get-Date -f HH:mm:ss) + "]"
$timestamp >> $log_file
"rnrn" >> $log_file
"ping to $server)" >> $log_file
& ping $server >> $log_file
}
}
while ($true){NetworkLogging}
You might consider the Log-Entry framework (also on GitHub) I published awhile ago:
It has basically all the features were you ask for:
File is automatically truncated if it grows over ~100Kb (default)
Time stamps
Inline logging
Proper type casting (e.g. notice that the $gateway has also a $Null property in my case)
Function Main {
Log -File ".\Network.log"
$server = Log "Server:" "192.168.1.1" ?
$gateway = Log "Gateway:" (Get-wmiObject Win32_networkAdapterConfiguration | ?{$_.IPEnabled}).DefaultIPGateway ?
Log "IP Config:" ((& ipconfig /all) -Join "`r`n")
Log "Ping to $Server" ((& ping $server) -Join "`r`n")
}
The log will look like this:
2017-06-08 Test (version: 01.00.02, PowerShell version: 5.X.X5063.296)
09:13:14.01 C:\Users\User\Network.ps1
15:28:14.67 Server: 192.168.X.X
15:28:14.75 Gateway: #($Null, "192.168.X.X")
15:28:14.78 IP Config: Windows IP Configuration
Host Name . . . . . . . . . . . . : Computer
Primary Dns Suffix . . . . . . . :
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
DNS Suffix Search List. . . . . . : lan
Ethernet adapter Ethernet:
Connection-specific DNS Suffix . : lan
Description . . . . . . . . . . . : Intel(R) 82579LM Gigabit Network Connection
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : XXXX::XXXX:XXXX:XXXX:XXXX%8(Preferred)
IPv4 Address. . . . . . . . . . . : 192.168.X.X(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Lease Obtained. . . . . . . . . . : Thursday, June 8, 2017 9:08:58 AM
Lease Expires . . . . . . . . . . : Friday, June 9, 2017 9:08:57 AM
Default Gateway . . . . . . . . . : 192.168.X.X
DHCP Server . . . . . . . . . . . : 192.168.X.X
DHCPv6 IAID . . . . . . . . . . . : 9808.X.X
DHCPv6 Client DUID. . . . . . . . : XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX
DNS Servers . . . . . . . . . . . : 192.168.X.X
NetBIOS over Tcpip. . . . . . . . : Enabled
Ethernet adapter VirtualBox Host-Only Network:
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : VirtualBox Host-Only Ethernet Adapter
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : XXXX::XXXX:XXXX:XXXX:XXXX%5(Preferred)
IPv4 Address. . . . . . . . . . . : 192.168.X.X(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
DHCPv6 IAID . . . . . . . . . . . : 420085799
DHCPv6 Client DUID. . . . . . . . : XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX-XX
DNS Servers . . . . . . . . . . . : XXXX:0:0:XXXX:.X.X
XXXX:0:0:XXXX::2%1
XXXX:0:0:XXXX::3%1
NetBIOS over Tcpip. . . . . . . . : Enabled
Wireless LAN adapter Wi-Fi:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . : lan
Description . . . . . . . . . . . : Intel(R) Centrino(R) AdvancXX-N 6235
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Wireless LAN adapter Local Area Connection* 2:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Ethernet adapter Bluetooth Network Connection:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Bluetooth Device (Personal Area Network)
Physical Address. . . . . . . . . : XX-XX-XX-XX-XX-XX
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
15:28:17.82 Ping to 192.168.X.X Pinging 192.168.X.X with 32 bytes of data:
Reply from 192.168.X.X: bytes=32 time<1ms TTL=64
Reply from 192.168.X.X: bytes=32 time=1ms TTL=64
Reply from 192.168.X.X: bytes=32 time<1ms TTL=64
Reply from 192.168.X.X: bytes=32 time<1ms TTL=64
Ping statistics for 192.168.X.X:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 1ms, Average = 0ms
15:28:17.84 End
I have a hash. The value for the key in hash is concatenation of 2 strings. ($value1 and $value2)
When I print the value of concatenated variable $values it prints with the newline.
But when I am printing it in html table format the newline doesn't exist. How can I have the newline retained.
my $value1 = "Good files are 272 :10%";
my $value2 = "Bad files are 300 : 15%";
my $values = $value1 . $value2;
print "values : $values ";
# HASH with multiple values for each key
my %hash ;
$keyvalue = "foobar";
$hash{$keyvalue} = $values ;
# SET UP THE TABLE
print "<table border='1'>";
print "<th>Category</th><th>value</th>";
#Print key and values in hash in tabular format
foreach $key (sort keys %hash) {
print "<tr><td>".$key."</td>";
print "<td>".$hash{$key}."</td>";
}
* Current Output: *
It prints the hash values without newline
values : Good files are 272 :10%
Bad files are 300 : 15%
Category Value
foobar Good files are 272 :10%Bad files are 300 : 15%
* Desired Output: *
Category Value
foobar Good files are 272 :10%
Bad files are 300 : 15%
In HTML, you use the <br> element to create a new line. Therefore, just do a search and replace -- replace any line breaks in your input with <br>.
Since you're concatenating $value1 and $value2, you can do this:
$values = $value1 . '<br>' . $value2;
Playing around with PS and I have a simple script.
ipconfig /all | where-object {$_ -match "IPv4" -or $_ -match "Description"}
This is great and does what i would expect. What I would like to do is read ahead and only show the description preceding the IPv4 line. Or reverse search and get the ipv4 and the next description then look for the next IPv4 etc.
Is there a way to do this without spinning through creating an array and then spinning through the array extricating the meaningful parts?
This command on my laptop results in:
Description . . . . . . . . . . . : Microsoft Virtual WiFi Miniport Adapter
Description . . . . . . . . . . . : Killer Wireless-N 1103 Network Adapter
IPv4 Address. . . . . . . . . . . : 192.168.1.2(Preferred)
Description . . . . . . . . . . . : Atheros AR8151 PCI-E Gigabit Ethernet Controller (NDIS 6.20)
Description . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
IPv4 Address. . . . . . . . . . . : 192.168.122.1(Preferred)
Description . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
IPv4 Address. . . . . . . . . . . : 192.168.88.1(Preferred)
Description . . . . . . . . . . . : Microsoft ISATAP Adapter
Description . . . . . . . . . . . : Microsoft ISATAP Adapter #2
Description . . . . . . . . . . . : Microsoft ISATAP Adapter #3
Description . . . . . . . . . . . : Teredo Tunneling Pseudo-Interface
Description . . . . . . . . . . . : Microsoft ISATAP Adapter #4
Description . . . . . . . . . . . : Microsoft ISATAP Adapter #5
What I want is:
Description . . . . . . . . . . . : Killer Wireless-N 1103 Network Adapter
IPv4 Address. . . . . . . . . . . : 192.168.1.2(Preferred)
Description . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
IPv4 Address. . . . . . . . . . . : 192.168.122.1(Preferred)
Description . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
IPv4 Address. . . . . . . . . . . : 192.168.88.1(Preferred)
If you want to extract all Descriptions for IPv4 enabled adapters, you could try something like this:
ipconfig /all | Select-String "IPv4" -AllMatches -SimpleMatch -Context 5 | % {
$_.Context.Precontext -match "Description" -replace 'Description(?:[^:]+):(.*)$', '$1'
}
Intel(R) 82579V Gigabit Network Connection
To get it with your code, try this:
ipconfig /all | where-object {
$_ -match "IPv4" -or $_ -match "Description"
} | Select-String "IPv4" -SimpleMatch -AllMatches -Context 1 | % {
$_.context.precontext -replace 'Description(?:[^:]+):(.*)$', '$1'
}
EDIT Sorry, I misread your question earlier it seems. I thought you only wanted the description. This shows the description and IP lines for IPv4 active adapters
ipconfig /all | Select-String "IPv4" -AllMatches -SimpleMatch -Context 5 | % {
$_.Context.Precontext -match "Description"
$_.Line
}
Description . . . . . . . . . . . : Intel(R) 82579V Gigabit Network Connection
IPv4 Address. . . . . . . . . . . : xx.xx.xx.xx(Preferred)
Alternative solution:
[regex]$regex = '(?ms)^\s*(Description[^\r]+\r\n\s*IPv4[^\r]+)\r'
$regex.matches(((ipconfig /all) -match '^\s*Description|IPv4') -join "`r`n") |
foreach {$_.groups[1].value -replace '\. ',''}
Another option, which simply keeps track of the last description found in the output:
switch -regex ( ipconfig /all ) { 'IPv4' { $d + $_ } 'Description' { $d = #($_) } }
Also, the -match comparison operator can work on an array as well as a single string. So using (ipconfig /all) -match 'IPv4|Description' is equivalent to the original ipconfig /all | where { $_ -match 'IPv4' -or $_ -match 'Description' } that checks each line individually.
Perl and I disagree as to whether a variable is a number or not. I'm wrong, of course, but why?
my $bytes = 0;
# . . .
$bytes = $bytes + length($text);
# . . .
my $MB = $bytes / 1048576;
my $string = sprintf("Downloaded: %.1f MB\n", MB);
gives Argument "MB" isn't numeric in sprintf at foo.pl line 200..
It is, in fact, numeric as can be seen when I use
my $string = "Downloaded: $MB MB\n";
which sets the string to Downloaded: 3.09680080413818 MB.
Edit: Ah, silly mistake, thanks for catching it.
You need the variable sigil:
my $bytes = 0;
# . . .
$bytes = $bytes + length($text);
# . . .
my $MB = $bytes / 1048576;
my $string = sprintf("Downloaded: %.1f MB\n", $MB); # <--- note the $MB at the end
Looks like you probably meant:
my $string = sprintf("Downloaded: %.1f MB\n", $MB);
other than that this should work.
Suppose the call was
/usr/local/bin/perl verify.pl 1 3 de# > result.log
Inside verify.pl I want to capture the whole call above and append it to a log file for tracking purposes.
How can I capture the whole call as it is?
$0 has the script name and #ARGV has the arguments, so the whole commandline is:
$commandline = $0 . " ". (join " ", #ARGV);
or, more elegantly (thanks FMc):
$commandline = join " ", $0, #ARGV;
I don't however, know how to capture the redirection (> result.log)
There is way (at least on unix-systems) to get whole command line:
my $cmdline = `ps -o args -C perl | grep verify.pl`;
print $cmdline, "\n";
e: Cleaner way using PID (courtesy of Nathan Fellman):
print qx/ps -o args $$/;
$commandline = join " ", $0, #ARGV; does not handle the case that command line has quotes such as ./xxx.pl --love "dad and mom"
A Quick Solution:
my $script_command = $0;
foreach (#ARGV) {
$script_command .= /\s/ ? " \'" . $_ . "\'"
: " " . $_;
}
Try to save the following code as xxx.pl and run ./xxx.pl --love "dad and mom":
#!/usr/bin/env perl -w
use strict;
use feature qw ( say );
say "A: " . join( " ", $0, #ARGV );
my $script_command = $0;
foreach (#ARGV) {
$script_command .= /\s/ ? " \'" . $_ . "\'"
: " " . $_;
}
say "B: " . $script_command;
Here virtually same Linux-only variants (of course, after shell intervention):
pure perl
BEGIN {
my #cmd = ( );
if (open(my $h, "<:raw", "/proc/$$/cmdline")) {
# precisely, buffer size must be at least `getconf ARG_MAX`
read($h, my $buf, 1048576); close($h);
#cmd = split(/\0/s, $buf);
};
print join("\n\t", #cmd), "\n";
};
using File::Slurp:
BEGIN {
use File::Slurp;
my #cmd = split(/\0/s, File::Slurp::read_file("/proc/$$/cmdline", {binmode => ":raw"}));
print join("\n\t", #cmd), "\n";
};
See In Perl, how do I get the directory or path of the current executing code?
Example which handles more special cases (and without adding own name) and with processing the line with GetOpt:
#!perl
use strict;
use warnings;
use Getopt::Long qw(GetOptionsFromString);
use feature qw ( say );
# Note: $0 is own name
my $sScriptCommandLine;
my #asArgs = ();
my $iRet;
my $sFile = '';
my $iN = -1;
my $iVerbose = 0;
# ============================================================================
my %OptionsHash = (
"f=s" => \$sFile,
"n=i" => \$iN,
"v:+" => \$iVerbose);
$sScriptCommandLine = join( ' ', #ARGV ); # useless for argument with spaces
say 'A: ' . $sScriptCommandLine;
$sScriptCommandLine = '"' . join( '" "', #ARGV ) . '"'; # all arguments in "", but not suitable for arguments with '"' (given as '\"')
say 'B: ' . $sScriptCommandLine;
$sScriptCommandLine = '';
foreach (#ARGV) {
$sScriptCommandLine .= ' ' if ($sScriptCommandLine);
$_ =~ s/\\/\\\\/g; # needed for GetOptionsFromString
$_ =~ s/\"/\\\"/g;
if (/\s/) {
$sScriptCommandLine .= '"'.$_.'"';
}
else {
$sScriptCommandLine .= $_;
}
}
say 'C: ' . $sScriptCommandLine;
my ($iRet,$paArgs);
($iRet,$paArgs) = GetOptionsFromString($sScriptCommandLine,%OptionsHash);
# remaining parameters in $$paArgs[0] etc.
if (!$iRet) {
# error message already printed from GetOptionsFromString
print "Invalid parameter(s) in: \"$sScriptCommandLine\"\n";
}
say 'D: ' . '<<' . join( '>> <<', #{$paArgs} ) . '>>';
say 'f=s: "'.$sFile.'"';
say 'n=i: '.$iN;
say 'v:+: '.$iVerbose;
# eof