tartarus's bolg tartarus's bolg
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • pdb

  • make

  • cmake

  • Linux and Unix

    • Linux Tool介绍
    • Useful Linux Tool
    • regular-expression
    • Basic-Shell-Script
      • 本章内容
      • Getting Started
      • Using variables
        • command subsitution
      • Redirecting input
      • Performing Math
        • expr
        • dollar sign + square brackets
        • Bash shell数学运算的局限性
        • 在scripts中使用bc
        • inline input redirection
      • Exiting the script
    • Using-Structured-Commands
    • More-Structured-Commands
    • Handling-User-Input
    • Presenting-Data
    • Script-Control
  • Basic_Software
  • Linux and Unix
tartarus
2023-05-07
目录

Basic-Shell-Script

# 本章内容

  • Getting Started
  • Using variables
  • Inline Redirecting Input
  • Performing math
  • Exiting the script

# Getting Started

  • 所有脚本 (shell, python...) 都要使用 shabang ( #! ) 置于行首
#!/usr/bin/bash
1
#!/usr/bin/python3
1

提示

使用 which -a python3 命令可以列出环境变量中已经包含的所有 pyton3 的路径

注意

使用 .\ script.sh 时 shell 脚本默认在 subshell 中执行
如果需要将脚本运行在当前 shell 中,可以使用 . script.sh (在 bash 中有效)

举例:

$ cat script.sh
echo $var

$ var="hello world"
$ . script.sh # 说明`. script.sh`使得脚本在当前shell中执行
hello world
$ ./script.sh # 说明`./script.sh`使得脚本不在当前shell中执行

$ pwd
/tmp
$ /tmp/script.sh # 不在当前shell中执行

$
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 使用 echo 添加文本信息,以帮助脚本用户了解脚本中发生的情况。

    • 如果需要使用 echo 展示 " 和 ' 需要使用 back slash 进行转译 ( \" , ' )。
    • 添加 '-n' 命令使得 echo 不会在结尾输出换行符。举例如下:
    $ cat script.sh
    #!/usr/bin/bash
    echo -n "The time and date are: "
    date
    $ ./script.sh
    The time and date are: Mon 12 Jun 2023 03:30:04 AM EDT
    
    1
    2
    3
    4
    5
    6

# Using variables

  • 设置局部变量

    • 为了便于区别,建议局部变量都使用小写字母,全局变量都是用大写字母。这样可以防止局部变量的命名和系统默认的全局变量命名发生冲突。

    • 在变量名中、等号的左右值中都不要使用 space,否则导致变量名被识别为一个变量。

    # 正确使用方法
    my_variales="hello world"
    
    # my_variables会被识别为一个命令,该命令有两个参数
    my_variales = "hello world"
    
    1
    2
    3
    4
    5

    举例:

    $ my_variales = "hello world"
    my_variales: command not found
    $
    
    1
    2
    3
    • 局部变量只能在当前 shell 进程中使用,其他任何 shell 进程 (该 shell 进程的子进程或者父进程) 都无法使用在当前 shell 进程中设置的局部变量。如果需要在其他 shell 进程中使用当前 shell 进程中创建的变量,可以将变量创建为全局变量。
    $ var=hello
    $ echo $var
    hello
    $ bash
    $ echo $var
    
    $
    
    1
    2
    3
    4
    5
    6
    7
    • 若赋给变量的值中间有 space,需要使用 " 将值括起来或者使用 \ 对 space 进行转译。
    • 若要将 shell ' , " 等,需要使用 \ 对进行转译。
    # 正确使用方法
    my_variales="hello \'ning\'" # or my_variales=hello\ \'ning\'
    
    1
    2
  • 设置全局变量

    • 全局变量可以被当前进程的 shell 和子进程的 shell 使用。

    • 使用命令 export 将变量设置为全局变量。

    export my_variables="I am Global now"
    
    1

    提示

    如果需要在其他进程 shell (除当前进程的 shell 和子进程的 shell 外) 中也使用该全局变量,可以把它加入.zshrc or .bashrc 中。

    • 更改子进程 shell 中全局变量的值不会改变父进程 shell 中的全局变量值。
  • 使用命令 unset 删除环境变量

my_variable="I am going to be removed"
unset my_variables
1
2

在子进程中 unset 删除的全局变量不会删除其在父进程的值。

$ export my_variable="I am Global now" 
$ echo $my_variable 
I am Global now 
$
$ bash 
$ echo $my_variable 
I am Global now 
$ unset my_variable 
$ echo $my_variable 

$ exit 
exit 
$ echo $my_variable 
I am Global now 
$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 设置全局变量 PATH
# 可以不使用"",但是有时会产生错误,为了养成好习惯都使用""(因为有时候路径中有空格,不使用""就会造成错误)
PATH="$PATH:/home/christine/Scripts"

# or 
PATH="/home/christine/Scripts:$PATH"
1
2
3
4
5
  • 设置变量数组
$ mytest=(zero one two three four)

$ echo $mytest
zero

$ echo ${mytest[2]}
two

$ echo ${mytest[*]}
zero one two three four

$ unset mytest

$ echo ${mytest[*]}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

shell script 中设置的变量,当脚本执行结束后,定义在 shell 脚本中的变量也会被删除。

$ cat test3 
#!/bin/bash 
days=10 
guest="Katie" 
echo "$guest checked in $days days ago" 
days=5 
guest="Jessica" 
echo "$guest checked in $days days ago" 
$
$ ./script.sh
Katie checked in 10 days ago
Jessica checked in 5 days ago
1
2
3
4
5
6
7
8
9
10
11
12

注意

在脚本中使用变量时需要使用 $ ,给变量赋值时不需要 $ 。

举例:

$ cat test4 
#!/bin/bash 
# assigning a variable value to another variable 
value1=10 
value2=$value1
echo "value1 is $value1"
echo "value2 is $value2"
1
2
3
4
5
6
7

output:

value1 is 10
value2 is 10
1
2

# command subsitution

(command substitution: 在脚本运行线程的子进程中运行命令,并返回命令的输出)

将一个命令的输出赋值给某个变量在 shell script 中是非常常见的操作,有两种方法可以实现:

  • backtick```(重音符)
  • $()
# backtick
$ testing=`date`
# $()
$ testing=$(date)
1
2
3
4

有了 command substitution 我们就可以做很多 fancy 的事情,比如:

#!/bin/bash 
# copy the /usr/bin directory listing to a log file 
today=$(date +%y%m%d) # 或者使用 today=`date +%y%m%d` 
echo "today is $today"
ls -al /usr/bin > log.$today
1
2
3
4
5

下面所示的代码中,command substitution 和 ./script 都会创建 subshell,然后使用 echo $var 查看变量 var 的值,但是为什么他们一个会保留父进程的局部变量,一个不会呢?

$ var=5
$ (echo "$var")
5
$ cat script.sh
#!/bin/bash
echo $var

$ ./script.sh

$
1
2
3
4
5
6
7
8
9
10

这是因为 command substitution 会 fork 出一个子进程,然后运行程序;而 ./script 会 fork 一个子进程,然后调用 exec 执行一个外部命令,外部命令的执行会覆盖子进程的进程空间。

参考资料:
Why is a variable visible in a subshell? (opens new window)
Differences between fork and exec (opens new window)

# Redirecting input

输入重定向还有一种方式叫做 inline input redirection .
这种重定向方式使得我们可以从命令行中输入而不是从文件中。需要在开头使用的开头和结尾使用一个 marker 字符串 (marker 可以是任意的字符或者字符串,而且结尾的 marker 字符必须在一行的开头)。

# 本例中marker为i_am_a_marker,并且在shell中而不是shell脚本中运行本例
$ wc << i_am_a_marker
heredoc> It have 4 words
heredoc> i_am_a_marker   
       1       4      16
1
2
3
4
5

将 cat 命令的标准输入重定向为从命令行输入

$ cat >> testfile << EOF
heredoc> hello      
heredoc> good morning 
heredoc> EOF

$ cat testfile
hello 
good morning
1
2
3
4
5
6
7
8

# Performing Math

在 shell script 中有两种方式可以对数字进行计算:

# expr

Unix Bourne shell 提供命令 expr 专门用来处理数学运算。

$ expr 1 + 5
6
1
2

expr 可以使用的数学操作符有:。

ARG1 | ARG2
Return ARG1 if neither argument is null or 0; otherwise, return ARG2 .
ARG1 & ARG2Return ARG1 if neither argument is null or 0; otherwise, return 0.
ARG1 < ARG2 Return 1 if ARG1 is less than ARG2 ; otherwise, return 0.
ARG1 = ARG2 Return 1 if ARG1 is equal to ARG2 ; otherwise, return 0.
ARG1 <= ARG2 Return 1 if ARG1 is less than or equal to ARG2 ; otherwise, return 0.
ARG1 != ARG2 Return 1 if ARG1 is not equal to ARG2 ; otherwise, return 0.
ARG1 >= ARG2 Return 1 if ARG1 is greater than or equal to ARG2 ; otherwise, return 0.
ARG1 > ARG2 Return 1 if ARG1 is greater than ARG2 ; otherwise, return 0.
ARG1 + ARG2 Return the arithmetic sum of ARG1 and ARG2 .
ARG1 - ARG2Return the arithmetic difference of ARG1 and ARG2 .
ARG1 * ARG2 Return the arithmetic product of ARG1 and ARG2 .
ARG1 / ARG2 Return the arithmetic quotient of ARG1 divided by ARG2 .
ARG1 % ARG2 Return the arithmetic remainder of ARG1 divided by ARG2 .
STRING : REGEXP Return the pattern match if REGEXP matches a pattern in STRING .
match STRING REGEXP Return the pattern match if REGEXP matches a pattern in STRING .
substr STRING POS LENGTH Return the substring LENGTH characters in length, starting at position POS (starting at 1).
index STRING CHARS Return position in STRING where CHARS is found; otherwise, return 0.
length STRING + TOKEN (EXPRESSION) Return the numeric length of the string STRING .
+ Interpret TOKEN as a string, even if it's a keyword.
(EXPRESSION) Return the value of EXPRESSION .

但是如果运算符在 bash 中有其他含义 (比如 * 在 bash 中是一个通配符),那么就可能产生错误,需要使用 backslash 来进行 Escape:

$ expr 1 + 3
4
$ expr 1 * 3
expr: syntax error
$ expr 1 \* 3
3
1
2
3
4
5
6

# dollar sign + square brackets

因此,bash 的设计者提供了另一种简单的方式,使得执行时如果使用到了通配符等,不需要进行 Escape: 使用 dollar sign + square brackets 来表示数学运算。

$ var1=$[1 + 5] 
$ echo $var1 
6
$ var2=$[$var1 * 2] 
$ echo $var2 
12
1
2
3
4
5
6

# Bash shell 数学运算的局限性

Bash shell 中只能进行整型运算,无法进行浮点数的运算。

$ cat test8 
#!/bin/bash 
var1=100 
var2=45 
var3=$[$var1 / $var2] 
echo The final result is $var3

$ chmod u+x test8 
$ ./test8
The final result is 2
1
2
3
4
5
6
7
8
9
10

提示

zsh 中提供了浮点数运算。

  • Bash 中无法使用浮点运算的解决方法

bc (bash calculator) 是一种允许在 bash 中进行浮点运算的编程语言,类似于在 python 解释器进行数学运算,bc 中可以出现:

  1. Numbers (both integer and floating point)
  2. Variables (both simple variables and arrays)
  3. Comments (lines starting with a pound sign or the C language /* */ pair)
  4. Expressions Programming statements (such asif-then statements)
  5. Functions

使用方法:

$ bc
>>> 12 * 5.4
64.8
>>> scale
0
>>> scale = 4
>>> 12 / 13
.9230
>>> var1 = 10
>>> var2 = 4
>>> var3 = var1 / var2
>>> print var3
2.5000 
>>> quit
1
2
3
4
5
6
7
8
9
10
11
12
13
14

提示

使用内置变量 scale 来设置需要保留的浮点位数。

# 在 scripts 中使用 bc

使用 command substitution 将 bc 运算后的结果保存到变量中,使用格式为:

variable=$(echo "options; expression" | bc)
1

其中 option 用来设置 bc 中的变量和调整 scale (精确位数);expression 用来定义数学表达式。
举例:

$ cat test9 
#!/bin/bash 
var1=$(echo " scale=4; 3.44 / 5" | bc) 
echo The answer is $var1
1
2
3
4

可以使用 shell script 中的变量在 bc 中进行运算操作:

$ cat test10 
#!/bin/bash 
var1=100 
var2=45 
var3=$(echo "scale=4; $var1 / $var2" | bc)
echo The answer for this is $var3
1
2
3
4
5
6

# inline input redirection

上面这种方法的缺点是:如果计算涉及的计算较复杂,而且不能在一个数学表达式中直接计算出来时,使用上面这种方式就不太合适,这个时候,我们就可以使用 inline input redirection :

variable=$(bc << EOF 
options 
statements 
expressions 
EOF 
)
1
2
3
4
5
6

option: 用来设置精度
statements: 用来设置变量
expressions: 用来设置算数表达式
上面的例子中 EOF 作为 marker

inline input redirection 前面我们已经介绍过了,所以这里只举一个例子进行说明:

$ cat test12 
#!/bin/bash 
var1=10.46 
var2=43.67 
var3=33.2 
var4=71 

var5=$(bc << EOF 
scale = 4 
a1 = ( $var1 * $var2) 
b1 = ($var3 * $var4) 
a1 + b1 
EOF 
) 

echo The final answer for this mess is $var5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

提示

在带有 bc 的脚本中,bc 中创建的变量只能在 bc 中使用,不能在 scirpts 中使用。 但是 bc 的运算中的运算结果可以赋值给 bc 外的变量。

# Exiting the script

shell 中每条命令都执行完成后都有 exit status. exit status 是 0 到 255 中值。

Linux 提供 $? 来记录上一条执行命令的 exit status.

一些参考的退出状态码:

Code Description
0 Successful completion of the command
1 General unknown error 2 Misuse of shell command
126 The command can't execute
127 Command not found
128 Invalid exit argument
128+x Fatal error with Linux signalx
130 Command terminated with Ctrl+C
255 Exit status out of range

退出状态码是非常有用的,它可以用来检查一条命令的执行情况,下个 chapter 我们将介绍 if-then 语句来检查 shell script 中命令的状态码。

通常来说 shell script 的退出状态码默认是 script 中最后一条命令的退出状态码。也可以使用 exit 指定 scipt 的退出状态码:

$ cat test13 
#!/bin/bash 
# testing the exit status 
var1=10 
var2=30 
var3=$[ $var1 + var2 ] 
echo The answer is $var3 
exit 5
1
2
3
4
5
6
7
8
上次更新: 12/27/2023, 8:55:47 AM
regular-expression
Using-Structured-Commands

← regular-expression Using-Structured-Commands→

Theme by Vdoing | Copyright © 2023-2023 tartarus | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式