How to search for a not equals value on a multi line nawk output - nawk

I have this current solution for CVS status managment:-
cvs -q status|awk 'c-->0;$0~s{if(b)for(c=b+1;c>1;c--)print r[(NR-c+1)%b];print;c=a}b{r[NR%b]=$0}' b=1 a=9 s='(Locally Modified)|(Needs Patch)'
This gives me a display of Locally Modified files and files that need patching, which is great.
However a better solution for me that would catch all status is when the status is not equal to 'Up-to-date'.
I have tried s!= and s<> but it only seems to allow =.

A little whitespace will go a long way...
The opposite of $0 ~ s is $0 !~ s, so
cvs -q status | awk '
c-- > 0
$0 !~ s {
if (b)
for (c=b+1; c>1; c--)
print r[(NR-c+1)%b]
print
c=a
}
b {r[NR%b]=$0}
' b=1 a=9 s='Up-to-date'

Related

Counting specific lines that don't contain specific word

Please I have question: I have a file like this
#HWI-ST273:296:C0EFRACXX:2:2101:17125:145325/1
TTAATACACCCAACCAGAAGTTAGCTCCTTCACTTTCAGCTAAATAAAAG
+
8?8A;DDDD;#?++8A?;C;F92+2A#19:1*1?DDDECDE?B4:BDEEI
#BBBB-ST273:296:C0EFRACXX:2:1303:5281:183410/1
TAGCTCCTTCGCTTTCAGCTAAATAAAAGCCCAGTACTTCTTTTTTACCA
+
CCBFFFFFFHHHHJJJJJJJJJIIJJJJJJJJJJJJJJJJJJJIJJJJJI
#HWI-ST273:296:C0EFRACXX:2:1103:16617:140195/1
AAGTTAGCTCCTTCGCTTTCAGCTAAATAAAAGCCCAGTACTTCTTTTTT
+
#C#FF?EDGFDHH#HGHIIGEGIIIIIEDIIGIIIGHHHIIIIIIIIIII
#HWI-ST273:296:C0EFRACXX:2:1207:14316:145263/1
AATACACCCAACCAGAAGTTAGCTCCTTCGCTTTCAGCTAAATAAAAGCC
+
CCCFFFFFHHHHHJJJJJJJIJJJJJJJJJJJJJJJJJJJJJJJJJJJIJ
I
I'm interested just about the line that starts with '#HWI', but I want to count all the lines that are not starting with '#HWI'. In the example shown, the result will be 1 because there's one line that starts with '#BBB'.
To be more clear: I just want to know know the number of the first line of the patterns (that are 4 line that repeated) that are not '#HWI'; I hope I'm clear enough. Please tell me if you need more clarification
With GNU sed, you can use its extended address to print every fourth line, then use grep to count the ones that don't start with #HWI:
sed -n '1~4p' file.fastq | grep -cv '^#HWI'
Otherwise, you can use e.g. Perl
perl -ne 'print if 1 == $. % 4' -- file.fastq | grep -cv '^#HWI'
$. contains the current line number, % is the modulo operator.
But once we're running Perl, we don't need grep anymore:
perl -lne '++$c if 1 == $. % 4; END { print $c }' -- file.fastq
-l removes newlines from input and adds them to output.

grep a lot of data in the same file

I want a command that can match all the below criteria in Red Hat:
·number range between 0100xxxx to 0110xxxxx
·And have money over 300
·Status either X or Z
·id contains letter ‘a’
·Error_code starting with 2
number,money,status,error-code,id
010018739,13213,X,300,abcde
010523456,343,Z,500,xcvfe
010743576,563,X,201,fgsa
012095654,300,X,400,gcaz
019432343,300,X,402,dewa
011023324,200,X,206,dea
020023433,100,X,303,a
010832134,300,X,200,a
012244242,433,Z,204,ghfsa
Something like this:
awk -F, '($1>=1000000 && $1<11099999) && $2>300 && ($3 ~ "X" || $3 ~ "Z") && index($5,"a") && index($4,"2")==1' file
It doesn't cater for the status being lower-case (but you didn't ask for that), nor does it cater for there being spaces in front of the status or error code (but you didn't ask for that either).
grep only matches text, awk is much more flexible and should fit your case better. For instance:
awk 'BEGIN {FS=","} $2 > 300 {print;}' < yourfile
Basically this is saying that ',' is the field separator, and then for every line where the second field ($2) is > 300, the action (in this case just print the whole line, which could even be omitted IIRC) is executed.
You can have conditions as complex as you like, with a syntax that is similar to C. I would suggest reading man awk and googling for more complex examples, but you should get the idea.

How to horizontally mirror ascii art?

So ... I know that I can reverse the order of lines in a file using tac or a few other tools, but how do I reorder in the other dimension, i.e. horizontally? I'm trying to do it with the following awk script:
{
out="";
for(i=length($0);i>0;i--) {
out=out substr($0,i,1)}
print out;
}
This seems to reverse the characters, but it's garbled, and I'm not seeing why. What am I missing?
I'm doing this in awk, but is there something better? sed, perhaps?
Here's an example. Input data looks like this:
$ cowsay <<<"hello"
_______
< hello >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
And the output looks like this:
$ cowsay <<<"hello" | rev
_______
> olleh <
-------
^__^ \
_______\)oo( \
\/\) \)__(
| w----||
|| ||
Note that the output is identical whether I use rev or my own awk script. As you can see, things ARE reversed, but ... it's mangled.
rev is nice, but it doesn't pad input lines. It just reverses them.
The "mangling" you're seeing is because one line may be 20 characters long, and the next may be 15 characters long. In your input text they share a left-hand column. But in your output text, they need to share a right-hand column.
So you need padding. Oh, and asymmetric reversal, as Joachim said.
Here's my revawk:
#!/usr/bin/awk -f
#
length($0)>max {
max=length($0);
}
{
# Reverse the line...
for(i=length($0);i>0;i--) {
o[NR]=o[NR] substr($0,i,1);
}
}
END {
for(i=1;i<=NR;i++) {
# prepend the output with sufficient padding
fmt=sprintf("%%%ds%%s\n",max-length(o[i]));
printf(fmt,"",o[i]);
}
}
(I did this in gawk; I don't think I used any gawkisms, but if you're using a more classic awk variant, you may need to adjust this.)
Use this the same way you'd use rev.
ghoti#pc:~$ echo hello | cowsay | ./revawk | tr '[[]()<>/\\]' '[][)(><\\/]'
_______
< olleh >
-------
^__^ /
_______/(oo) /
/\/( /(__)
| w----||
|| ||
If you're moved to do so, you might even run the translate from within the awk script by adding it to the last printf line:
printf(fmt," ",o[i]) | "tr '[[]()<>/\\]' '[][)(><\\/]'";
But I don't recommend it, as it makes the revawk command less useful for other applications.
Your lines aren't the same length, so reversing the cow will break it. What you need to do is to "pad" the lines to be the same length, then reverse.
For example;
cowsay <<<"hello" | awk '{printf "%-40s\n", $0}' | rev
will pad it to 40 columns, and then reverse.
EDIT: #ghoti did a script that sure beats this simplistic reverse, have a look at his answer.
Here's one way using GNU awk and rev
Run like:
awk -f ./script.awk <(echo "hello" | cowsay){,} | rev
Contents of script.awk:
FNR==NR {
if (length > max) {
max = length
}
next
}
{
while (length < max) {
$0=$0 OFS
}
}1
Alternatively, here's the one-liner:
awk 'FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS }1' <(echo "hello" | cowsay){,} | rev
Results:
_______
> olleh <
-------
^__^ \
_______\)oo( \
\/\) \)__(
| w----||
|| ||
----------------------------------------------------------------------------------------------
Here's another way just using GNU awk:
Run like:
awk -f ./script.awk <(echo "hello" | cowsay){,}
Contents of script.awk:
BEGIN {
FS=""
}
FNR==NR {
if (length > max) {
max = length
}
next
}
{
while (length < max) {
$0=$0 OFS
}
for (i=NF; i>=1; i--) {
printf (i!=1) ? $i : $i ORS
}
}
Alternatively, here's the one-liner:
awk 'BEGIN { FS="" } FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS; for (i=NF; i>=1; i--) printf (i!=1) ? $i : $i ORS }' <(echo "hello" | cowsay){,}
Results:
_______
> olleh <
-------
^__^ \
_______\)oo( \
\/\) \)__(
| w----||
|| ||
----------------------------------------------------------------------------------------------
Explanation:
Here's an explanation of the second answer. I'm assuming a basic knowledge of awk:
FS="" # set the file separator to read only a single character
# at a time.
FNR==NR { ... } # this returns true for only the first file in the argument
# list. Here, if the length of the line is greater than the
# variable 'max', then set 'max' to the length of the line.
# 'next' simply means consume the next line of input
while ... # So when we read the file for the second time, we loop
# through this file, adding OFS (output FS; which is simply
# a single space) to the end of each line until 'max' is
# reached. This pad's the file nicely.
for ... # then loop through the characters on each line in reverse.
# The printf statement is short for ... if the character is
# not at the first one, print it; else, print it and ORS.
# ORS is the output record separator and is a newline.
Some other things you may need to know:
The {,} wildcard suffix is a shorthand for repeating the input file name twice.
Unfortunately, it's not standard Bourne shell. However, you could instead use:
<(echo "hello" | cowsay) <(echo "hello" | cowsay)
Also, in the first example, { ... }1 is short for { ... print $0 }
HTH.
You could also do it with bash, coreutils and sed (to make it work with zsh the while loop needs to be wrapped in tr ' ' '\x01' | while ... | tr '\x01' ' ', not sure why yet):
say=hello
longest=$(cowsay "$say" | wc -L)
echo "$say" | rev | cowsay | sed 's/\\/\\\\/g' | rev |
while read; do printf "%*s\n" $longest "$REPLY"; done |
tr '[[]()<>/\\]' '[][)(><\\/]'
Output:
_______
< hello >
-------
^__^ /
_______/(oo) /
/\/( /(__)
| w----||
|| ||
This leaves a lot of excess spaces at the end, append | sed 's/ *$//' to remove.
Explanation
The cowsay output needs to be quoted, especially the backslashes which sed takes care of by duplicating them. To get the correct line width printf '%*s' len str is used, which uses len as the string length parameter. Finally asymmetrical characters are replaced by their counterparts, as done in ghoti's answer.
I don't know if you can do this in AWK, but here are the needed steps:
Identify the length of your original's most lengthy line, you will need it give proper spacing to any smaller lines.
(__)\ )\/\
For the last char on each line, map out the need of start-of-line spaces based on what you acquired from the first step.
< hello >
//Needs ??? extra spaces, because it ends right after '>'.
//It does not have spaces after it, making it miss it's correct position after reverse.
(__)\ )\/\
< hello >???????????????
For each line, apply the line's needed number of spaces, followed by the original chars in reverse order.
_______
> olleh <
-------
^__^ \
_______\)oo( \
\/\) \)__(
| w----||
|| ||
Finally, replace all characters that are not horizontally symmetrical with their horizontally-opposite chars. (< to >, [ to ], etc)
_______
< olleh >
-------
^__^ /
_______/(oo) /
/\/( /(__)
| w----||
|| ||
Two things to watch out for:
Text, as you can see, will not go right with reversions.
Characters like $, % and & are not horizontally symmetrical,
but also might not have an opposite unless you use specialized
Unicode blocks.
I would say that you may need each line to be fixed column width so each line is the same length. So if the first line is a character followed by a LF, you'll need to pad the reverse with white space before reversing.

Display all the "if statements " in a file using "grep"

I have a file "file.txt" that contains a number of if-statements as shown below:
if ( x == y)
if (x == (y + 1))
and so on.
How to display all the if-statements present in the file on the screen using grep.
I tried: grep -R "if ( * == * )" * but it's not showing the required result.
It looks like you're trying to glob. grep uses regular expressions.
Using GNU Grep:
grep -n '\<if\s*(' file.txt
This looks for if at the start of a word, followed by optional (*) whitespace (\s) and an opening parenthesis (().
If you only want to see tests for equivalence, you can do:
grep -n '\<if\s*(.*==' file.txt
...this adds a check for zero-to-many (*) (wildcard) characters (.) between ( and == but this won't catch multiline tests like:
if (status != OFF &&
volume == 11)
{
// [...]
}
If you only want to test for assignment:
grep -n '\<if\s*(.*[^!<>=+*/^-]=[^=]' test.txt
...this checks that the character before the = is not (^) and of !<>=+*/^- ([]) and that the character immediately afterward isn't an =.
This is not flawless, but I'm sure you can tweak the regular expression yourself to meet your needs.

Double/half inverted egrep

I'd like to show all lines except those containing foo, unless they also contain bar. Logically !(foo and (!bar)) === (!foo) or bar, so I can use two separate expressions. Can I do this sort of match with a single grep or egrep? -v doesn't work, since it negates both expressions, and I probably can't use Perl regex.
The following works, but it would be much less work to convert the code if it could be done in egrep:
$ echo '
foo
bar
moofoo
foobar
barbar' | grep -Pv '^((?!bar).)*foo((?!bar).)*$'
bar
foobar
barbar
The issue at hand is speed (looking for patterns in gigabytes of data).
If using awk is fine then following gives desired output
awk 'BEGIN {FS=" "};
{
if ($0 ~ /(foo)/)
{
if ($0 ~ /(bar)/)
{
print $0
}
}
else
{
print $0
}
}' FileContainingText.txt
since this works per line and no pipes are involved this should be fast.

Resources