首页 > 系统相关 > 从入门到掌握 - 系统学习shell语言

从入门到掌握 - 系统学习shell语言

时间:2023-08-29 20:55:13浏览次数:55  
标签:返回 10 shell 入门 echo file Output true 语言

简介

什么是 shell

Shell是一种程序或命令行解释程序,用于解释用户直接输入的用户命令或从文件中读取的用户命令,然后将 它们传递给操作系统以进行操作或处理。要注意,这个过程是解释而不编译脚本,因为计算机系统会解释它 们,并且无需按执行顺序编译Shell脚本。Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是 一个典型的图形界面 Shell。
  • Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。
  • Shell 既是一种命令语言,又是一种程序设计语言。
  • Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问 Linux 内核的服务。

什么是 shell 脚本

Shell脚本是一个纯文本文件,其中包含一组通常在命令行中键入的各种命令。它用于在Linux文件系统上自动

执行重复性任务。它可能包含一组命令或一个命令,或者可能包含命令式编程的标志,例如循环,函数,条件

构造等。Shell 脚本(shell script),是一种为 shell 编写的脚本程序,一般文件后缀为 .sh

Shell 环境

Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

Shell 的解释器种类众多,常见的有:

  • sh - 即 Bourne Shell。sh 是 Unix 标准默认的 shell。
  • bash - 即 Bourne Again Shell。bash 是 Linux 标准默认的 shell。
  • fish - 智能和用户友好的命令行 shell。
  • xiki - 使 shell 控制台更友好,更强大。
  • zsh - 功能强大的 shell 与脚本语言。

指定脚本解释器

在 shell 脚本, #!  告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。 #!  被称作shebang(也称为 Hashbang )。

所以,你应该会在 shell 中,见到诸如以下的注释:

  • 指定 sh 解释器
 
#!/bin/sh
  • 指定 bash 解释器
 
#!/bin/bash

注意

上面的指定解释器的方式是比较常见的,但有时候,你可能也会看到下面的方式:

 
#!/usr/bin/env bash

这样做的好处是,系统会自动在 PATH 环境变量中查找你指定的程序(本例中的bash)。相比第一种写法,你应该尽量用这种写法,因

为程序的路径是不确定的。这样写还有一个好处,操作系统的PATH变量有可能被配置为指向程序的另一个版本。比如,安装完新版本

bash,我们可能将其路径添加到PATH中,来“隐藏”老版本。如果直接用 #!/bin/bash ,那么系统会选择老版本的bash来执行脚本,如

果用 #!/usr/bin/env bash ,则会使用新版本。

模式

shell 有交互和非交互两种模式。

交互模式

简单来说,你可以将 shell 的交互模式理解为执行命令行。

看到形如下面的东西,说明 shell 处于交互模式下:

 
user@host:~$

接着,便可以输入一系列 Linux 命令,比如 lsgrepcdmkdirrm 等等。

非交互模式

简单来说,你可以将 shell 的非交互模式理解为执行 shell 脚本。

在非交互模式下,shell 从文件或者管道中读取命令并执行。

当 shell 解释器执行完文件中的最后一个命令,shell 进程终止,并回到父进程。

可以使用下面的命令让 shell 以非交互模式运行:

 
sh /path/to/script.sh
bash /path/to/script.sh
source /path/to/script.sh
./path/to/script.sh

上面的例子中,script.sh是一个包含 shell 解释器可以识别并执行的命令的普通文本文件,shbash

shell 解释器程序。你可以使用任何喜欢的编辑器创建script.sh(vim,nano,Sublime Text, Atom 等等

)。其中,source /path/to/script.sh和 ./path/to/script.sh 是等价的。除此之外,你还可以通过

chmod命令给文件添加可执行的权限,来直接执行脚本文件:

 
chmod +x /path/to/script.sh #使脚本具有执行权限
/path/to/test.sh

这种方式要求脚本文件的第一行必须指明运行该脚本的程序,比如:

 
#!/usr/bin/env bash
echo "Hello, world!"

上面的例子中,我们使用了一个很有用的命令echo来输出字符串到屏幕上。

基本语法

解释器

前面虽然两次提到了 #! ,但是本着重要的事情说三遍的精神,这里再强调一遍:

在 shell 脚本, #!  告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。 #! 被称作shebang(也称为 Hashbang )。

 #! 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入shbashpythonphp等。

 
# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash

注释

注释可以说明你的代码是什么作用,以及为什么这样写。

shell 语法中,注释是特殊的语句,会被 shell 解释器忽略。

  • 单行注释 - 以 # 开头,到行尾结束。
  • 多行注释 - 以 :<<EOF 开头,到 EOF 结束。
 
#--------------------------------------------
# shell 注释示例
#--------------------------------------------

# echo '这是一行文字'

########## 这是分割线 ##########

:<<EOF
echo '这是一行文字'
echo '这是一行文字'
echo '这是一行文字'
EOF

echo

echo 用于字符串的输出。

输出普通字符串:

 
echo "hello, world"
# Output: hello, world

输出含变量的字符串:

 
echo "hello, \"zp\""
# Output: hello, "zp"

输出含变量的字符串:

 
name=zp
echo "hello, \"${name}\""
# Output: hello, "zp"

输出含换行符的字符串:

 
# 输出含换行符的字符串
echo "YES\nNO"
#  Output: YES\nNO

echo -e "YES\nNO" # -e 开启转义
#  Output:
#  YES
#  NO

输出含不换行符的字符串:

 
echo "YES"
echo "NO"
#  Output:
#  YES
#  NO

echo -e "YES\c" # -e 开启转义 \c 不换行
echo "NO"
#  Output:
#  YESNO

输出重定向至文件

 
echo "test" > test.txt

输出执行结果

 
echo `pwd`
#  Output:(当前目录路径)

printf

printf 用于格式化输出字符串。

默认,printf 不会像 echo 一样自动添加换行符,如果需要换行可以手动添加 \n

 
# 单引号
printf '%d %s\n' 1 "abc"
#  Output:1 abc

# 双引号
printf "%d %s\n" 1 "abc"
#  Output:1 abc

# 无引号
printf %s abcdef
#  Output: abcdef(并不会换行)

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出
printf "%s\n" abc def
#  Output:
#  abc
#  def

printf "%s %s %s\n" a b c d e f g h i j
#  Output:
#  a b c
#  d e f
#  g h i
#  j

# 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替
printf "%s and %d \n"
#  Output:
#   and 0

# 格式化输出
printf "%-10s %-8s %-4s\n"   姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 小明 男 66.1234
printf "%-10s %-8s %-4.2f\n" 小亮 男 48.6543
printf "%-10s %-8s %-4.2f\n" 小红 女 47.9876
#  Output:
#  姓名     性别   体重kg
#  小明     男      66.12
#  小亮     男      48.65
#  小红     女      47.99

printf 的转义符

序列说明
\a 警告字符,通常为 ASCII 的 BEL 字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b 格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\\ 一个字面上的反斜杠字符
\ddd 表示 1 到 3 位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示 1 到 3 位的八进制值字符

变量

跟许多程序设计语言一样,你可以在 bash 中创建变量。

Bash 中没有数据类型,bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。

变量命名原则

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。

声明变量

访问变量的语法形式为: ${var}  和  $var  。

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。

 
word="hello"
echo ${word}
# Output: hello

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

 
rword="hello"
echo ${rword}
readonly rword
# rword="bye"  # 如果放开注释,执行时会报错

删除变量

使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。

 
dword="hello"  # 声明变量
echo ${dword}  # 输出变量值
# Output: hello

unset dword    # 删除变量
echo ${dword}
# Output: (空)

变量类型

  • 局部变量 - 局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。
  • 环境变量 - 环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 export 关键字,shell 脚本也可以定义环境变量。

常见的环境变量:

变量描述
$HOME 当前用户的用户目录
$PATH 用分号分隔的目录列表,shell 会到这些目录中查找命令
$PWD 当前工作目录
$RANDOM 0 到 32767 之间的整数
$UID 数值类型,当前用户的用户 ID
$PS1 主要系统输入提示符
$PS2 次要系统输入提示符

这里 有一张更全面的 Bash 环境变量列表。

字符串

单引号和双引号

shell 字符串可以用单引号 '',也可以用双引号 “”,也可以不用引号。

  • 单引号的特点
    • 单引号里不识别变量
    • 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。
  • 双引号的特点
    • 双引号里识别变量
    • 双引号里可以出现转义字符

综上,推荐使用双引号。

拼接字符串

 
# 使用单引号拼接
name1='white'
str1='hello, '${name1}''
str2='hello, ${name1}'
echo ${str1}_${str2}
# Output:
# hello, white_hello, ${name1}

# 使用双引号拼接
name2="black"
str3="hello, "${name2}""
str4="hello, ${name2}"
echo ${str3}_${str4}
# Output:
# hello, black_hello, black

获取字符串长度

 
text="12345"
echo ${#text}
# Output:
# 5

截取子字符串

 
text="12345"
echo ${text:2:2}
# Output:
# 34

从第 3 个字符开始,截取 2 个字符

查找子字符串

 
#!/usr/bin/env bash

text="hello"
echo `expr index "${text}" ll`

# Execute: ./str-demo5.sh
# Output:
# 3

查找 ll 子字符在 hello 字符串中的起始位置。

数组

bash 只支持一维数组。

数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。

创建数组

 
# 创建数组的不同方式
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")

访问数组元素

  • 访问数组的单个元素:
 
echo ${nums[1]}
# Output: 1
  • 访问数组的所有元素:
 
echo ${colors[*]}
# Output: red yellow dark blue

echo ${colors[@]}
# Output: red yellow dark blue

上面两行有很重要(也很微妙)的区别:

为了将数组中每个元素单独一行输出,我们用 printf 命令:

 
printf "+ %s\n" ${colors[*]}
# Output:
# + red
# + yellow
# + dark
# + blue

为什么darkblue各占了一行?尝试用引号包起来:

 
printf "+ %s\n" "${colors[*]}"
# Output:
# + red yellow dark blue

现在所有的元素都在一行输出 —— 这不是我们想要的!让我们试试 ${colors[@]} 

 
printf "+ %s\n" "${colors[@]}"
# Output:
# + red
# + yellow
# + dark blue

在引号内, ${colors[@]} 将数组中的每个元素扩展为一个单独的参数;数组元素中的空格得以保留。

  • 访问数组的部分元素:
 
echo ${nums[@]:0:2}
# Output:
# 0 1

在上面的例子中, ${array[@]}  扩展为整个数组,:0:2取出了数组中从 0 开始,长度为 2 的元素。

访问数组长度

 
echo ${#nums[*]}
# Output:
# 3

向数组中添加元素

向数组中添加元素也非常简单:

 
colors=(white "${colors[@]}" green black)
echo ${colors[@]}
# Output:
# white red yellow dark blue green black

上面的例子中, ${colors[@]}  扩展为整个数组,并被置换到复合赋值语句中,接着,对数组colors的赋值覆盖了它原来的值。

从数组中删除元素

unset命令来从数组中删除一个元素:

 
unset nums[0]
echo ${nums[@]}
# Output:
# 1 2

运算符

算术运算符

下表列出了常用的算术运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
+ 加法 expr $x + $y 结果为 30。
- 减法 expr $x - $y 结果为 -10。
* 乘法 expr $x * $y 结果为 200。
/ 除法 expr $y / $x 结果为 2。
% 取余 expr $y % $x 结果为 0。
= 赋值 x=$y 将把变量 y 的值赋给 x。
== 相等。用于比较两个数字,相同则返回 true。 [ $x == $y ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $x != $y ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$x==$y] 是错误的,必须写成 [ $x == $y ]

 
x=10
y=20

echo "x=${x}, y=${y}"

val=`expr ${x} + ${y}`
echo "${x} + ${y} = $val"

val=`expr ${x} - ${y}`
echo "${x} - ${y} = $val"

val=`expr ${x} \* ${y}`
echo "${x} * ${y} = $val"

val=`expr ${y} / ${x}`
echo "${y} / ${x} = $val"

val=`expr ${y} % ${x}`
echo "${y} % ${x} = $val"

if [[ ${x} == ${y} ]]
then
  echo "${x} = ${y}"
fi
if [[ ${x} != ${y} ]]
then
  echo "${x} != ${y}"
fi

#  Execute: ./operator-demo.sh
#  Output:
#  x=10, y=20
#  10 + 20 = 30
#  10 - 20 = -10
#  10 * 20 = 200
#  20 / 10 = 2
#  20 % 10 = 0
#  10 != 20

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ]返回 false。
-ne 检测两个数是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ]返回 true。
 

x=10 y=20 echo "x=${x}, y=${y}" if [[ ${x} -eq ${y} ]]; then echo "${x} -eq ${y} : x 等于 y" else echo "${x} -eq ${y}: x 不等于 y" fi if [[ ${x} -ne ${y} ]]; then echo "${x} -ne ${y}: x 不等于 y" else echo "${x} -ne ${y}: x 等于 y" fi if [[ ${x} -gt ${y} ]]; then echo "${x} -gt ${y}: x 大于 y" else echo "${x} -gt ${y}: x 不大于 y" fi if [[ ${x} -lt ${y} ]]; then echo "${x} -lt ${y}: x 小于 y" else echo "${x} -lt ${y}: x 不小于 y" fi if [[ ${x} -ge ${y} ]]; then echo "${x} -ge ${y}: x 大于或等于 y" else echo "${x} -ge ${y}: x 小于 y" fi if [[ ${x} -le ${y} ]]; then echo "${x} -le ${y}: x 小于或等于 y" else echo "${x} -le ${y}: x 大于 y" fi # Execute: ./operator-demo2.sh # Output: # x=10, y=20 # 10 -eq 20: x 不等于 y # 10 -ne 20: x 不等于 y # 10 -gt 20: x 不大于 y # 10 -lt 20: x 小于 y # 10 -ge 20: x 小于 y # 10 -le 20: x 小于或等于 y

布尔运算符

下表列出了常用的布尔运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
 

x=10 y=20 echo "x=${x}, y=${y}" if [[ ${x} != ${y} ]]; then echo "${x} != ${y} : x 不等于 y" else echo "${x} != ${y}: x 等于 y" fi if [[ ${x} -lt 100 && ${y} -gt 15 ]]; then echo "${x} 小于 100 且 ${y} 大于 15 : 返回 true" else echo "${x} 小于 100 且 ${y} 大于 15 : 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]]; then echo "${x} 小于 100 或 ${y} 大于 100 : 返回 true" else echo "${x} 小于 100 或 ${y} 大于 100 : 返回 false" fi if [[ ${x} -lt 5 || ${y} -gt 100 ]]; then echo "${x} 小于 5 或 ${y} 大于 100 : 返回 true" else echo "${x} 小于 5 或 ${y} 大于 100 : 返回 false" fi # Execute: ./operator-demo3.sh # Output: # x=10, y=20 # 10 != 20 : x 不等于 y # 10 小于 100 且 20 大于 15 : 返回 true # 10 小于 100 或 20 大于 100 : 返回 true # 10 小于 5 或 20 大于 100 : 返回 false

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 x 为 10,变量 y 为 20:

运算符说明举例
&& 逻辑的 AND [[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false
|| 逻辑的 OR [[ ${x} -lt 100 || ${y} -gt 100 ]] 返回 true
 

x=10 y=20 echo "x=${x}, y=${y}" if [[ ${x} -lt 100 && ${y} -gt 100 ]] then echo "${x} -lt 100 && ${y} -gt 100 返回 true" else echo "${x} -lt 100 && ${y} -gt 100 返回 false" fi if [[ ${x} -lt 100 || ${y} -gt 100 ]] then echo "${x} -lt 100 || ${y} -gt 100 返回 true" else echo "${x} -lt 100 || ${y} -gt 100 返回 false" fi # Execute: ./operator-demo4.sh # Output: # x=10, y=20 # 10 -lt 100 && 20 -gt 100 返回 false # 10 -lt 100 || 20 -gt 100 返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg":

运算符说明举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为 0,为 0 返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为 0,不为 0 返回 true。 [ -n $a ] 返回 true。
str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
 

x="abc" y="xyz" echo "x=${x}, y=${y}" if [[ ${x} = ${y} ]]; then echo "${x} = ${y} : x 等于 y" else echo "${x} = ${y}: x 不等于 y" fi if [[ ${x} != ${y} ]]; then echo "${x} != ${y} : x 不等于 y" else echo "${x} != ${y}: x 等于 y" fi if [[ -z ${x} ]]; then echo "-z ${x} : 字符串长度为 0" else echo "-z ${x} : 字符串长度不为 0" fi if [[ -n "${x}" ]]; then echo "-n ${x} : 字符串长度不为 0" else echo "-n ${x} : 字符串长度为 0" fi if [[ ${x} ]]; then echo "${x} : 字符串不为空" else echo "${x} : 字符串为空" fi # Execute: ./operator-demo5.sh # Output: # x=abc, y=xyz # abc = xyz: x 不等于 y # abc != xyz : x 不等于 y # -z abc : 字符串长度不为 0 # -n abc : 字符串长度不为 0 # abc : 字符串不为空

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符说明举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ]返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
 

file="/etc/hosts" if [[ -r ${file} ]]; then echo "${file} 文件可读" else echo "${file} 文件不可读" fi if [[ -w ${file} ]]; then echo "${file} 文件可写" else echo "${file} 文件不可写" fi if [[ -x ${file} ]]; then echo "${file} 文件可执行" else echo "${file} 文件不可执行" fi if [[ -f ${file} ]]; then echo "${file} 文件为普通文件" else echo "${file} 文件为特殊文件" fi if [[ -d ${file} ]]; then echo "${file} 文件是个目录" else echo "${file} 文件不是个目录" fi if [[ -s ${file} ]]; then echo "${file} 文件不为空" else echo "${file} 文件为空" fi if [[ -e ${file} ]]; then echo "${file} 文件存在" else echo "${file} 文件不存在" fi # Execute: ./operator-demo6.sh # Output:(根据文件的实际情况,输出结果可能不同) # /etc/hosts 文件可读 # /etc/hosts 文件可写 # /etc/hosts 文件不可执行 # /etc/hosts 文件为普通文件 # /etc/hosts 文件不是个目录 # /etc/hosts 文件不为空 # /etc/hosts 文件存在

控制语句

条件语句

跟其它程序设计语言一样,Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在[[ ]]里的表达式。

[[ ]]sh中是[ ])包起来的表达式被称作 检测命令 或 基元。这些表达式帮助我们检测一个条件的结果。这里可以找到有关bash 中单双中括号区别的答案。

共有两个不同的条件表达式:ifcase

if

(1)if 语句

if在使用上跟其它语言相同。如果中括号里的表达式为真,那么thenfi之间的代码会被执行。fi标志着条件代码块的结束。

 
# 写成一行
if [[ 1 -eq 1 ]]; then echo "1 -eq 1 result is: true"; fi
# Output: 1 -eq 1 result is: true

# 写成多行
if [[ "abc" -eq "abc" ]]
then
  echo ""abc" -eq "abc" result is: true"
fi
# Output: abc -eq abc result is: true

(2)if else 语句

同样,我们可以使用if..else语句,例如:

 
if [[ 2 -ne 1 ]]; then
  echo "true"
else
  echo "false"
fi
# Output: true

(3)if elif else 语句

有些时候,if..else不能满足我们的要求。别忘了if..elif..else,使用起来也很方便。

 
x=10
y=20
if [[ ${x} > ${y} ]]; then
   echo "${x} > ${y}"
elif [[ ${x} < ${y} ]]; then
   echo "${x} < ${y}"
else
   echo "${x} = ${y}"
fi
# Output: 10 < 20

case

如果你需要面对很多情况,分别要采取不同的措施,那么使用case会比嵌套的if更有用。使用case来解决复杂的条件判断,看起来像下面这样:

 
exec
case ${oper} in
  "+")
    val=`expr ${x} + ${y}`
    echo "${x} + ${y} = ${val}"
  ;;
  "-")
    val=`expr ${x} - ${y}`
    echo "${x} - ${y} = ${val}"
  ;;
  "*")
    val=`expr ${x} \* ${y}`
    echo "${x} * ${y} = ${val}"
  ;;
  "/")
    val=`expr ${x} / ${y}`
    echo "${x} / ${y} = ${val}"
  ;;
  *)
    echo "Unknown oper!"
  ;;
esac

每种情况都是匹配了某个模式的表达式。|用来分割多个模式,)用来结束一个模式序列。第一个匹配上的模式对应的命令将会被执行。*代表任何不匹配以上给定模式的模式。命令块儿之间要用;;分隔。

循环语句

循环其实不足为奇。跟其它程序设计语言一样,bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。

Bash 中有四种循环:forwhileuntilselect

for循环

for与它在 C 语言中的姊妹非常像。看起来是这样:

 
for arg in elem1 elem2 ... elemN
do
  ### 语句
done

在每次循环的过程中,arg依次被赋值为从elem1elemN。这些值还可以是通配符或者大括号扩展

当然,我们还可以把for循环写在一行,但这要求do之前要有一个分号,就像下面这样:

 
for i in {1..5}; do echo $i; done

还有,如果你觉得for..in..do对你来说有点奇怪,那么你也可以像 C 语言那样使用for,比如:

 
for (( i = 0; i < 10; i++ )); do
  echo $i
done

当我们想对一个目录下的所有文件做同样的操作时,for就很方便了。举个例子,如果我们想把所有的.bash文件移动到script文件夹中,并给它们可执行权限,我们的脚本可以这样写:

 
DIR=/home/zp
for FILE in ${DIR}/*.sh; do
  mv "$FILE" "${DIR}/scripts"
done
# 将 /home/zp 目录下所有 sh 文件拷贝到 /home/zp/scripts

while循环

while循环检测一个条件,只要这个条件为 ,就执行一段命令。被检测的条件跟if..then中使用的基元并无二异。因此一个while循环看起来会是这样:

 
while [[ condition ]]
do
  ### 语句
done

for循环一样,如果我们把do和被检测的条件写到一行,那么必须要在do之前加一个分号。

比如下面这个例子:

 
### 0到9之间每个数的平方
x=0
while [[ ${x} -lt 10 ]]; do
  echo $((x * x))
  x=$((x + 1))
done
#  Output:
#  0
#  1
#  4
#  9
#  16
#  25
#  36
#  49
#  64
#  81

until循环

until循环跟while循环正好相反。它跟while一样也需要检测一个测试条件,但不同的是,只要该条件为  就一直执行循环:

 
x=0
until [[ ${x} -ge 5 ]]; do
  echo ${x}
  x=`expr ${x} + 1`
done
#  Output:
#  0
#  1
#  2
#  3
#  4

select循环

select循环帮助我们组织一个用户菜单。它的语法几乎跟for循环一致:

 
select answer in elem1 elem2 ... elemN
do
  ### 语句
done

select会打印elem1..elemN以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是$?PS3变量)。用户的选择结果会被保存到answer中。如果answer是一个在1..N之间的数字,那么语句会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用break语句。

一个可能的实例可能会是这样:

 
#!/usr/bin/env bash

PS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} in
  bower) bower install ${PACKAGE} ;;
  npm) npm install ${PACKAGE} ;;
  gem) gem install ${PACKAGE} ;;
  pip) pip install ${PACKAGE} ;;
esac
break # 避免无限循环
done

这个例子,先询问用户他想使用什么包管理器。接着,又询问了想安装什么包,最后执行安装操作。

运行这个脚本,会得到如下输出:

 
$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: gitbook-cli

break 和 continue

如果想提前结束一个循环或跳过某次循环执行,可以使用 shell 的breakcontinue语句来实现。它们可以在任何循环中使用。

break语句用来提前结束当前循环。

continue语句用来跳过某次迭代。

 
# 查找 10 以内第一个能整除 2 和 3 的正整数
i=1
while [[ ${i} -lt 10 ]]; do
  if [[ $((i % 3)) -eq 0 ]] && [[ $((i % 2)) -eq 0 ]]; then
    echo ${i}
    break;
  fi
  i=`expr ${i} + 1`
done
# Output: 6
 
# 打印10以内的奇数
for (( i = 0; i < 10; i ++ )); do
  if [[ $((i % 2)) -eq 0 ]]; then
    continue;
  fi
  echo ${i}
done
#  Output:
#  1
#  3
#  5
#  7
#  9

函数

bash 函数定义语法如下:

 
[ function ] funname [()] {
    action;
    [return int;]
}

标签:返回,10,shell,入门,echo,file,Output,true,语言
From: https://www.cnblogs.com/mq0036/p/17665802.html

相关文章

  • 数据库备份和Shell基础测试及AWK(运维)
    第一题:使用MySQL命令进行备份和恢复的步骤如下:备份test库:使用mysqldump命令备份test库,并将备份写入一个.sql文件中。命令示例:mysqldump-u用户名-p密码test>backup.sql恢复备份:使用mysql命令将备份文件中的数据恢复到test库中。命令示例:mysql-u用户名-p密码test<backu......
  • 1-8汇编语言程序上机调试
    COM_8255EQU0273H ;8255控制口PA_8255EQU0270HPB_8255EQU0271HPC_8255EQU0272H_STACKSEGMENTSTACKDW100DUP(?)_STACKENDSDATASEGMENTWORDPUBLIC'DATA'DATAENDSCODESEGMENTSTARTPROCNEARASSUMECS:CODE,DS:DATA,SS:_STACK......
  • MySQL数据库:第十四章:(DML)Data Manipulation Language数据操纵语言
    回退至Mysql数据库理论与实战#DML语句★DataManipulationLanguage数据操纵语言关键字:insert 、update、deleteUSEstu0906;CREATETABLEstuinfo(idINT,stunameVARCHAR(20)NOTNULL,genderCHAR,borndate TIMESTAMP,seatINT);#一、插入语法:插入单行:insertinto表......
  • draw.io快速入门(下)
    6图像和图层本章的学习内容在draw.io中创建、重命名和重新排列图层插入图片作为模板,并在顶层重建图表查看、隐藏和锁定特定图层删除图层使用图层构建图表可以让你的工作更加灵活--你可以在图表的不同视图之间切换,将相关元素分组,并保护它们在不同图层中工作时不会被修改。......
  • ThinkPHP 多语言本地文件包含漏洞
    ThinkPHP多语言本地文件包含漏洞ThinkPHP是一个再中国使用比较多的PHP框架。在其6.0.13版本及以前,存在一处本地文件包含漏洞。当多语言特性被开启时,攻击者可以使用lang参数来包含任意php文件。虽然只能包含本地PHP文件,但在开启了register_argc_argv且安装了pcel/pear的环境下,可......
  • C语言指针进阶
    目录字符指针指针数组数组指针数组指针的定义&数组名VS数组名数组指针的使用数组参数、指针参数一维数组传参二维数组传参一级指针传参二级指针传参一级指针二级指针数组指针函数指针函数指针数组指向函数指针数组的指针回调函数编码的三种境界:1.看代码就是代码2.看代码就是内......
  • R语言之基础绘图
    文章和代码已经归档至【Github仓库:<https://github.com/timerring/dive-into-AI>】或者公众号【AIShareLab】回复R语言也可获取。R的基础绘图系统由RossIhaka编写,功能非常强大,主要由graphics包和grDevices包组成,它们在启动R时会自动加载。基础绘图系统中有两类函数,一......
  • Redis 入门篇
    1.初始Redis1.1认识NoSQL‍NoSql可以翻译做NotOnlySql(不仅仅是SQL),或者是NoSql(非Sql的)数据库。是相对于传统关系型数据库而言,有很大差异的一种特殊的数据库​,因此也称之为非关系型数据库。‍1.1.1结构化与非结构化‍​​1.1.2关系型与非关系型的差异存储方式......
  • C++语言学习02
    一、函数重载1、什么是函数重载在同一作用域下,函数名相同,参数列表不同的函数构成重载关系函数重载与返回值的类型、参数名无关与作用域是否相同,以及参数列表的数量、参数类型、常属性不同等有关2、C++是如何实现函数重载的?通过g++-S的方式生成汇编代码可以知道,编译器......
  • 1.5 编写自定位ShellCode弹窗
    在笔者上一篇文章中简单的介绍了如何运用汇编语言编写一段弹窗代码,虽然简易ShellCode可以被正常执行,但却存在很多问题,由于采用了硬编址的方式来调用相应API函数的,那么就会存在一个很大的缺陷,如果操作系统的版本不统或系统重启过,那么基址将会发生变化,此时如果再次调用基址参数则会......