I have very long string which I copy and paste from external program to PowerShell.
After splitting it (
$variable=$variable.split("`n")
) I received array from which i Want remove every third element.
What is most convenient way of accomplishing it?
I thought about loop from
0 to $variable.lenght()-1
and check if i can be divided by three, but maybe there is other way?
If you need to remove values any 3 positions (0-based: 2,5,8,11,14 and so on) in the array use something like this:
$newArray = #()
0..($variable.length) | % {
if ((($_+1) % 3 ) -ne 0) {
$newArray += $variable[$_]
}
}
$new=for ($i=2;$i -lt $array.count;$i+=3) {$array[$i]}
This will start at the 3rd element and get every third. Pipelined output is saved to $new.
$i = 0
$variable = $variable.split("`n") | ? {++$i % 3}
$new_variable = $variable | foreach {$i=1} {if ($i++ %3){$_}}
Or
0..($variable.count-1) | foreach { if($_%3) {$variable[$_]} }
Related
We have a text file of students and their notes and we have to count how many "1" notes all the students have got.
My code shows how many lines contain the "1" note, but when it finds a "1", it jumps to the next line.
Could you help me please?
for example:
Huckleberry Finn 2 1 4 1 1
Tom Sawyer 3 2 1 4 1
It should be 5, but it gets 2.
$ones = 0
$file= Get-Content notes.txt
foreach ($i in $file) {
if ($i.Split(' ') -eq 1){
$ones ++
}
}
$ones
If all the 1 tokens are whitespace-separated in your input file, as the sample content suggests, try:
# With your sample file content, $ones will receive the number 5.
$ones = (-split (Get-Content -Raw notes.txt) -eq '1').Count
The above uses the unary form of -split, the string splitting operator and the -Raw switch of the Get-Content cmdlet, which loads a file into memory as a single, multi-line string.
That is, the command above splits the entire file into white-space separated tokens, and counts all tokens that equal '1', across all lines.
If, instead, you meant to count the number of '1' tokens per line, use the following approach:
# With your sample file content, $ones will receive the following *array*:
# 3, 2
$ones = Get-Content notes.txt | ForEach-Object { ((-split $_) -eq '1').Count }
As for what you tried:
if ($i.Split(' ') -eq 1)
While $i.Split(' ') does return all (single-)space-separated tokens contained in a single input line stored in $i, using that expression in a conditional expression of an if statement only results in one invocation of the associated { ... } block and therefore increments $ones only by a value of 1, not the number of 1 tokens in the line at hand.
Solved!
Thank you mklement0!
I don't understand why, but it works so:
$ones = 0
$file= Get-Content notes.txt
foreach ($i in $file) {
$ones=(-split ($i) -eq '1').Count
}
}
$ones
I would like to search values after a specific word (Current Value = ) in a log file, and makes a string with values.
vcs_output.log: a log file
** Fault injection **
Count = 1533
0: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[6]
0: Current value = x
1: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[51]
1: Current value = x
2: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[3]
2: Current value = 1
3: Path = cmp_top.iop.sparc0.exu.alu.shft_alu_shift_out_e[18]
3: Current value = 0
4: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[17]
4: Current value = x
5: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[43]
5: Current value = 0
6: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[38]
6: Current value = x
7: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs2_data_e_l[30]
7: Current value = 1
.
.
.
If I store values after "Current value = ", then x,x,1,0,x,0,x,1. I ultimately save/print them as a string such as xx10x0x1.
Here is my code
code.pl:
#!/usr/bin/perl
use strict;
use warnings;
##### Read input
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!;
chomp ( my #input = <$input_fh> );
my $i=0;
my #arr;
while (#input) {
if (/Current value = /)
$arr[i]= $input; # put the matched value to array
}
}
## make a string from the array using an additional loop
close ( $input_fh );
I think there is a way to make a string in one loop (or even not using a loop). Please advise me to make it. Any suggestion is appreciated.
You can do both that you ask for.
To build a string directly, just append to it what you capture in the regex
my $string;
while (<$input_fh>)
{
my ($val) = /Current\s*value\s*=\s*(.*)/;
$string .= $val;
}
If the match fails then $val is an empty string, so we don't have to test. You can also write the whole while loop in one line
$string .= (/Current\s*value\s*=\s*(.*)/)[0] while <$input_fh>;
but I don't see why that would be necessary. Note that this reads from the filehandle, and line by line. There is no reason to first read all lines into an array.
To avoid (explicit) looping, you can read all lines and pass them through map, naively as
my $string = join '',
map { (/Current\s*value\s*=\s*(.*)/) ? $1 : () } <$input_fh>;
Since map needs a list, the filehandle is in list context, returning the list of all lines in the file. Then each is processed by code in map's block, and its output list is then joined.
The trick map { ($test) ? $val : () } uses map to also do grep's job, to filter -- the empty list that is returned if $test fails is flattened into the output list, thus disappearing. The "test" here is the regex match, which in the scalar context returns true/false, while the capture sets $1.
But, like above, we can return the first element of the list that match returns, instead of testing whether the match was successful. And since we are in map we can in fact return the "whole" list
my $string = join '',
map { /Current\s*value\s*=\s*(.*)/ } <$input_fh>;
what may be clearer here.
Comments on the code in the question
the while (#input) is an infinite loop, since #input never gets depleted. You'd need foreach (#input) -- but better just read the filehandle, while (<$input_fh>)
your regex does match on a line with that string, but it doesn't attempt to match the pattern that you need (what follows =). Once you add that, it need be captured as well, by ()
you can assign to the i-th element (which should be $i) but then you'd have to increment $i as you go. Most of the time it is better to just push #array, $value
You can use capturing parentheses to grab the string you want:
use strict;
use warnings;
my #arr;
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!;
while (<$input_fh>) {
if (/Current value = (.)/) {
push #arr, $1;
}
}
close ( $input_fh );
print "#arr\n";
__END__
x x 1 0 x 0 x 1
Use grep and perlre
http://perldoc.perl.org/functions/grep.html
http://perldoc.perl.org/perlre.html
If on a non-Unix environment then...
#!/usr/bin/perl -w
use strict;
open (my $fh, '<', "vcs_output.log");
chomp (my #lines = <$fh>);
# Filter for lines which contain string 'Current value'
#lines = grep{/Current value/} #lines;
# Substitute out what we don't want... leaving us with the 'xx10x0x1'
#lines = map { $_ =~ s/.*Current value = //;$_} #lines;
my $str = join('', #lines);
print $str;
Otherwise...
#!/usr/bin/perl -w
use strict;
my $output = `grep "Current value" vcs_output.log | sed 's/.*Current value = //'`;
$output =~ s/\n//g;
print $output;
So, I am trying to get information out of OS X's "favorite servers" .plist so that I can then decide whether or not I want to add certain servers to it. Some of the information for how this can be done can be found here:
http://jacobsalmela.com/bash-script-set-favorite-servers-in-connect-to-menu/
The problem with this is that you can't, for example, just do
/usr/libexec/Plistbuddy -c "Add favoriteservers:CustomListItems:0:Name string server1.fqdn.com" com.apple.sidebarlists.plist
over and over, because Plistbuddy is not smart enough to do an insert into the array. You have to know how long the array is, and then add things to the end of it, such that when you go to add things you have already determined whether you need to use 0 or 5 or 7 between "CustomListItems" and "Name" up there.
The obnoxiousness of that aside, I'm having trouble just parsing the output from the Plistbuddy print command, which looks like this:
Array { Dict { Name = afp://or-fs-001/vol1 URL = afp://or-fs-001/vol1 } Dict { Name = smb://or-fs-001/vol1 URL = smb://or-fs-001/vol1 } Dict { Name = vnc://or-fs-001/vol1 URL = vnc://or-fs-001/vol1 } Dict { Name = ftp://or-fs-001/vol1 URL = ftp://or-fs-001/vol1 } }
So you have the same URL twice for each entry (I have no idea why there is both a "Name" and "URL" when you can't actually make them different), and they may start with any protocol supported by Finder, which means afp, http, https, smb, or vnc. The first thing I'm trying to do is just split them up into pieces by the "Name" substring so that I know how many entries are in the list, but that results in weird behavior when I use tr for that; it starts cutting out way too many pieces.
Does anyone have ideas for better ways to do this? Can I count the number of times "Dict" shows up?
You can use grep -o to extract interesting parts of the input. An example:
#!/bin/bash
output='Array { Dict { Name = afp://or-fs-001/vol1 URL = afp://or-fs-001/vol1 } Dict { Name = smb://or-fs-001/vol1 URL = smb://or-fs-001/vol1 } Dict { Name = vnc://or-fs-001/vol1 URL = vnc://or-fs-001/vol1 } Dict { Name = ftp://or-fs-001/vol1 URL = ftp://or-fs-001/vol1 } }'
count=$(echo "$output" | grep -o 'Name =' | wc -l)
names=($(grep -o 'Name = [^ ]\+' <<< "$output" | cut -f3- -d' '))
echo $count = ${#names[#]}
for name in "${names[#]}" ; do
echo "$name"
done
I am very new to Perl, and I am trying to write a word frequency counter as a learning exercise.
However, I am not able to figure out the error in my code below, after working on it. This is my code:
$wa = "A word frequency counter.";
#wordArray = split("",$wa);
$num = length($wa);
$word = "";
$flag = 1; # 0 if previous character was an alphabet and 1 if it was a blank.
%wordCount = ("null" => 0);
if ($num == -1) {
print "There are no words.\n";
} else {
print "$length";
for $i (0 .. $num) {
if(($wordArray[$i]!=' ') && ($flag==1)) { # start of a new word.
print "here";
$word = $wordArray[$i];
$flag = 0;
} elsif ($wordArray[$i]!=' ' && $flag==0) { # continuation of a word.
$word = $word . $wordArray[$i];
} elsif ($wordArray[$i]==' '&& $flag==0) { # end of a word.
$word = $word . $wordArray[$i];
$flag = 1;
$wordCount{$word}++;
print "\nword: $word";
} elsif ($wordArray[$i]==" " && $flag==1) { # series of blanks.
# do nothing.
}
}
for $i (keys %wordCount) {
print " \nword: $i - count: $wordCount{$i} ";
}
}
It's neither printing "here", nor the words. I am not worried about optimization at this point, though any input in that direction would also be much appreciated.
This is a good example of a problem where Perl will help you work out what's wrong if you just ask it for help. Get used to always adding the lines:
use strict;
use warnings;
to the top of your Perl programs.
Fist off,
$wordArray[$i]!=' '
should be
$wordArray[$i] ne ' '
according to the Perl documentation for comparing strings and characters. Basically use numeric operators (==, >=, …) for numbers, and string operators for text (eq, ne, lt, …).
Also, you could do
#wordArray = split(" ",$wa);
instead of
#wordArray = split("",$wa);
and then #wordArray wouldn't need to do the wonky character checking and you never would have had the problem. #wordArray will be split into the words already and you'll just have to count the occurrences.
You seem to be writing C in Perl. The difference is not just one of style. By exploding a string into a an array of individual characters, you cause the memory footprint of your script to explode as well.
Also, you need to think about what constitutes a word. Below, I am not suggesting that any \w+ is a word, rather pointing out the difference between \S+ and \w+.
#!/usr/bin/env perl
use strict; use warnings;
use YAML;
my $src = '$wa = "A word frequency counter.";';
print Dump count_words(\$src, 'w');
print Dump count_words(\$src, 'S');
sub count_words {
my $src = shift;
my $class = sprintf '\%s+', shift;
my %counts;
while ($$src =~ /(?<sequence> $class)/gx) {
$counts{ $+{sequence} } += 1;
}
return \%counts;
}
Output:
---
A: 1
counter: 1
frequency: 1
wa: 1
word: 1
---
'"A': 1
$wa: 1
=: 1
counter.";: 1
frequency: 1
word: 1
I'm doing some script in Powershell to automate a task. This script is going to get arguments, such as:
PS > myscript.ps1 par=a,1,2,0.1 par=b,3,4,0.1 par=c,5,6,0.1 bin=file.exe exeargs="fixed args for file.exe"
In short, file.exe is an executable which accept parameters (including a, b and c) and this ps1 script is going to execute file.exe passing args a, b and c within the specified range, varying 'em by the specified precision.
The question is, I first split each $arg in $args by the character "=", and then I should split them by "," to get the specified values.
The thing is, when I do:
foreach ($arg in $args)
{
$parts = ([string] $arg).split("=")
Write-Host $parts[1]
}
The output is
a 1 2 0.1
b 3 4 0.1
c 5 6 0.1
file.exe
fixed args for file.exe
I.e., it already substituted the "," character with a whitespace, so my second split should be with white space, not with comma.
Any guess on why does it happen?
Thanks in advance!
First of all why are you writing it like a C program or something? You don't have to pass arguments like that, use $args and split on = etc. when Powershell has a more powerful concept of parameters, whereby you can pass the named paramters and arguments rather than doing the parsing that you are doing. ( More on parameters here: http://technet.microsoft.com/en-us/library/dd315296.aspx)
With that said, let me answer your question:
What you are doing is when you pass in arguments like:
par=a,1,2,0.1 par=b,3,4,0.1 par=c,5,6,0.1 bin=file.exe exeargs="fixed args for file.exe"
you are passing in array of arrays. The first element is the array with elements:
par=a
1
2
0.1
Ok coming to the split:
When you do [string] $a, you are converting the array into a string. By default this means an array with elements 1,2,3 will become 1 2 3.
So your first argument there par=a,1,2,0.1, becomes par=a 1 2 0.1 and the split on = means parts[1] becomes a 1 2 0.1, which is what you see.
So how can you retain the comma?
Just make an array to be converted into a string with , inbetween than space, right?
Here are some ways to do that:
Using -join operator:
foreach ($arg in $args)
{
$parts = ($arg -join ",").split("=")
Write-Host $parts[1]
}
now you will see the output with the commas that you want.
Another way, using $ofs special variable:
foreach ($arg in $args)
{
$ofs =","
$parts = ([string] $arg).split("=")
Write-Host $parts[1]
}
(more on $ofs here: http://blogs.msdn.com/b/powershell/archive/2006/07/15/what-is-ofs.aspx )
Disclaimer - All this explanation to make you understand what is happening. Please do not continue this and use paramters.
This is happening in the parsing of the command line for your script and not during the split() method. To see this, try putting a "Write-Host $args" at the beginning, like so:
Write-Host $args
foreach ($arg in $args)
{
$parts = ([string] $arg).split("=")
Write-Host $parts[1]
}
This is because the ',' character is used to separate elements in an array. For example:
PS C:\temp> $a = 1,2,3
PS C:\temp> Write-Host $a
1 2 3
PS C:\temp> $a.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Try this command line instead:
.\myscript.ps1 "par=a,1,2,0.1" "par=b,3,4,0.1" "par=c,5,6,0.1" "bin=file.exe" "exeargs=fixed args for file.exe"