1.Variables
xxx@xxx-pc ~ $ salutation=hello
xxx@xxx-pc ~ $ echo $salutation
hello
xxx@xxx-pc ~ $ read salutation
what? //user in
xxx@xxx-pc ~ $ echo $salutation
what?
Try It Out:
#!/bin/sh
myvar="Hi there"
echo $myvar
echo "$myvar"
echo '$myvar'
echo \$myvar
echo Enter some text
read myvar
echo '$myvar' now equals $myvar
exit 0
-----------------------
//output:
Hi there
Hi there
$myvar
$myvar
Enter some text
Hello World //user-in
$myvar now equals Hello World
Environment Variables
$HOME //the home directory of the current user
$PATH //a colon-separated list of directories to search for commands
$PS1 //a cmd prompt, frequently $. [\u@\h \w] $
$PS2 //a secondary prompt, used when prompting for additional input; usually >.
$IFS //an input field separator.separate word ,usually space
$0 //shell script
$# //the number of parameters passed to script
$$ //process ID of shell script, use for example /tmp/tmpfile_$$
man env
Parameter Variables
$1,$2... //parameters
$* //a list of parameters.
$@
--ex.
--------
xx@xxx-pc ~ $ IFS='' /* '\0' */
xxx@xxx-pc ~ $ echo "$@"
foo bar bam
xxx@xxx-pc ~ $ echo "$*"
foobarbam
xxx@xxx-pc ~ $ unset IFS
xxx@xxx-pc ~ $ echo "$*"
foo bar bam
Try It Out
#!/bin/sh
salutation="Hello"
echo $salutation
echo "The program $0 is now running"
echo "The second parameter was $2"
echo "The first parameter was $1"
echo "The parameter list was $*"
echo "The user's home directory is $HOME"
echo "Please enter a new greeting"
read salutation
echo $salutation
echo "The script is now complete"
exit 0
------
------------------------
xxx@xxx-pc ~ $ sh 1.sh foo bar baz
Hello
The program 1.sh is now running
The second parameter was bar
The first parameter was foo
The parameter list was foo bar baz
The user's home directory is /home/xxx
Please enter a new greeting
sire //user-in
sire
The script is now complete
Conditions
The test or [ Command
xxx@xxx-pc ~ $ which test
/usr/bin/test
check a file exists.
xxx@xxx-pc ~ $ if test -f 1.sh
> then echo test
> fi
test
/* or */
xxx@xxx-pc ~ $ if [ -f 1.sh ]; then echo test; fi
test
The condition types that you can use with the test command fall into three types: string comparison,arithmetic comparison, and file conditionals. The following table describes these condition types:
/* String Comparison & Result */
string1 = string2 //True if the strings are equal
string1 != string2 //True if the strings are not equal
-n string //True if the string is not null
-z string //True if the string is null (an empty string)
/* Arithmetic Comparison & Result */
expression1 -eq expression2 //True if the expressions are equal
expression1 -ne expression2 // not equal
expression1 -gt expression2 // greater than
expression1 -ge expression2 // greater than or equal
expression1 -lt expression2 // less than
expression1 -le expression2 // less than or equal
! expression //True if the expression is false, and vice versa
/* File Conditional & Result */
-d file //True if the file is a directory
-e file /*True if the file exists. Note that historically the
-e option has not been portable, so -f is usually used.*/
-f file //True if the file is a regular(普通) file
-g file //True if set-group-id is set on file
-r file //True if the file is readable
-s file //True if the file has nonzero size
-u file //True if set-user-id is set on file
-w file //True if the file is writable
-x file //True if the file is executable
#!/bin/sh
if [ -f /bin/bash ]
then
echo "file /bin/bash exists"
fi
if [ -d /bin/bash ]
then
echo "/bin/bash is a directory"
else
echo "/bin/bash is NOT a directory"
fi
----
---------------
xxx@xxx-pc ~ $ sh 1.sh
file /bin/bash exists
/bin/bash is NOT a directory
Control Structures
if
#!/bin/sh
echo "It is morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ]; then
echo "Good morning"
else
echo "Good afternoon"
fi
exit 0
-----
-----------------
xxx@xxx-pc ~ $ sh 1.sh
It is morning? Please answer yes or no
yes //user-in
Good morning
elif
Try It Out :
#!/bin/sh
echo "It is morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ]; then
echo "Good morning"
elif [ $tmeofday = "no" ]; then
echo "Good afternoon"
else
echo "Sorry, $timeofday not recognized. Enter yes or no"
exit 1
fi
exit 0
------
--------------------
xxx@xxx-pc ~ $ sh 1.sh
It is morning? Please answer yes or no
//push Enter, is null
1.sh: 6: [: =: unexpected operator
1.sh: 8: [: =: unexpected operator
Sorry, not recognized. Enter yes or no
/* so we should alter like:
if [ "$timeofday" = "yes" ]; then
*/
bash allows echo -n to suppress(去除) the new line.
for
#!/bin/sh
for foo in bar fud 43
do
echo $foo
done
exit 0
-------
------------------
xxx@xxx-pc ~ $ sh 1.sh
bar
fud
43
/* find f*.sh */
#!/bin/sh
for file in $(ls f*.sh); do
echo $file
done
exit 0
while
a rather poor password-checking program:
#!/bin/sh
echo "Enter password"
read trythis
while [ "$trythis" != "secret" ]; do
echo "Sorry, try again"
read trythis
done
exit 0
---------
------------------
Enter password
sss //user-in
Sorry, try again
secret //user-in
until
until condition
do
statements
done
an until loop, you can set up an alarm that is initiated when another user, whose login name you pass on the command line**, logs on**
#!/bin/sh
until who | grep "$1" > /dev/null
do
sleep 60
done
#now ring the bell and announce the expected user.
echo -e '\a'
echo "**** $1 has just logged in ****"
exit 0
------
-------------------
xxx@xxx-pc ~ $ sh f1.sh xxx
-e
**** xxx has just logged in ****
case
case variable in
pattern [ | pattern] ...) statements;;
pattern [ | pattern] ...) statements;;
...
esac
- User Input
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes) echo "Good Morning";;
no ) echo "Good Afternoon";;
y ) echo "Good Morning";;
n ) echo "Good Afternoon";;
* ) echo "Sorry, answer not recognized";;
esac
exit 0
2 . Putting Patterns Together
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes | y | Yes | YES ) echo "Good Morning";;
n* | N* ) echo "Good Afternoon";;
* ) echo "Sorry, answer not recognized";;
esac
exit 0
3.Executing Multiple Statements
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes | y | Yes | YES )
echo "Good Morning"
echo "Up bright and early this morning"
;;
[nN]*)
echo "Good Afternoon"
;;
*)
echo "Sorry, answer not recognized"
echo "Please answer yes or no"
exit 1
;;
esac
exit 0
/* more powerful : [yY] | [Yy][Ee][Ss] ) */
Lists
The AND List
#!/bin/sh
touch file_one
rm -f file_two
if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
then
echo "in if"
else
echo "in else"
fi
exit 0
-----------
------------------------
hello
in else
The OR List
execute a series of commands until one succeeds, and then not execute any more.
#!/bin/sh
rm -f file_one
if [ -f file_one ] || echo "hello" || echo " there"
then
echo "in if"
else
echo "in else"
fi
exit 0
----
--------------------
hello
in if
[ -f file_one ] && command for true || command for false
Statement Blocks
{} to make a statement block.
get_confirm && {
grep -v "$cdcatnum" $tracks_file > $temp_file
cat $temp_file > $tracks_file
echo
add_record_tracks
}
Functions
function_name () {
statements
}
#!/bin/sh
foo() {
echo "Function foo is executing"
}
echo "script starting"
foo
echo "script ended"
exit 0
----
----------------
script starting
Function foo is executing
script ended
#!/bin/sh
sample_text="global variable"
foo() {
local sample_text="local_variable"
echo "Function foo is executing"
echo $sample_text
}
echo "script starting"
echo $sample_text
foo
echo "script ended"
echo $sample_text
exit 0
---------
-----------------
script starting
global variable
Function foo is executing
local_variable
script ended
global variable
Try It Out
#!/bin/sh
yes_or_no() {
echo "Is your name $* ?"
while true
do
echo -n "Enter yes or no: "
read x
case "$x" in
y | yes) return 0;;
n | no) return 1;;
*) echo "Answer yes or no"
esac
done
}
echo "Original parameters are $*"
if yes_or_no "$1"
then
echo "Hi $1, nice name"
else
echo "Never mind"
fi
exit 0
------
---------------
Original parameters are xxx
Is your name xxx ?
Enter yes or no: y
Hi xxx, nice name
Commands
break
#!/bin/sh
rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4
for file in fred*
do
if [ -d "$file" ]; then
break;
fi
done
echo first directory starting fred was $file
rm -rf fred*
exit 0
----
--------------
first directory starting fred was fred3
The : Command
The colon command is a null command.
#!/bin/sh
rm -f fred
if [ -f fred ]; then
:
else
echo file fred did not exist
fi
exit 0
continue
#!/bin/sh
rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4
for file in fred*
do
if [ -d "$file" ]; then
echo "skipping directory $file"
continue;
fi
echo file is $file
done
rm -rf fred*
exit 0
######
####
file is fred1
file is fred2
skipping directory fred3
file is fred4
continue can take the enclosing loop number at which to resume as an optional parameter so that you can partially jump out of nested loops. This parameter is rarely used, as it often makes scripts much harder to understand. For example
#!/bin/sh
for x in 1 2 3
do
echo before $x
continue 1
echo after $x
done
exit 0
####
before 1
before 2
before 3
The . Command
usually create a subshell for a script.
The dot ( . ) command executes the command in the current shell:
. ./shell_script
set the environment
#!/bin/sh
version=classic
PATH=/usr/local/old_bin:/usr/bin:/bin:.
PS1="classic> "
-------------
#!/bin/sh
version=latest
PATH=/usr/local/new_bin:/usr/bin:/bin:.
PS1="latest version>"
#############
####
$ . ./classic_set
classic> echo $version
classic
classic> . ./latest_set
latest version> echo $version
latest
latest version>
The scripts are executed using the dot command, so each script is executed in the current shell. This enables the script to change environment settings in the current shell, which remains changed even when the script finishes executing.
echo
suppressing a newline
echo -n “string to output” // linux
echo -e “string to output\c”// \c for suppressing a newline
eval
The eval command enables you to evaluate arguments. It’s built into the shell and doesn’t normally exist as a separate command.
xxx@xxx-pc ~ $ foo=10
xxx@xxx-pc ~ $ x=foo
xxx@xxx-pc ~ $ y='$'$x
xxx@xxx-pc ~ $ echo $y
$foo
xxx@xxx-pc ~ $ foo=10
xxx@xxx-pc ~ $ x=foo
xxx@xxx-pc ~ $ eval y='$'$x
xxx@xxx-pc ~ $ echo $y
10
printf
printf “format string“ parameter1 parameter2 ...
$ printf “%s\n” hello
hello
$ printf “%s %d\t%s” “Hi There” 15 people
Hi There 15 people
return
return takes a single numeric parameter that is available to the script calling the function. If no parameter is specified, then return defaults to the exit code of the last command.
set
The set command sets the parameter variables for the shell.
#!/bin/sh
echo the date is $(date)
set $(date)
echo The month is $2
exit 0
#########
####
the date is Sat Sep 5 14:33:06 CST 2015
The month is Sep
This program sets the parameter list to the date command’s output and then uses the positional parameter $2 to get at the month.
set -x
which makes a script display a trace of its currently executing command.
shift
The shift command moves all the parameter variables down by one.
you can scan through all the positional parameters like this:
#!/bin/sh
while [ "$1" != "" ]; do
echo "$1"
shift
done
exit 0
trap
The trap command is used to specify the actions to take on receipt(接收) of signals.
trap -l
. detail : man 7 signal
trap command signal
To reset a trap condition to the default, simply specify the command as - .To ignore a signal, set the command to the empty string “” . A trap command with no parameters prints out the current list of traps and actions.
Signal & Description
HUP(1) Hang up(挂起); usually sent when a terminal goes offline, or a user logs out
INT(2) Interrupt(中断); usually sent by pressing Ctrl+C
QUIT(3) Quit; usually sent by pressing Ctrl+\
ABRT(6) Abort(中止); usually sent on some serious execution error
ALRM(14) Alarm; usually used for handling timeouts
TERM(15) Terminate(终止); usually sent by the system when it’s shutting down
#!/bin/sh
trap 'rm -f /tmp/my_tmp_file_$$' INT
echo creating file /tmp/my_tmp_file_$$
date > /tmp/my_tmp_file_$$
echo "press interrupt (CTRL_C) to interrupt ...."
while [ -f /tmp/my_tmp_file_$$ ]; do
echo File exists
sleep 1
done
echo The file no longer exists
trap INT // null to do
echo creating file /tmp/my_tmp_file_$$
date > /tmp/my_tmp_file_$$
echo "press interrupt (control-c) to interrupt ...."
while [ -f /tmp/my_tmp_file_$$ ]; do
echo File exists
sleep 1
done
echo we never get here
exit 0
##########
######
creating file /tmp/my_tmp_file_628
press interrupt (CTRL_C) to interrupt ....
File exists
File exists
File exists
^CThe file no longer exists
creating file /tmp/my_tmp_file_628
press interrupt (control-c) to interrupt ....
File exists
File exists
File exists
File exists
File exists
^C
unset
The unset command removes variables or functions from the environment. It can’t do this to read-only variables defined by the shell itself, such as IFS. It’s not often used.
#!/bin/sh
foo="Hello World"
echo $foo
unset foo
echo $foo
########
####
Hello World
Two More Useful Commands and Regular Expressions
The find Command
find / -name test -print
find / -mount -name test -print /* find quickly, don't search other filesystem directories*/
The full syntax for the find command is as follows:
find [path] [options] [tests] [actions]
There are several options; the main ones are shown in the following table:
Option & Meaning
-depth Search the contents of a directory before looking at the directory itself.
-follow Follow symbolic links.
-maxdepths N Search at most N levels of the directory when searching.
-mount (or -xdev ) Don’t search directories on other file systems.
Now for the tests. A large number of tests can be given to find , and each test returns either true or false .
Test Meaning
-atime N The file was last accessed N days ago.
-mtime N The file was last modified N days ago.
-name pattern The name of the file
-newer otherfile The file is newer than otherfile .
-type C type C, the most common are “d” for a directory and “f” for a regular file.
-user username The file is owned by the user with the given name.
You can also combine tests using operators. Most have two forms: a short form and a longer form, as shown in the following table:
Operator, Short Form Operator, Long Form Meaning
! -not Invert the test.
-a -and Both tests must be true.
-o -or Either test must be true.
write the test “newer than file X or called a name that starts with an underscore,” you could write the following test:
\( -newer X -o -name "_*" \)
example:
$ find . -newer while2 -print
.
./elif3
./words.txt
./words2.txt
./_trap
###################
$ find . -newer while2 -type f -print
./elif3
./words.txt
./words2.txt
./_trap
find . \( -newer f1.sh -o -name "_*" \) -type f -print
//你必须转义圆括号保证不会被shell处理, 把*号用引号括起保证直接传递给find.
this is just a list of the most common actions:
Action & Meaning
-exec command Execute a command. This action must be terminated with a \; character pair.
-ok command Like -exec, prompts before executing. terminated with a \; character pair.
-print Print out the name of the file.
-ls Use the command ls -dils on the current file.
//-exec 和 -ok之后直到\; 都是其参数; 它们是嵌入式命令, 以\;结束.
The magic string “{}” is a special type of parameter to an -exec or -ok command and is replaced with the full path to the current file.
xxx@xxx-pc ~ $ find . -newer f1.sh -type f -exec ls -l {} \;
The grep Command
general regular expression parser.(通用正则表达式解析器) You use find to search your system for files, but you use grep to search files for strings. Indeed, it’s quite common to have grep as a command passed after -exec when using find .
grep [options] PATTERN [FILES]
If no filenames are given, it searches standard input.
Option & Meaning
-c print a count of the number of lines that match.
-E Turn on extended expressions.
-h Suppress the normal prefixing of each output line with the name of the file it was found in.
-i Ignore case.
-l List the names of the files with matching lines; don’t output the actual matched line.
-v Invert the matching pattern to select nonmatching lines, rather than matching lines.
$ grep in words.txt //search "in"
When shall we three meet again. In thunder, lightning, or in rain?
I come, Graymalkin!
$ grep -c in words.txt words2.txt
words.txt:2
words2.txt:14
$ grep -c -v in words.txt words2.txt
words.txt:9
words2.txt:16
Regular Expressions
certain characters are processed in a special way.
Character & Meaning
^ Anchor(指向) to the beginning of a line
$ Anchor to the end of a line
. Any single character
[ ] The square braces contain a range of characters, any one of which may be matched, such as a range of characters like a–e or an inverted range by preceding the range with a ^ symbol.
Match Pattern & Meaning
[:alnum:] Alphanumeric(字母和数字) characters
[:alpha:] Letters
[:ascii:] ASCII characters
[:blank:] Space or tab
[:cntrl:] ASCII control characters
[:digit:] Digits
[:graph:] Noncontrol, nonspace characters
[:lower:] Lowercase letters
[:print:] Printable characters
[:punct:] Punctuation(标点符号) characters
[:space:] Whitespace characters, including vertical tab
[:upper:] Uppercase letters
[:xdigit:] Hexadecimal(十六进制) digits
if the -E for extended matching is also specified, other characters that control the completion of matching may follow the regular expression . With grep it is also necessary to precede these characters with a \ .
Option & Meaning
? Match is optional but may be matched at most once
* Must be matched zero or more times
+ Must be matched one or more times
{n} Must be matched n times
{n,} Must be matched n or more times
{n,m} Must be matched between n or m times, inclusive
xxx@xxx-pc ~ $ grep h$ *.sh //line end in h
f1.sh:#!/bin/sh
f2.sh:#!/bin/sh
xxx@xxx-pc ~ $ grep h[[:blank:]] *.sh //word end in h
f2.sh:echo The month is $2
xxx@xxx-pc ~ $ grep Th.[[:space:]] *.sh //three-letter start with Th
f2.sh:echo The month is $2
xxx@xxx-pc ~ $ grep -E [a-z]\{5\} *.sh //five-letter word
f1.sh:unset foo
f2.sh:echo The month is $2
Command Execution
#!/bin/sh
echo The current directory is $PWD
echo The current users are $(who)
exit 0
############
####
The current directory is /home/xxx
The current users are xxx tty8 2015-09-05 09:35 (:0) xxx pts/2 2015-09-05 13:47 (:0)
Since the current directory is a shell environment variable, the first line doesn’t need to use this command execution construct.
xxx@xxx-pc ~ $ whoisthere=$(who)
xxx@xxx-pc ~ $ echo $whoisthere
xxx tty8 2015-09-05 09:35 (:0) xxx pts/2 2015-09-05 13:47 (:0)
If you ever find yourself trying to convert a set of parameters that are the output of a command on standard output and capture(捕获) them as arguments for a program, you may well find the command xargs can do it for you.
Arithmetic Expansion
#!/bin/sh
x=0
while [ "$x" -ne 10 ]; do
echo $x
x=$(($x+1))
done
exit 0
Parameter Expansion
Parameter Expansion & Description
${param:-default} If param is null, then set it to the value of default .
${#param} Gives the length of param
${param%word} From the end, removes the smallest part of param that matches word and returns the rest
${param%%word} From the end, removes the longest part of param that matches word and returns the rest
${param#word} From the beginning, removes the smallest part of param that matches word and returns the rest
${param##word} From the beginning, removes the longest part of param that matches word and returns the rest
#!/bin/sh
unset foo
echo ${foo:-bar}
foo=fud
echo ${foo:-bar}
foo=/usr/bin/X11/startx
echo ${foo#*/}
echo ${foo##*/}
bar=/usr/local/etc/local/networks
echo ${bar%local*}
echo ${bar%%local*}
exit 0
##########
####
bar
fud
usr/bin/X11/startx
startx
/usr/local/etc/
/usr/
convert a GIF file into a JPEG file using the cjpeg program:
$ cjpeg image.gif > image.jpg
#!/bin/sh
for image in *.gif
do
cjpeg $image > ${image%%gif}jpg
done
exit 0
Here Documents
One special way of passing input to a command from a shell script is to use a here document.
#!/bin/sh
cat <<!FUNKY!
hello
this is a here
document
!FUNKY
###########
####
hello
this is a here
document
#!/bin/sh
ed a_text_file <<!FunkyStuff! // ed editor
3 //move to 3rd line
d //delete
.,\$s/is/was/ //current line, replace
w //write
q //quit
!FunkyStuff!
exit 0
##########
####
xxx@xxx-pc ~ $ cat a_text_file
That is line 1
That is line 2
That was line 4
Debugging Scripts
Command Line | Option set Option | Description
sh -n <script> | set -o noexec | Checks for syntax errors only;
| set -n | doesn’t execute commands
sh -v <script> | set -o verbose | Echoes(回显) commands before
| set -v | running them
sh -x <script> | set -o xtrace | Echoes commands after pro-
| set -x | cessing on the command line
sh -u <script> | set -o nounset | Gives an error message when
| set -u | an undefined variable is used
For an initial check, you can use the command-line option, but for finer debugging, you can put the xtrace flags (setting an execu-
tion trace on and off) inside the script around the problem code. The execution trace causes the shell to print each line in the script, with variables expanded, before executing the line.
Use the following command to turn xtrace on:
set -o xtrace
Use this command to turn xtrace off again:
set +o xtrace
In the shell, you can also find out the program state wherever it exits by trapping the EXIT signal with a line something like the following placed at the start of the script:
trap ‘echo Exiting: critical variable = $critical_variable’ EXIT