Extracting a pattern using sed command - sed

I have a string like this
"112344 1234234 guest 25 % allocation used"
I want to extract 25 out of this string using sed.
In the given line "112344 1234234 guest 25 % allocation used", I am interested in only "25 %". The first fields can be empty also, something like this:
" 25 % allocation used"
but " % allocation used" is fixed string.
So the regex should be based on this fixed pattern.
Can anyone please help me on this.

Essentially, you want the field that is fourth from the right. To get this, I'd use awk:
awk '{ print $(NF-3) }'
If it absolutely must be in sed, try this:
sed -e 's/^/ /' -e 's/\(^|.* \)\(.*\) % allocation used/\1/'

If you are sure that the number preceding the '%' is 2 digits then you can use the following
[jaypal~/Temp]$ cat text5
112344 1234234 guest 25 % allocation used
25 % allocation used
2344 guest 15 % allocation used
[jaypal~/Temp]$ sed 's/.*\(.. %\).*/\1/' text5
25 %
25 %
15 %

Try this
cut -d% -f1 | awk '{ print $(NF) }'

Related

Use of uninitialized value $5 and argument isn't numeric in numeric lt (<)

I'm discovering the language Perl. I try to create a script to integrate inside my Nagios server, but i got two errors that I'm not able to resolve. Can you help me?
The errors are the following:
Use of uninitialized value $5 in concatenation (.) or string at
check_disque.pl line 53.
Argument "/dev/mapper/centos-root 50G 5,5G 45G 11 /\n" isn't
numeric in numeric lt (<) at check_disque.pl line 55.
My line 55 :
$espace_utilise=`df -h / | awk 'FNR == 2 {print $5}' | sed 's/%//g'`;
And the line 56 :
if ($espace_utilise < $warning) {
$espace_utilise=`df -h / | awk 'FNR == 2 {print $5}' | sed 's/%//g'`;
# ^^--- here
The backticks interpolate variables, so $5 will be interpolated by Perl. You can solve this by escaping the dollar sign with a backslash \$5, or use qx'', which does the same as backticks, but the single quote delimiters disables interpolation. It will cause some issues with your awk/sed commands, though. Which will require more escaping. This is one reason using shell commands inside Perl is a bad idea.
$espace_utilise=`df -h / | awk 'FNR == 2 {print \$5}' | sed 's/%//g'`;
$espace_utilise=qx'df -h / | awk \'FNR == 2 {print $5}\' | sed \'s/%//g\'';
Luckily for you, you can just do the df command directly and use the text processing with Perl commands, which will be a lot easier. I would help you, but I don't know exactly what that awk command does. I would guess:
$espace_utilise=`df -h /`; # get the line
my $df = (split ' ', $espace_utilise)[4]; # get the 5th field
$df =~ s/%//g; # remove %. Can also use tr/%d//d
The other error:
Argument "/dev/mapper/centos-root 50G 5,5G 45G 11 /\n" isn't numeric in numeric lt (<) at check_disque.pl line 55. My line 55 :
...is just because the first statement failed. Perl interpolates $5 even though it warns about it, and it becomes the empty string instead. So your awk line just says { print }, which I assume is the same as printing the whole line. So if you fix the first part, you can ignore this.
I'm discovering the language PERL.
Then take a look at CPAN. Among many modules there is Filesys::DiskSpace which does what you want. You need to install it first. In order to do that you need to learn how to INSTALL modules from CPAN, following
cpan App::cpanminus
cpanm Filesys::DiskSpace
should work in your case. Note that if you did not use cpan earlier it might ask you if you want it to autoconfigure itself. Hit enter to say yes.
After installation usage is as simple as
use Filesys::DiskSpace;
($fs_type, $fs_desc, $used, $avail, $fused, $favail) = df $dir;
Note that it does not provide percentage implicitly, so you would need to follow df behavior
The percentage of the normally available space that is currently allocated to all
files on the file system. This shall be calculated using the fraction:
<space used>/( <space used>+ <space free>)
expressed as a percentage. This percentage may be greater than 100 if <space free> is less
than zero. The percentage value shall be expressed as a positive integer, with any
fractional result causing it to be rounded to the next highest integer.

Join certain lines with sed

I have an input which looks like this:
1
2
3
4
5
6
And I want to transform it with sed to :
12
345
6
I know it can be easily done with other tools but I want to do it specifically with sed as a learning exercise.
I have attempted this:
sed ':x ; /^ *$/{ N; s/\n// ; bx; }'
But it prints :
123456
Can someone help me fix this?
Quoting from the GNU sed manual:
A common technique to process blocks of text such as paragraphs (instead of line-by-line) is using the following construct:
sed '/./{H;$!d} ; x ; s/REGEXP/REPLACEMENT/'
The first expression, /./{H;$!d} operates on all non-empty lines, and adds the current line (in the pattern space) to the hold space. On all lines except the last, the pattern space is deleted and the cycle is restarted.
The other expressions x and s are executed only on empty lines (i.e. paragraph separators). The x command fetches the accumulated lines from the hold space back to the pattern space. The s/// command then operates on all the text in the paragraph (including the embedded newlines).
And indeed,
sed '/./{H;$!d} ; x ; s/\n//g'
does what you want.
FWIW here's how to really do that task in UNIX:
$ awk -v RS= -v OFS= '{$1=$1}1' file
12
345
6
The above will work on any UNIX box.
A GNU awk approach:
$ awk -F"\n" '{gsub("\n","");}1' RS='\n{2,}' file
12
345
6
Note it will add a trailing newline\n after last line.

Print last "window" with sed

I have a log to process that's roughly structured like this:
...
...
sentinel
marker
...
marker
...
sentinel
marker
...
I want everything between a marker and the following sentinel, and I want the last such "window." The following works ok:
sed -e "1{h;d} ; 2,109{H;d} ; 110{H;g} ; /sentinel/h ; \${g;q} ; N ; D" file.log
Here, 110 is a rough (but consistent within a couple lines) estimate of the space between markers for this log, but I'd have to recompute this estimate for other logs, which is annoying.
I'm wondering if there's a more elegant way to achieve this with sed, i.e. to automatically return the last window between marker and sentinel (I'll also accept an answer that demonstrates why you can't do this in sed).
Thanks.
P.S. I know that could do this in any number of languages, but I'd like to exercise the sed muscles.
This might work for you (GNU sed):
sed '/marker/,/sentinel/{/marker/h;//!H};$!d;x' file
Stash lines between marker and sentinel in the hold space (overwriting old with new) and at the end of the file print whatever is left in the hold space.
EDIT:
The solution above caters for marker and sentinel pairs. If the either of those is likely to be missing then use:
sed '/marker/,/sentinel/H;$!d;x;s/.*\(marker.*sentinel\).*/\1/p;d' file
This saves all marker/sentinel pairs in the hold space and the at end of the file removes all but the last complete pair.
If you know that there are no blank lines in the file, you could do:
sed -e '/^marker$/i\
\
' -e '/^sentinel$/a\
\
' input | awk '/sentinel/{l=$0}END{print l}' RS=
(Not sure I'd call that elegant: basically you are inserting blank lines between the records and letting awk's RS to the hard work. If you cannot guarantee that there are no blank lines, pre/post process the data to ensure that:
sed 's/^/x/' input | sed -e '/^xmarker$/i\
\
' -e '/^sentinel$/a\
\
' | awk '/sentinel/{l=$0}END{print l}' RS= | sed 's/^x//'
(Of course, you could avoid the extra sed by wrapping them into the existing sed and the awk, but the idea is (I think) clearer this way.)

Reading line by line in perl

Suppose I have a text file with the following format
#ATDGGSGDTSG
NTCCCCC
+
#nddhdhnadn
#ATDGGSGDTSG
NTCCCCC
+
nddhdhnadn
Now its a repeating pattern of "4" lines and I every time want to print only the 2nd line i.e. the line after the line starting with "#" i.e 2nd line..6th line..etc.
How can I do it?
perl -ne 'print if $b and !/^#/; $b=/^#/' file
With awk:
$ awk 'NR%4==2' a
NTCCCCC
NTCCCCC
NR stands for number or record, in this case being number of line. Then, if we divide it by 4, we get all lines whose modulus is 2.
Update on your comment
And wat if I want the output to be > "nextline" NTCCCCC > "nextline"
NTCCCCC i.e. I want to add ">" before that line while redirecting the
output.
This way, for example:
$ awk 'NR%4==2 {print ">"; print $0}' a
>
NTCCCCC
>
NTCCCCC
Another example:
$ seq 30 | awk 'NR%4==2'
2
6
10
14
18
22
26
30
awk '/^\#/{getline;print}' your_file
You could have a variable like $printNextLine and loop over all your Input, Setting it to 1 whenever you see a line with # and printing the current line while Setting the variable back to 0 if it is 1.
Not as effective and short as the other answers but maybe more intuitive for someone new to perl.
awk '/^\#/{getline;print}' file

regular expression in sed for masking credit card

We need to mask credit card numbers.Masking all but last 4 digits. I am trying to use SED. As credit card number length varies from 12 digits to 19,I am trying to write regular expression.Following code will receive the String. If it contains String of the form "CARD_NUMBER=3737291039299199", it will mask first 12 digits.
Problem is how to write regular expression for credit card-12 to 19 digits long? If I write another expression for 12 digits, it doesn't work.that means for 12 digit credit card- first 8 digits should be masked. for 15 digit credit card, first 11 digits should be masked.
while read data; do
var1=${#data}
echo "Length is "$var1
echo $data | sed -e "s/CARD_NUMBER=\[[[:digit:]]\{12}/CARD_NUMBER=\[\*\*\*\*\*\*\*\*/g"
done
How about
sed -e :a -e "s/[0-9]\([0-9]\{4\}\)/\*\1/;ta"
(This works in my shell, but you may have to add or remove a backslash or two.) The idea is to replace a digit followed by four digits with a star followed by the four digits, and repeat this until it no longer triggers.
This does it in one sed command without an embedded newline:
sed -r 'h;s/.*([0-9]{4})/\1/;x;s/CARD_NUMBER=([0-9]*)([0-9]{4})/\1/;s/./*/g;G;s/\n//'
If your sed doesn't have -r:
sed 'h;s/.*\([0-9]\{4\}\)/\1/;x;s/CARD_NUMBER=\([0-9]*\)\([0-9]\{4\}\)/\1/;s/./*/g;G;s/\n//'
If your sed needs -e:
sed -e 'h' -e 's/.*\([0-9]\{4\}\)/\1/' -e 'x' -e 's/CARD_NUMBER=\([0-9]*\)\([0-9]\{4\}\)/\1/' -e 's/./*/g' -e 'G' -e 's/\n//'
Here's what it's doing:
duplicate the number so it's in pattern space and hold space
grab the last four digits
swap them into hold space and the whole number into pattern space
grap all but the last four digits
replace each digit with a mask character
append the last four digits from hold space to the end of the masked digits in pattern space (a newline comes along for free)
get rid of the newline
try this, you don't have to create complicated regex
var1="CARD_NUMBER=3737291039299199"
IFS="="
set -- $var1
cardnumber=$2
echo $cardnumber | awk 'BEGIN{OFS=FS=""}{for(i=1;i<=NF-4 ;i++){ $i="*"} }1'
output
$ ./shell.sh
************9199
I'm not much of a sed guru, and thus I cannot manage to do it in only one command, though there surely are ways. But with two sed commands, here is what I got:
sed -e 's/CARD_NUMBER=\([0-9]*\)\([0-9]\{4\}\)/\1\
\2/' | sed -e '1s/./x/g ; N ; s/\n//'
Please note the embedded newline.
Because sed works by lines, I first break the card number into the initial part and the last four digits, separating them by a newline (the first sed command). Then, I mask the initial part (1s/./x/g), and remove the new line (N ; s/\n//).
Good luck!