Is there any way to grep some text from large file and mark that in BOLD letters in Linux BASH shell ?
Like
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 15G 11G 3.3G 76% /
tmpfs 7.9G 0 7.9G 0% /dev/shm
/dev/sda3 51G 45G 3.8G 93% /home
/dev/sdc1 917G 359G 512G 42% /data
I have above output and I want whenever system mails me about this df output the /data line should be in bold letters.
Using ANSI escape sequence you can do this (though this is terminal dependent):
echo -e "\033[1m$(grep '/data' file)\033[0m"
Will produce:
/dev/sdc1 917G 359G 512G 42% /data
Bash doesn't really have bold (it does have bright which is effectively equivilent). You could use sed to insert the bash control codes, but if you are less concerned with the exact colour choice, you can use
grep --color -E "text match pattern|" mylargefile
To get grep to do the highlighting. (see Colorized grep -- viewing the entire file with highlighted matches for alternatives and discussion)
Related
I want to converting the output of df -h into an array and then modified it's output with bash script.
For example the output of command is:
Filesystem Size Used Avail Use% Mounted on
/dev/sda4 28G 480M 26G 2% /var
/dev/sda2 28G 45M 26G 1% /tmp
/dev/sda5 275G 4.6G 256G 2% /home
tmpfs 790M 84K 789M 1% /run/user/1000
The output should be:
Filesystem Size Used Avail Use%
device4 28G 480M 26G 2%
device2 28G 45M 26G 1%
device5 275G 4.6G 256G 2%
Tmp 790M 84K 789M 1%
I know I should set IFS=$'\n' to have this output in an array but I have no idea how can I recognize device name and replace with the proper name.
Thank you for helping me to solve the problem.
This works for me:
#!/bin/bash
df -h | sed 's#\(.*\) \(.*\)$#\1#' | sed 's#Mounted##' | sed 's#/dev/sda\(.\)#device\1 #'
the first sed removes the last column
the second sed removes the word "Mounted". The last column header is "Mounted on", so the first sed only removes "on".
the last sed replaces /dev/sda by device.
So I've looked all over the place for what to do here and just find "from file". I am looking to extract from a command output.
Task: display the absolute path names of disks beginning with the sd.
Current progress: displaying absolute path name of disks
[host /]$ lsblk -o name -lpn
/dev/sda
/dev/sda1
/dev/mapper/centos-root
/dev/sda2
/dev/md127
....
Desired output
/dev/sda
/dev/sda1
/dev/sda2
....
I've played around with cut, print, awk and sed.
Got syntax errors or no output.
grep
lsblk -o name -lpn | grep "/dev/sd"
awk
lsblk -o name -lpn | awk '/dev\/sd/{print}'
sed
lsblk -o name -lpn | sed -n '/\/dev\/sd/p'
Output:
/dev/sda
/dev/sda1
/dev/sda2
lsblk accepts arguments, so you can in many cases just as easily say
lsblk -no name -lp /dev/sd?
The ? is just wildcard match of a single character, so sda, sdb, ...
There is a potential pitfall. If you have more than 26 disks or you are working on a system that uses those unique disk identifiers. In which case you need to change the wildcard to an asterisk and filter out only the unique results,
lsblk -no name -lp /dev/sd* | sort -u
Output (for either command):
/dev/sda
/dev/sda1
/dev/sda2
/dev/sda3
/dev/sdb
/dev/sdb1
/dev/sdb2
Try with sed:
lsblk -o name -lpn |sed -n '/\/sd/p'
I have a bash script that computes used diskspace percentage for given partition:
df -k $devname | grep -v ^File | awk '{printf ("%i",$3*100 / $2); }
it doesn't work on a partition with long name because the name pushes the other columns to the next line, how do I fix this?
df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/VolGroup60-ROOT
2865493440 2222006740 497927968 82% /
tmpfs 30913808 0 30913808 0% /dev/shm
/dev/sda1 495844 103347 366897 22% /boot
Instead of parsing the entire "table" you could output the usage in percentage directly by using the --output parameter (see man 1 df for details):
$ df -k --output=pcent /dev/sda1
Use%
13%
That should be a lot easier to filter.
E.g. by creating an array with readarray in Bash 4:
$ readarray -t -s 1 percentage < <(df -k --output=pcent /dev/sda1)
$ echo "${percentage[0]// /}"
13%
Assigning the output of df to an array line by line:
$ percentage=($(df -k --output=pcent /dev/sda1))
$ echo "${percentage[1]}"
13%
The -P (portability) option for df use a output format compliant with posix and keeps everything in one line. You can also simplify the awk part using sel.
I want convert this text:
qa-ops01.mysite.com
/dev/mapper/sys-home 58G 26G 30G 47% /home
/dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /tmp
qa-ops02.mysite.com
/dev/mapper/sys-home 58G 26G 30G 47% /usr
/dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /var
qa-ops03.mysite.com
/dev/mapper/sys-home 58G 26G 30G 47% /lib
/dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /etc
to this one:
qa-ops01.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /home
qa-ops01.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /tmp
qa-ops02.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /usr
qa-ops02.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /var
qa-ops03.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /lib
qa-ops03.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /etc
I have used
cat FILE |sed 'N;s/.com\n//'
Is there anyway to achieve this, or should I just write the If... Then...
Thanks everybody for the answers :D (you always show me new things :D)
The answer by potong is almost correct; it only handles one server, rather than multiple servers, but the change required is small.
$ sed -e '/^[^ ]*$/{h;d;}' -e 'G; s/\(.*\)\n\(.*\)/\2 \1/' data
qa-ops01.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /home
qa-ops01.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /tmp
qa-ops02.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /usr
qa-ops02.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /var
qa-ops03.mysite.com /dev/mapper/sys-home 58G 26G 30G 47% /lib
qa-ops03.mysite.com /dev/mapper/sys-tmp 3.9G 2.3G 1.5G 61% /etc
$
The script is in two parts, identified by the two -e options. The first part identifies server names; those lines contain no spaces (hence /^[^ ]*$/ looks for a line with no spaces), and copies the line into the hold space (h) and then deletes it (d) and continues with the next line. The second part of the script is only exercised on lines that contain spaces. It appends the content of the hold space to the pattern space after a newline (G); then it splits the line into 'everything up to the newline' and 'everything after the newline', and switches them so that the 'after' (\2) comes first, then a space, then the 'before' (\1).
This uses the classic sed regular expressions; it was tested on Mac OS X (10.7.5) with both the BSD sed and also with GNU sed without change. GNU sed has options such as -r to change the interpretation of regexes which would save you a few backslashes.
I don't know much about sed, but in AWK:
awk 'NR==1{prefix=$0;next} {print prefix, $0}' file
Update
If that is the case, look for lines with only one field (column), use it for prefix. That means, the only change to the above script is NF (number of fields) in place of NR (number of records, or lines).
awk 'NF==1{prefix=$0;next} {print prefix, $0}' file
This might work for you (GNU sed):
sed -r '1{h;d};G;s/(.*)\n(.*)/\2 \1/' file
1{h;d} save the first line in the hold space (HS) and then delete the pattern space (PS).
G on all subsequent lines append a newline and the HS to PS.
s/(.*)\n(.*)/\2 \1/ re-arrange the PS and remove the introduced newline.
Since posting the answer the original question was changed, to take this into account:
sed -r '/%/!{h;d};G;s/(.*)\n(.*)/\2 \1/' file
This handles multiple servers by saving lines in the HS which do not contain %.
try this one:
awk '!/^\/dev/{header=$0;next} {print header, $0}' input.txt
alternatively (functionally identical, but a bit easier to read and understand, IMO):
awk '{if($0 !~ /^\/dev/){header=$0}else{print header, $0}}' input.txt
awk '{if($0~/mysite.com/){x=$0}else{print x,$0}}' your_file
tested here
Another way using awk is as follows:
awk '{if ($1 ~ /mysite.com/){a= $0 } if ($1 ~ /dev/){ print a" "$0}}' temp.txt
a = $0 copies the line to a if that contains mysite.com
If second lines contains /dev then it joins both lines
or other variation of potong sed
sed -re '/mysite.com/{h;d};G;s/(.*)\n(.*)/\2 \1/' temp.txt
My code:
df -h | sed 's/G/Gb/' >> $2
The output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 29Gb 5.5G 22G 21% /
devtmpfs 757M 196K 757M 1% /dev
tmpfs 757M 436K 757M 1% /dev/shm
The output I need:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 29Gb 5.5Gb 22Gb 21% /
devtmpfs 757M 196K 757M 1% /dev
tmpfs 757M 436K 757M 1% /dev/shm
Add the global flag
df -h | sed 's/G/Gb/g' >> $2
Your original code changes the first match of the pattern. Adding a "g" (global) after the ending "/" makes it global - changes all matching instances. Not this could have unintended consequences e.g. If any of the volumne names comtained a "G", that would be changed too.
This will convert M to Mb and K to Kb as well:
df -lh | perl -pe 's/([0-9])(G)( )/\1Gb\3/g; s/([0-9])(M)( )/\1Mb\3/g; s/([0-9])(K)( )/\1Kb\3/g'