For our first shell script, we'll just write a script which says "Hello World". We will then try to get more out of a Hello World program than any other tutorial you've ever read
#!/bin/sh echo"What is your name?"readUSER_NAME
echo"Hello $USER_NAME"echo"I will create you a file called ${USER_NAME}_file"shell现知指代的是USER_NAME且我们希望它以_file为后缀
touch"${USER_NAME}_file"
", $, `, and \ are still interpreted by the shell, even when they're in double quotes.
The backslash (\) character is used to mark these special characters so that they are not interpreted by the shell, but passed on to the command being run
Bash
1 2 3 4 5 6 7 8 91011121314
Aim:
1.Aquoteis", backslash is \, backtick is `.2. A few spaces are and dollar is $. $X is 5.Code:1. ➜ shell-script echo "Aquoteis\",backslashis\\backtickis\`"A quote is ",backslashis\ backtickis`
➜shell-script2.
➜shell-scriptexportX=5
➜shell-scriptecho"A few spaces are and dollar is \$. \$X is ${X}."
Afewspacesareanddollaris$.$Xis5.
➜shell-script
if[...];then# do somethingfi# Commonly: "if [ ... ]" and "then" commands must be on different lines# Alternatively: the semicolon ";" can separate them
#!/bin/sh#test.shif["$X"-lt"0"]thenecho"X is less than zero"fiif["$X"-gt"0"];thenecho"X is more than zero"fi["$X"-le"0"]&&\echo"X is less than or equal to zero"["$X"-ge"0"]&&\echo"X is more than or equal to zero"["$X"="0"]&&\echo"X is the string or number \"0\""["$X"="hello"]&&\echo"X matches the string \"hello\""["$X"!="hello"]&&\echo"X is not the string \"hello\""[-n"$X"]&&\echo"X is of nonzero length"[-f"$X"]&&\echo"X is the path of a real file"||\echo"No such file: $X"[-x"$X"]&&\echo"X is the path of an executable file"["$X"-nt"/etc/passwd"]&&\echo"X is a file which is newer than /etc/passwd"
-------------------------------------------------------------------------------------
➜ shell-script X=hello
➜ shell-script ./test.sh
./test.sh: 2: [: Illegal number: hello
./test.sh: 6: [: Illegal number: hello
./test.sh: 9: [: Illegal number: hello
./test.sh: 11: [: Illegal number: hello
X matches the string "hello"
X is of nonzero length
No such file: hello
Text Only
1 2 3 4 5 6 7 8 910111213
-------------------------------------------------------------------------------------
➜ shell-script X=test.sh
➜ shell-script ./test.sh
./test.sh: 2: [: Illegal number: test.sh
./test.sh: 6: [: Illegal number: test.sh
./test.sh: 9: [: Illegal number: test.sh
./test.sh: 11: [: Illegal number: test.sh
X is not the string "hello"
X is of nonzero length
X is the path of a real file
X is the path of an executable file
X is a file which is newer than /etc/passwd
➜ shell-script
if["$X"-nt"/etc/passwd"];thenecho"X is a file which is newer than /etc/passwd"fi
-------------------------------------------------------------------------------------
["$X"-nt"/etc/passwd"]&&\echo"X is a file which is newer than /etc/passwd"
(2) test 可对数字、字符串和文件名执行许多测试 :
(1) There is a simpler way of writing if statements: The && and || commands give code to run if the result is true, or false, respectively.
[ A judgement B ] && Action1 || Action2
if the result of [...] is TRUE => Action1
else => Action2
(2) The categories of 'judgement' in daily usage [File]:
-a -e : 文件存在
-s : 文件是套接字socket
-nt : file is newer than
-ot : file is older than
-ef : paths refer to the same file
-O : file is owned by the user running the test
(3) The categories of 'judgement' in daily usage [Num]:
-lt、-gt、-le 和 -ge 比较仅适用于整数,不适用于字符串
-lt : less than
-gt : greater than
-le : less than or equal to
-ge : greater than or equal to
(4) The categories of 'judgement' in daily usage [String]:
#!/bin/sh#code_originX=0while[-n"$X"]doecho"Enter some text (RETURN to quit)"readX
echo"You said: $X"done
-------------------------------------------------------------------------------------
$./test2.sh
Entersometext(RETURNtoquit)
fred
Yousaid:fred
Entersometext(RETURNtoquit)
wilma
Yousaid:wilma
Entersometext(RETURNtoquit)
Yousaid:
$
-------------------------------------------------------------------------------------
注意,运行此脚本将以不整齐的方式结束!如何优化输出?
-------------------------------------------------------------------------------------
#!/bin/sh#code_nowX=0while[-n"$X"]doecho"Enter some text (RETURN to quit)"readX
if[-n"$X"];thenecho"You said: $X"fidone
while:
doreadSTRING
case$STRINGina)do_A# 如果 STRING 匹配 a,则执行该代码段,直到双分号;;b)do_B
;;...
*)do_ending
;;esac# case statement is ended with `esac`done# we end the while loop with a `done`# 如果我们想完全退出脚本,那么我们将使用命令 exit 而不是 break
#!/bin/shecho"Please talk to me ..."while:
doreadINPUT_STRING
echo"$INPUT_STRING"case$INPUT_STRINGinhello)echo"Hello yourself!";;bye)echo"See you again!"break;;*)echo"Sorry, I don't understand";;esacdone#echo echo"That's all folks!"
$*, is similar, but does not preserve any whitespace, and quoting (遇见空格自动截断的"变色龙"参数) => "File with spaces" becomes "File" "with" "spaces"
As a general rule, use $@ and avoid $*
$# is the number of parameters the script was called with. (调用脚本时使用的参数数目)
Bash
1234567
#!/bin/sh#code:echo"There are $# parameters from the input shell"($#总参数数量)echo"1. My name is $0"($0程序名)
echo"2. My first parameter is $1"($!展示第一个参数)echo"3. My second parameter is $2"($2展示第二个参数)
echo"All parameters are $@"($@展示全部参数)
As we mentioned in Variables - Part I, curly brackets around a variable( { } ) avoid confusion:
That's not all, though - these fancy brackets have a another, much more powerful use. We can deal with issues of variables being undefined or null (in the shell, there's not much difference between undefined and null).
=> {variable} 可以处理变量未定义或 null 的问题
This could be done better using a shell variable feature. By using curly braces and the special ":-" usage, you can specify a default value to use if the variable is unset.(大括号 和 “:-” => 可指定在变量未设置时使用默认值)
#!/bin/sh# name_new.shecho-en"1) What is your name [ `whoami` ] "readmyname
echo"Your name is : ${myname:-`whoami`}"# mynameDEFAULT = 系统导入的个人IDecho-en"2) What is your age?"readMYAGE
echo"Your name is : ${MYAGE:-Guess hhh}"# myageDEFAULT = Guess hhh
External programs are often used within shell scripts; there are a few builtin commands (echo, which, and test are commonly builtin), but many useful commands are actually
Unix utilities, such as tr, grep, expr and cut.
A function may return a value in one of four different ways:
- Change the state of a variable or variables
更改一个或多个变量的状态
- Use the exit command to end the shell script
使用命令 exit 结束 shell 脚本
- Use the return command to end the function, and return the supplied value to the calling section of the shell script
使用该 return 命令结束函数,并将提供的值返回到 shell 脚本的调用部分
- echo output to stdout, which will be caught by the caller just as c=expr $a + $b is caught
回显输出到 stdout,调用方将在 c='expr $a + $b' 被捕获时捕获
#!/bin/sh# A simple script with a function...
add_a_user()# 在调用函数之前,不会执行此代码; 函数被读入,但在实际调用它们之前基本上被忽略{USER=$1# $USER=bobPASSWORD=$2# $PASSWORD=letmeinshift;shift;# 指向初始位置参数的指针向后移动了2位( 1,2,3,4,5... => x,x,1,2,3,... )# Having shifted twice, the rest is now comments ...COMMENTS=$@# $COMMENTS=Bob Holness the presenterecho"Adding user $USER ..."echouseradd-c"$COMMENTS"$USERechopasswd$USER$PASSWORDecho"Added user $USER ($COMMENTS) with pass $PASSWORD"}#### Main body of script starts here###echo"Start of script..."
add_a_userbobletmeinBobHolnessthepresenter# 函数调用
add_a_userfredbadpasswordFredDurstthesinger# 函数调用
add_a_userbilkoworsepasswordSgt.Bilkotherolemodel# 函数调用echo"End of script..."
1. Within that function, $1 is set to bob, regardless of what $1 may be set to outside of the function
2. So if we want to refer to the "original" $1 inside the function, we have to assign a name to it - such as: A=$1 before we call the function. Then, within the function, we can refer to $A
3. Use the shift command again to get the $3 and onwards parameters into $@. The function then adds the user and sets their password.
Programmers used to other languages may be surprised at the scope rules for shell functions. Basically, there is no scoping, other than the parameters ($1, $2, $@, etc).
#!/bin/sh
myfunc(){echo"I was called as : $@"x=2}### Main script starts here echo"Script was called with $@"# 命令行传递给执行脚本的参数(a,b,c)x=1# 变量x 实际上是一个全局变量echo"x is $x"
myfunc123# 传递给函数体的参数(1,2,3)echo"x is $x"# 说明函数体内对应的x跟外面的一样:都是"全局变量"!
#!/bin/sh
myfunc(){echo"\$1 is $1"echo"\$2 is $2"# cannot change $1 - we'd have to say:# 1="Goodbye Cruel"# which is not a valid syntax. However, we can# change $a:a="Goodbye Cruel"}### Main script starts here a=Hello
b=World
myfunc$a$becho"a is $a"echo"b is $b"
Functions cannot change the values they have been called with, either - this must be done by changing the variables themselves, not the parameters as passed to the script.
As promised, we will now briefly discuss using libraries between shell scripts. These can also be used to define common variables, as we shall see.
MainFunc: 简洁包装,代码重用
Bash
1 2 3 4 5 6 7 8 91011121314151617
# common.lib 表示这是一个名为 "common.lib" 的库文件# Note no #!/bin/sh as this should not spawn # an extra shell. It's not the end of the world # to have one, but clearer not to. 这个脚本是库文件,不需要启动额外的 shell#STD_MSG="About to rename some files..."用于存储标准消息的字符串变量
rename(){# expects to be called as: rename .txt .bak FROM=$1TO=$2foriin*$FROM遍历[当前目录下]所有文件名,其中文件名以`$FROM`结尾
doj=`basename$i$FROM`使用basename命令获取文件名(去掉路径),并将其中的$FROM部分去除,结果赋给变量j
mv$i${j}$TO使用`mv`命令将文件重命名,将`$FROM`替换为`$TO`done}
Here we see two user shell scripts, function2.sh and function3.sh, each sourceing the common library file common.lib, and using variables and functions declared in that file
For now, though we shall briefly look at the return call.
Bash
1 2 3 4 5 6 7 8 91011121314151617181920212223
#!/bin/sh
adduser(){USER=$1PASSWORD=$2shift;shiftCOMMENTS=$@useradd-c"${COMMENTS}"$USERif["$?"-ne"0"];thenecho"Useradd Failed"return1fipasswd$USER$PASSWORDif["$?"-ne"0"];thenecho"Setting password failed"return2fiecho"Added user $USER ($COMMENTS) with pass $PASSWORD"}
Bash
1 2 3 4 5 6 7 8 910111213
## Main Script starts here:
adduserhbx2223410945ROOTHBXfromXJTU
ADDUSER_RETURN_CODE=$?if["$ADDUSER_RETURN_CODE"-eq"1"];thenecho"Useradd went wrong!"elif["$ADDUSER_RETURN_CODE"-eq"1"];thenecho"Passwd went wrong!"elseecho"Con: You are added into this system!"fi
➜shell-script
For some time, this tutorial wrongly checked "$?" both times, rather than setting ADDUSER_RETURN_CODE=$?, and then looking at the value of ADDUSER_RETURN_CODE each time. This was a bug. You have to save the value of $? immediately, because as soon as you run another command, such as if, its value will be replaced. That is why we save the adduser return value in the $ADDUSER_RETURN_CODE variable, before acting on its content. $ADDUSER_RETURN_CODE is certain to remain the same; $? will change with every command that is executed.
Unix is full of text manipulating utilities, some of the more powerful of which we will now discuss in this section of this tutorial. The significance of this, is that virtually everything under Unix is text. Virtually anything you can think of is controlled by either a text file, or by a command-line-interface (CLI). The only thing you can't automate using a shell script is a GUI-only utility or feature. And under Unix, there aren't too many of them!