Difference between revisions of "Bash"
Tom Golubev (Talk | contribs) (→Find Hacks) |
Tom Golubev (Talk | contribs) (→Here are some of my BASH tricks) |
||
(12 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
==Here are some of my BASH tricks== | ==Here are some of my BASH tricks== | ||
+ | |||
+ | |||
+ | |||
+ | Remove non-ascii chars from file | ||
+ | cat compile.py |tr -cd '\11\12\40-\176' >compile2.py | ||
Line 35: | Line 40: | ||
echo $PATH|sed s/\:/\\n/g|grep -v synopsys|tr '\n' ':' | echo $PATH|sed s/\:/\\n/g|grep -v synopsys|tr '\n' ':' | ||
− | |||
− | |||
==Find Hacks== | ==Find Hacks== | ||
Line 53: | Line 56: | ||
find "*.h" -exec grep "FE_BITS" -Hn {} \; | find "*.h" -exec grep "FE_BITS" -Hn {} \; | ||
This greps every file, and prints the filename and line number of each instance | This greps every file, and prints the filename and line number of each instance | ||
+ | |||
+ | |||
+ | Sometimes, you want to do something for all files of a particular class. For example, grep *.cpp and *.h files, and see if they contain a specific string or variable. | ||
+ | The previous method is to use the find -exec option. This has inherit weaknesses. Using a pipe is very difficult if not impossible with the exec option ie -exec grep "a"|grep "b" will not work, nor will redirects. | ||
+ | |||
+ | What you really want to do in that case, is similar to a bash for loop. | ||
+ | $for i in `find ~/Download/ -name "*.pdf"`; do echo pdftk \"${i}\" dump_data output ; done | ||
+ | |||
+ | This for loop will work correctly, but only if the filename DON'T contain SPACES. Here's a quick digression of why. | ||
+ | The 'for i in' bash construct parses tokens by both the newline, and the space. So when find returns this file: | ||
+ | /home/tom/Download/lxf/LinuxFormat 2009.pdf | ||
+ | |||
+ | The i variable will become the value "/home/tom/Download/lxf/LinuxFormat" then in the next iteration, it will progress onto "2009.pdf". Clearly not the behaviour we want. | ||
+ | |||
+ | Bash has the read utility, so that is how we will fix the for loop. First, we need to use a while loop, second, it must use the read utility. | ||
+ | find ~/Download/ -name "*.pdf"|while read filename; do echo " FILE: $filename"|grep -i linux; done | ||
+ | |||
+ | As you can see in this cumbersome example, we use the while read construct. This will set the filename variable to that of the output of the find utility per line, not per space and per line. Going through all this trouble is quite worth it though, since sometimes you need to pipe and redirect to get what you want. The find -exec option does not allow that. | ||
+ | |||
+ | Another Solution: Create a bash script, and have the find -exec option invoke the script with the filename. Of-course, I am focusing on one-liners here,so my preffered method is the one-liner. | ||
+ | |||
+ | |||
+ | Example: Search for all Makefiles, replace -O2 option with -O0 option | ||
+ | find -name Makefile|while read aline; do sed -i $aline -e s/"\-O2"/"\O0"/g; done | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | For more information on bash scripts with filename containing spaces: http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html | ||
==AWK Hacks== | ==AWK Hacks== | ||
Line 64: | Line 96: | ||
Print out only unique lines, first instance only. The uniq command does not always work correctly! | Print out only unique lines, first instance only. The uniq command does not always work correctly! | ||
cat orig_file|awk '!x[$0]++' > new_file | cat orig_file|awk '!x[$0]++' > new_file | ||
+ | |||
+ | |||
+ | ==BASH Inline Tricks== | ||
+ | |||
+ | bash is very powerful. Knowing how to use the 'one-liners' will save significant amounts of time. | ||
+ | |||
+ | Inline Loop | ||
+ | |||
+ | |||
+ | This is a simple for loop, parsing in every line of ls and echoing it. | ||
+ | |||
+ | for i in `ls`; do echo $i; done | ||
+ | |||
+ | for each `define in fe.h, take second field, and make a $display statement with. Notice the semicolons | ||
+ | |||
+ | for i in `cat fe.h|grep ^'\`define'|awk '{print $2}' `; do echo "\$display (\"" $i = \%d\"\, $i \)\;; done>fe_print.v | ||
+ | |||
+ | for every line that contains "4'", replace the preceding const with const OP4Type. | ||
+ | |||
+ | for i in `grep "4'" sparcv8.v -n|awk '{print $1}'|sed 's/://g'`; do sed ${i}s/"const"/"const OP4Type"/g sparcv8.v -i; done | ||
+ | |||
+ | The preceding example is special because it goes line for line, it can be tweaked to match first n occurences.It may be complex, but it's more versatile than the find | sed example | ||
+ | |||
+ | |||
+ | Kill all processes containing 'main.rb' | ||
+ | The killall command does something similar, with the "-r, --regex - Interpret process name pattern as an extended regular expression." option | ||
+ | |||
+ | for i in `ps -aux|grep main.rb|awk '{print $2}' `; do kill -9 $i; done | ||
+ | |||
+ | The second field is the pid (process id) | ||
+ | |||
+ | ==SED Tricks== | ||
+ | |||
+ | Sed is an extremely powerfull Stream EDitor. Its popular use is search replace, and infile editing. You can replace all instances of DEBUG to DEBUG_PRINT for *.h *.cpp files in a tree. | ||
+ | |||
+ | Replace newline with a <BR> and a newline (great for formatting text for wiki) | ||
+ | cat test|tr '\n' "BR\n"|sed s/"B"/"\<BR\>\\n"/g | ||
+ | |||
+ | For more SED tricks, visit http://sed.sourceforge.net/sed1line.txt |
Latest revision as of 18:36, 30 April 2010
Contents
Here are some of my BASH tricks
Remove non-ascii chars from file
cat compile.py |tr -cd '\11\12\40-\176' >compile2.py
Find files containing MMU in the name, do a line count, and them add them up. The first part finds files, then redirects the output of the line counts to a temp file
[tom@garak gaisler]$ for i in $(find|grep mmu); do wc $i -l; done>~/leon3_mmu_linecount
The output file: 655 ./leon3/mmutlb.vhd 351 ./leon3/mmuconfig.vhd 258 ./leon3/mmutw.vhd 176 ./leon3/mmulru.vhd 1559 ./leon3/mmu_dcache.vhd 202 ./leon3/libmmu.vhd 245 ./leon3/mmuiface.vhd 609 ./leon3/mmu.vhd 148 ./leon3/mmu_cache.vhd 189 ./leon3/mmutlbcam.vhd 380 ./leon3/mmu_acache.vhd 111 ./leon3/mmulrue.vhd 662 ./leon3/mmu_icache.vhd
The second part cuts the first field of the file, which is an integer containing the line count, and add all of them up
[tom@garak gaisler]$ PASS=0;for i in $(cat ~/leon3_mmu_linecount|cut -d" " -f1); do PASS=$(($PASS+$i)); done; echo $PASS lines total 5545 lines total
--Done--
Remove all synopsys dirs in path, so we can use a different version
(need a bit of work, since it doesn't redirect to stdout for some reason)
echo $PATH|sed s/\:/\\n/g|grep -v synopsys|tr '\n' ':'
Find Hacks
Find using wildcards, and multiple names
find -name "*.v" -o -name "*.h" This finds all *.v and *.h files. Note that wildcards are enclosed in quotes; or the shell will interpret it! The -o option is necessary when specifying multiple instances of an option.
Find files, then grep them.
find "*.h" -exec grep "FE_BITS" -Hn {} \; This greps every file, and prints the filename and line number of each instance
Sometimes, you want to do something for all files of a particular class. For example, grep *.cpp and *.h files, and see if they contain a specific string or variable.
The previous method is to use the find -exec option. This has inherit weaknesses. Using a pipe is very difficult if not impossible with the exec option ie -exec grep "a"|grep "b" will not work, nor will redirects.
What you really want to do in that case, is similar to a bash for loop.
$for i in `find ~/Download/ -name "*.pdf"`; do echo pdftk \"${i}\" dump_data output ; done
This for loop will work correctly, but only if the filename DON'T contain SPACES. Here's a quick digression of why. The 'for i in' bash construct parses tokens by both the newline, and the space. So when find returns this file:
/home/tom/Download/lxf/LinuxFormat 2009.pdf
The i variable will become the value "/home/tom/Download/lxf/LinuxFormat" then in the next iteration, it will progress onto "2009.pdf". Clearly not the behaviour we want.
Bash has the read utility, so that is how we will fix the for loop. First, we need to use a while loop, second, it must use the read utility.
find ~/Download/ -name "*.pdf"|while read filename; do echo " FILE: $filename"|grep -i linux; done
As you can see in this cumbersome example, we use the while read construct. This will set the filename variable to that of the output of the find utility per line, not per space and per line. Going through all this trouble is quite worth it though, since sometimes you need to pipe and redirect to get what you want. The find -exec option does not allow that.
Another Solution: Create a bash script, and have the find -exec option invoke the script with the filename. Of-course, I am focusing on one-liners here,so my preffered method is the one-liner.
Example: Search for all Makefiles, replace -O2 option with -O0 option
find -name Makefile|while read aline; do sed -i $aline -e s/"\-O2"/"\O0"/g; done
For more information on bash scripts with filename containing spaces: http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
AWK Hacks
--Currently, awk cannot handle files in-place. So redirect to a new_file, then mv the new_file to the old_file.
Print out n'th field, where deliminator is 1 or more spaces, replace $1 with the number field you want e.g. $3 is third field.
cat orig_file|awk '{print $1}'> new_file
Print out only unique lines, first instance only. The uniq command does not always work correctly!
cat orig_file|awk '!x[$0]++' > new_file
BASH Inline Tricks
bash is very powerful. Knowing how to use the 'one-liners' will save significant amounts of time.
Inline Loop
This is a simple for loop, parsing in every line of ls and echoing it.
for i in `ls`; do echo $i; done
for each `define in fe.h, take second field, and make a $display statement with. Notice the semicolons
for i in `cat fe.h|grep ^'\`define'|awk '{print $2}' `; do echo "\$display (\"" $i = \%d\"\, $i \)\;; done>fe_print.v
for every line that contains "4'", replace the preceding const with const OP4Type.
for i in `grep "4'" sparcv8.v -n|awk '{print $1}'|sed 's/://g'`; do sed ${i}s/"const"/"const OP4Type"/g sparcv8.v -i; done
The preceding example is special because it goes line for line, it can be tweaked to match first n occurences.It may be complex, but it's more versatile than the find | sed example
Kill all processes containing 'main.rb'
The killall command does something similar, with the "-r, --regex - Interpret process name pattern as an extended regular expression." option
for i in `ps -aux|grep main.rb|awk '{print $2}' `; do kill -9 $i; done
The second field is the pid (process id)
SED Tricks
Sed is an extremely powerfull Stream EDitor. Its popular use is search replace, and infile editing. You can replace all instances of DEBUG to DEBUG_PRINT for *.h *.cpp files in a tree.
Replace newline with a
and a newline (great for formatting text for wiki)
cat test|tr '\n' "BR\n"|sed s/"B"/"\<BR\>\\n"/g
For more SED tricks, visit http://sed.sourceforge.net/sed1line.txt