general shell debug flags:
sh -n check syntax
sh -v echos commands before executing
sh -x echos commands after running them
sh -u gives an error message
define variable
a=1
to reference a variable, prepend it with $ or wrap with ${}
echo "your var is $a"
echo "your var is ${a}" # protects from ambiguity from connecting chars
like perl/php, variables in a double quoted string are interperlated.
to make the variable available to sub shells
export a=1
# spawn new shell
bash
echo $a
# prints 1
note: executing a script, or a pipe will typically create a new shell
avoid sub shells if you don't need them
prevent string interpolation
use single quotes
script 'a$s'
escape single quote
'"'"'
or
'\''
"don't"
# use double quotes only on string
'don'"'"'t'
'don'\''t'
dynamic string arguments
The interpreter is smart enough to pass along a string that's already parsed, to the next command. you do NOT need quotes around interpolated strings:
example:
# does not work ... too many single quotes
file=.
args=" -a '$file' "
ls $args
# fails. looking for a file named \'.\'
# works ... no single quotes within string:
file=.
args=" -a $file "
ls $args
summary: The only time you need to encapsulate string data in quotes is on the initial shell call (initial string parse). ... I think :)
add numbers
num=$((num1 + num2))
num=$(($num1 + $num2)) # also works
num=$((num1 + 2 + 3)) # ...
num=$[num1+num2] # old, deprecated arithmetic expression syntax
error handling / exception handling
# $? reports last error.
# WARNING, IT IS CONSUMED ON FIRST TEST, SINCE EACH COMMAND WRITES TO IT
grep 23412341 .
grep: .: Is a directory
$ echo $?
2
$ echo $?
0
# example test
if [[ $? == 0 ]]
then
java Test
fi
define / create an alias (bash syntax)
alias name=value
alias name2='command'
alias name3='command arg1 arg2'
alias name4='/path/to/script'
alias name5='/path/to/script.pl arg1'
disable alias that overrides command, use ''
'ls'
unalias aliasname
alternate string quote syntax
c
a = $'te\'st'
strings that are scanned for ansi c like escape sequences.
i18n
a = $"test"
means get the international text. if there is a translation available for that string, it is used instead of the given text. If not, or if the locale is C/POSIX, the dollar sign simply is ignored, which results in a normal double-quoted string.
copy var
b=$a
echo $b
if then, else if, else
# test if variable is NOT set
if [ -z "$VAR" ]; then
# else if
elif [ -z "$VAR2" ]; then
else
# end
fi
# note you can also write the 'then' on its own line
negation
! means not
test variable has a value
if [ -z "$VAR" ];
then
echo "value is not set"
fi
test file existence:
if [ -f $FILE ];
then
echo "File $FILE exists."
else
echo "File $FILE does not exist."
fi
if [ ! -f $FILE ];
then
echo "File $FILE does not exist."
fi
test if file contains pattern
if grep -q FOOBAR "$File"; then
# file has FOOBAR
fi
test if file does not contain pattern
if ! grep -q SomeString "$File"; then
# Some Actions
fi
then: can be written on the same line with a ; or on the next line
# people likely do this because veritical space is limited on an 80x24 terminal
if ! grep -q SomeString "$File"; then
echo 1;
fi
# means the same as
if ! grep -q SomeString "$File"
then
echo 1;
fi
shell redirection
cmd 2> redirect stderr
cmd &> redirect both stderr and stdout
cmd >&n send output to descriptor n
cmd m>&n redirect output that would have gone to m to n
cmd <&n get input from descriptor n
cmd <&- close standard in
silence output
# SILENCIO POR FAVOR!
cmd &> /dev/null
pipes | are used to chain commands together. output of the first command is wired up to the input of the next command (like snapping together legos)
comand | tee file.txt prints to screen and saves to file.txt
edit previous command
^foo^bar executes previous command, except replaces foo with bar
loops:
for loop
for ((i=1;i<=100;i++));
do
# your-unix-command-here
echo $i
done
while loop
while ((1));
do
# your-unix-command-here
sleep 1;
done
for
for x in file1 file2 file3
do
sed 's/thier/their/g' $x > ,$x
mv ,$x $x
done
# index iteration
for ((i=0; i<10; i++)); do
echo $i
done
bash heredoc syntax
# note no ; required
# for quoting multiline strings
cat << ENDOFBLOCK
sdfg
ENDOFBLOCK
Note: you can't assign this straight to a var
For example, you can use a cat do
variable=$(cat <<EOF
<html>
<body>
<p>THis is a test</p>
</body>
</html>
EOF
)
test loop
You can preview what loops will do by putting an echo in front of the commands. you don't want to realize there was a mistake after running the command a thousand times.
# test
for a in *
do
echo mv a b
done
# then use the command
mv a b
This can mess up " in output though, since echo will interpret it and discard. if you need exact output, wrap the block in a literal heredoc. THis will preserve all quotes " ' ` and meta chars
So, intest you can use a heredoc to prevent interpolation
cat <<DEBUG
echo PARAM=` grep "$ARG" /var/tmp/setfile | awk '{print $2}' `
DEBUG
# prints:
# echo PARAM=` grep "$ARG" /var/tmp/setfile | awk '{print $2}' `
piping output from one command to a loop
find photos/ | while read file; do echo $file ; done;
loops in bash and variable scope:
counter=0
for f in $(find .)
do
let counter+=1
done
echo $counter
problem: if ls returns a large result, can overflow the buffer.
counter=0
find . | while read f
do
let counter+=1
done
echo $counter
the problem: variables inside loop in their own shell, can't be used if needed.
For read only access, use export in the outer context
export var=1
Though, this won't work if you need write access. In this case you need < <() syntax
for example
counter=0
while read f
do
let counter+=1
done < <(find .)
echo $counter
ugly syntax, but works
handle whitespace in filename
find <path> -mtime +7 | while read line
do
# just wrap with double quotes
mv "$line" /somewhere/else
done
you can embed a comment string in the heredoc marker
# comment following line out after testing
cat <<'#ENDDEBUG'
echo PARAM=`ls -la | awk '{print $3}' `
#ENDDEBUG
parse delimted test with `cut`
# list all users defined on system
# split on :, print the first field
cut -d: -f1 /etc/passwd
increment var
y=$[y+1]
y=$(($y+1))
((y++))
polling process (repeatedly testing)
while (( 1 ));
do ls -lah sedOSEZA8 ;
sleep 1;
done
see all characters used in a file
# change char to char\n
cat Configuration | sed -e 's/\(.\)/\1\n/g' | sort | uniq
create a simple index page for .html files in a folder
ls *.html -1 | while read f ; do echo "<a href='$f'>$f</a><br>"; done >> index.htm
scp - secure copy (use instead of ftp)
scp d_HomeBk.sql user@server:/remote/path/to/dir/
note: if there are spaces in file name use: encased/escaped quotes "\" \""
eg
scp d_HomeBk.sql user@server:"\"/path/to/file/\""
or enclose entire string in single quotes, with double
scp d_HomeBk.sql 'user@server:"/path/to/file/"'
use -r to copy recursively
find files
# last 24 hours
find . -mtime 0
# older than a month
find . -mtime +31
# find files only
find -type f
when backticks overflow command line, can group in blocks and pipe to a loop:
% sh
find . -type f -mtime -1 -print |
fmt -1000 |
while read files
do pr -n $files
done | lpr
exit
...or use xargs
xargs lpr < allfiles.tmp
or
# NOTE the -I defines a string to replace
# default behavior sends to first argument of command.
command | xargs -I '{}' somecommand stuff '{}'
command | xargs rm -rf
# copy a lot of image files, convert sizes
ls -1 | while read file; do echo $file; \
convert -resize 460 "$file" "/cygdrive/c/temp/yourfiles/$file" ; done
# slightly better complex ... can be stopped, restarted
$destdir = /cygdrive/c/temp/yourfiles
ls -1 | while read $file
do
if [[ ! -e $destdir/$file ]]
then
echo $file
convert -resize 460 $file $destdir/$file
fi
done
internal file separator. note: also there is the internal variable IFS (internal file seperator) that is noramally set to white space
while can parse a line, such as
IFS=':'
cat /etc/passwd | while read var1 var2 var3 var4
do
...
done
# although awk is probably the better tool for this
the previous file edited on bash shell
!$
convert stream to lower case:
alias lc="tr '[:upper:]' '[:lower:]'"
alias uc="tr '[:lower:]' '[:upper:]'"
eg:
cat file.txt | lc
copy all but some files
cpio can copies the contents of /source-dir to /dest-dir, but omits
files and directories named .snapshot (and anything in them).
cd /source-dir
find . -name .snapshot -prune -o \( \! -name *~ -print0 \)| cpio -pmd0 /dest-dir
This command copies the contents of /source-dir to /dest-dir, but omits
files and directories named .svn (and anything in them).
cd /source-dir
find . -name .svn -prune -o \( \! -name *~ -print0 \)| cpio -pmd0 /dest-dir
profile vs bashrc
profiles are for interactive login shells (that requre user/pass),
rc is for non-interactive, non-login
mount cd rom manullly
mkdir -p /mnt/cdrom && mount -t iso9660 -o ro /dev/cdrom /mnt/cdrom
functions
# without arg, called like a command line tool
f() { echo 1; };
f;
# with argument
f() { echo $1; };
f arg ;
floating point numbers
bwa hahahah ... :-)
bash doesn't support floating point arithmetic evaluation, but you can use the pretty-standard bc command in your function to help out. example:
function f_to_c { echo "5/9 * ($1 - 32)" | bc -l; }
Though if your script is getting complex enough to use floating point numbers, it probably should be written in an actual scripting language (python, perl, ruby, php, etc) not bash.
arrays:
If you are using arrays, you should start to wonder if you should be using bash at all and not another programming language. :)
But yes, you can do arrays.
#simple array:
name[index]=value
# explicitly define numeric array
declare -a chars
chars[0]="a"
chars[1]="b"
echo ${chars[0]}
# set list of values
declare -a arrayname=(element1 element2 element3)
# bash shorthand: declare/assign array
arr=(Hello World)
# in referencing the value, you must enclose in {}
echo ${arr[0]} ${arr[1]}
To quote from the man page:
The braces are required to avoid conflicts with pathname expansion.
In addition the following funky constructs are available:
${arr[*]} # All of the items in the array
${!arr[*]} # All of the indexes in the array
${#arr[*]} # Number of items in the array
${#arr[0]} # Length of item zero
# associative array or hash
declare -A morse
morse[a]="dot; dash"
morse[b]="dash; dot; dot; dot"
morse[c]="dash; dot; dash; dot"
# ...
echo ${morse[$1]} # write dots, dashes for letter
# print all of array, use @ (index number) or * (all)
echo ${morse[@]}
# to also export declared vars
declare -x ....
eval "string"
execute code. similar to `` or $(), but may read a bit easier
case: (switch statement)
Note: no break/exit needed
# number of feet
case $ANIMAL in
horse | dog | cat) echo -n "four";;
man | kangaroo ) echo -n "two";;
alien ) echo -n "pi";;
*)
#default
echo -n "an unknown number";;
esac
print current user
echo "$USER"
id -u -n
To print numeric UID, run:
id -u
change passwd non-interactive
# reads a list of users passwords (non interactive)
# may be encrypted
echo "username:newpass" | chpasswd
find all files not owned by user
find '!' -uid 0
find '!' -user root
can also echo multiline strings
echo "
/var/log/top.log {
missingok
}
" > /etc/logrotate.d/top
to avoid pipe in main loop
this:
while read log
do
echo $log
done < <( svn log $1 | grep -E -e "^r[0-9]+ \| " | tac )
is mostly the same as:
svn log $1 | grep -E -e "^r[0-9]+ \| " | tac | while read log
do
echo $log
done
ssh
Warning: ssh reads from standard input, and can "eat" all your input if used in a while loop.
Instead, in a loop use:
cat /dev/null | ssh $u@$h $COMMAND
or
ssh -n
In general, you should close or redirect stdin for the other commands you
run, to stop them reading from the file.
sh call_other_script.sh </dev/null
rotate file based on size
#!/bin/bash
f=/var/log/top.log
date >> $f
echo 'top:' >> $f
top -b -n 1 >> $f
echo '--' >> $f
echo 'ps:' >> $f
ps auxw >> $f
echo '--' >> $f
echo '' >> $f
# don't let it get too big
FILESIZE=$(stat -c%s "$f")
MAXSIZE=10000000
if [ $FILESIZE -gt $MAXSIZE ]; then
mv -f $f $f.1
fi
count word frequency
tr ' ' '\n' < /tmp/kevtest_old.txt | sort | uniq -c | sort -nr | head
27306
5200 69
1980 137
1716 129
728 93
611 56
611 55
520 42
520 41
248 70
alt:
awk '{a[$1]++}END{for(k in a)print a[k],k}' RS=" |\n" file > myfile
test string for pattern
build=123000
if [[ $build =~ "3" ]]
then
echo "has 3"
fi
build=123000
if [[ $build =~ "4" ]]
then
echo "has 4"
fi
convert string list to array
STRING="test1 test2 test3"
# convert to array
list=( $STRING )
#Or more verbosely:
declare -a list=( $STRING )
PS: You can't export IFS and use the new value in the same command. You have to declare it first, then use it's effects in the following command:
list=( first second third )
echo "${list[*]}"
#first element
list=( first second third )
echo "${list[0]}"
# first
nohup
# don't stop command if the terminal connction is lost
nohup one_command_only &
#execute multiple commands, call script in future
# get both process ids
function sleep_restart() {
# to stagger node restarts, call script in future
sleep=1
echo "Will schedule start in $sleep seconds..."
echo ""
echo " sleep $sleep && /yourcommand_restart"
echo ""
nohup sh -c "sleep $sleep && /yourcommand_restart >& /dev/null" &
sleep 1
}
find by permission
find . -perm /222
Search for files which are writable by somebody (their owner, or their group, or anybody else).
find . -perm /220
find . -perm /u+w,g+w
find . -perm /u=w,g=w
find . -perm /o=w
find loops in symlinks
find . -follow -printf ""
find number of processes
nproc
cat /proc/cpuinfo
# count words or count repeated lines by occurance
sort | uniq -c
remove XML tags, remove HTML tags
sed -n '/^$/!{s/<[^>]*>//g;p;}'
base64 decode
base64 --decode
# eg, convert xml 64 data to jpeg
curl <YOUR_URL_HERE> | sed -n '/^$/!{s/<[^>]*>//g;p;}' | base64 --decode > test.jpg
simple tail log, execute trigger when it reads a pattern
# look for lines containing OutOfMemoryError
log=/tmp/kevtest.txt
pattern="OutOfMemoryError"
while read line;
do
if [[ $line =~ $pattern ]]
then
echo "hit $line !"
break;
fi
done < <(tail -f $log)
alt: with awk
# no exit
tail -f /tmp/kevtest.txt | awk '/YourPatternHere/ { system("echo 1"); }'
# with exit
tail -f /tmp/kevtest.txt | awk '/YourPatternHere/ { system("echo 1"); exit; }'
hex encoding to ascii
d=`cat lib.php `
echo -e $d | awk '{printf "%s\n", $_}' > kevtest.php
test if socket port is open
Use netcat (nc) for the client and server tests
# basic interactive chat
# server (use same ports on client and server)
nc -l 7601 -v
# client (use same ports on client and server)
nc <HOST> 7601
# note: exit with ctrl-C
# scan port (non-interactive)
nc -z <host> <port>
# --
# client examples
# try socket connection
# interactive
nc <host> <port>
or (older)
telnet <host> <port>
#scan (one shot, not interactive)
nc -z <host> <port>
# For a quick non-interative check (with a 5 seconds timeout):
nc -z -w5 <host> <port>
# --
# SERVER
# note: low number ports (such as 80) require higher perms
# create socket that stays open indefinitely until manually closed
#listen locally on port 1024 for all traffic and dump the output to STDOUT
nc -l 1024 -k
# more verbose
nc -l 1024 -k -v
# close after first message
nc -l 1024
# more complex request (send mail)
nc localhost 25 << EOF
HELO host.example.com
MAIL FROM: <user@host.example.com>
RCPT TO: <user2@host.example.com>
DATA
Body of email.
.
QUIT
EOF
--
BASH BRACKETS/BRACES/PARANS
There are a lot of brackets in Bash, like () (()) [] [[]], and some have overloaded meanings, which can get confusing. Here's a quick overview of what they mean. These types of symbols are hard to search for explanations as well.
bracket options in 'if' statements
no brackets execute shell commands and tests exit code
0 == true in this case, which can be counter-intuitive
'exit code' should really be named 'error code', so
'no errors' means true. last code is stored in variable $?
[ ] are simpler, older, POSIX compliant.
it is usually just an alias to /bin/test, /bin/[
[[ ]] the double brackets are builting bash keywords
in general use [[ vs [ in tests if you don't need to port to other shells
it's newer and processes internally (better for whitespace handling)
The double bracket enables additional functionality.
For example, you can use && and || instead of -a and -o
and there's a regular expression matching operator =~
(( )) are used for arithemetic operations. for example
a=((a++))
for ((i=0; i<10; i++)); do echo $i; done;
$[ ] deprecated syntax meaning (( ))
other bracket contexts
( ) 1. are used to create a subshell, won't affect current shell
( cd /tmp; pwd ); pwd;
2. are also used to create arrays, [ ] used for array index
array=(1 2 3)
echo ${array[1]}
3. are also used used to define functions.
# without arg, called like a command line tool
f() { echo 1; }; f;
# with argument
f() { echo $1; }; f arg ;
4. process substitution is:
cmd <(list) is similar to cat list | cmd
or
cmd > >(list) is similar to cmd | list
The effect of process substitution is to make each list act like a file.
however >() is more powerful than a normal pipe:
You can't use | to redirect standard output and standard error to different programs.
{ } does a couple things:
1. unambiguously identifies variables. example ${a}${b}
2. executes a sequence of commands in the CURRENT shell context, opposite of ()
{ date; top -b -n1 | head ; } >logfile
There is a subtle syntactic difference with ( ), though (see bash reference) ; essentially, a semicolon ; after the last command within braces is a must,
and the braces {, } must be surrounded by spaces.
[ ] 1. can mean a range operator, or matching list
ls [abc][1-4]
2. can be the index designator in an array ${a[0]}
$( ) runs a command and drops the output in another. like backticks ``
usefule for nesting operations, example:
# find string 'bar' in files named 'foo'
grep bar $( find -name foo )
--
BASH STRING MANIPULATION
strip extension
nameis=${filename%.php}
nameis=${filename%.*} # general
substring examples:
The variable var contains /a/b/c/d/e.f.g:
Expression Result
${var} /a/b/c/d/e.f.g
# match first from left
${var#/*/} b/c/d/e.f.g
#match last from left
${var##/*/} e.f.g
# match first from right
${var%.*} /a/b/c/d/e.f
# match last from right
${var%%.*} /a/b/c/d/e
# misc
${var%%/*/} /a/b/c/d/e.f.g
${var%%/*}
${var%/b*} /a
${var%%/b*} /a
by length
stringZ=abcABC123ABCabc
# 0123456789.....
# 0-based indexing.
echo ${stringZ:0} # abcABC123ABCabc
echo ${stringZ:1} # bcABC123ABCabc
echo ${stringZ:7} # 23ABCabc
echo ${stringZ:7:3} # 23A
# Three characters of substring.
# Is it possible to index from the right end of the string?
echo ${stringZ:-4} # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . .
echo ${stringZ:(-4)} # Cabc
echo ${stringZ: -4} # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.
find replace (substring replacement)
Replace first match of $substring with $replacement:
${string/substring/replacement}
Replace all matches of $substring with $replacement.
${string//substring/replacement}
bash string editing operators
Operator Explanation
${variable#pattern} Delete the shortest part of pattern that matches the beginning of variable's value. Return the rest.
${variable##pattern} Delete the longest part of pattern that matches the beginning of variable's value. Return the rest.
${variable%pattern} Delete the shortest part of pattern that matches the end of variable's value.Return the rest.
${variable%%pattern} Delete the longest part of pattern that matches the end of variable's value.Return the rest.
The patterns can be filename wildcard characters: *, ?, and []; with string editing operators, wildcards match strings in the same way they match filenames. (These are not sed-like regular expressions.) The first two operators, with #, edit variables from the front. The other two, with %, edit from the end. Here's a system for remembering which does what: you put a number sign (#) at the front of a number and a percent sign (%) at the end of a number.
No comments:
Post a Comment