首页 > Linux > Shell 编程入门
2024
12-20

Shell 编程入门

Shell 编程入门 - 第1张  | Weiguang的博客

Shell 是一个应用程序,充当用户界面和脚本解释器,是用户使用 Linux 系统的桥梁。大多数 Linux 发行版将 Bash 作为默认的 Shell 程序,本文将以 Bash 为例,介绍如何在 Linux 环境进行 Shell 编程。Shell 脚本是一种以 Shell 程序为解释器的脚本程序。由于习惯的原因,业界所说的”Shell 编程”都是指编写 Shell 脚本,不是指 Shell 程序扩展开发。

1 一个简单的 Hello World 脚本

创建一个名为 hello.sh 的文本文件,输入一下内容并保存退出。

# Hello world demo
#!/bin/bash

echo 'Hello world!'

第一行以 # 开头表示注释,说明脚本的功能;

第二行以 #! 开头指定使用的 Shell 程序;

最后一行使用 echo 命令向屏幕打印 “Hello World!”。

shell脚本的执行有两种方式,一种是显示使用 Shell 程序执行脚本,另一种方式是给脚本添加可执行权限后直接执行脚本。

# 1. 使用 Shell 程序执行脚本
# 使用 bash 执行脚本
bash hello.sh

# 2. 先为脚本添加可执行权限,然后直接执行脚本
chmod +x hello.sh
./hello.sh

其中第二种方式直接执行脚本需要指定脚本所在路径,否则将在 Path 环境变量中检索脚本。

2 变量

2.1 定义变量

Shell 脚本中使用如下方式定义变量,注意变量名和等号之间不能有空格。

# 直接定义变量
my_name="Bob"

# 在语句中给变量赋值,例如在 for 循环中给变量赋值
for file in `ls /etc`
for file in ./xxx

2.2 使用变量

使用一个定义过的变量,只需要在变量名前面加美元符号即可,例如:

my_name="Bob"   # 定义字符串变量 my_name
echo $my_name   # 打印 my_name 的值

# 可以使用 export 将局部变量转换为全局变量,可以使用 env 命令查看所有的全局变量
export my_name

2.3 重新定义变量

直接给已经定义的变量赋值就好。

my_name="Bob"
echo <span class="katex math inline">my_name

my_name="Tom"  # 重新给变量赋值
echo</span>my_name

3 注释

# 号开头的行就是注释,Shell 里没有多行注释,只能每一行加一个 #,如下:

# 这是一行注释
# 这也是一行注释

4 字符串

Shell 中的字符串可以用单引号,可以用双引号,也可以不用引号。

4.1 单引号字符串

str='This is a string.'
echo <span class="katex math inline">str

my_name="Bob"
str='I am</span>my_name'
echo $str

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
  • 单引号字符串中不能出现单引号,单引号中的单引号将会被忽略(对单引号使用转义字符也不行)

4.2 双引号字符串

my_name="Bob"
greeting="Hello, I am <span class="katex math inline">my_name!\n"
echo</span>greeting

my_name="Tom"
greeting="Hello, I am <span class="katex math inline">{my_name}!\n"
echo</span>greeting
  • 双引号字符串里可以有变量

  • 双引号字符串里可以出现转义字符

4.3 不加引号的字符串

不加引号的字符串不会将含有空格的字符串视为一个整体输出, 如果内容中有变量等,会先把变量解析出结果,然后在输出最终内容来,如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号,一般连续的字符串,数字,路径等可以用。

my_name="Bob"
# 不加引号字符串中的变量前不用加<span class="katex math inline">str=+++++name+++++
echo</span>str

4.4 反引号的字符串

如果需要调用命令的输出,或把命令的输出赋给变量,则命令必须使用反引号包含,这条命令才会执行。反引号的作用和 (命令) 是一样的,但是反引号非常容易和单引号搞混,所以推荐使用(命令) 的方式引用命令的输出。

# date被当做字符串
echo "date"

# 输出日期时间
echo `date`
echo $(date)

4.5 字符串操作

字符串操作包括拼接字符串、获取字符串的长度、提取子串、查找子串等。

# 字符串拼接
my_name="Bob"
greeting="I am "<span class="katex math inline">my_name"!"
echo</span>greeting

greeting="I am <span class="katex math inline">{my_name}"
echo</span>greeting


# 获取字符串的长度
str="abcde"
echo <span class="katex math inline">{#str}  # 输出 5


# 提取子串</span>{字符串名:起始索引:子串长度}
str="HUAWEI is a great company."
echo <span class="katex math inline">{str:12:5}   # 输出 great


# 查找子串
str="HUAWEI is a great company."
# 找出字母 H 的位置, 从 0 开始
echo</span>(expr index "$str" H) # 返回1

5 流程控制

和 Java 等语言不一样,Shell 的流程控制不可为空,如:

if (i == 0) {
    System.out.println("i is equal to 0")
} else {
    // do nothing...
}

在 Shell 中,这样写是不合法的,如果 else 分支没有语句执行,就不要写这个 else。

此外,Shell 中的方括号是一个可执行程序,在 CentOS/Ubuntu 中,它位于 /usr/bin 目录下:

ll /usr/bin/[
# 输出如下
-rwxr-xr-x 1 root root 55744 Apr  5  2024 '/usr/bin/['*

正因为方括号是一个可执行程序,方括号后面必须加空格,不能写成 if [$foo -eq 0]

5.1 if else

5.1.1 if

if condition
then
        command1
        command2
        ...
        commandN
fi

写成一行(适用于终端命令提示符):

if `ps -ef | grep ssh`;  then echo hello; fi

末尾的 fi 就是 if 倒过来拼写,后面还会遇到类似的情况。

5.1.2 if else

if condition
then
        command1
        command2
        ...
        commandN
else
        command
fi

5.1.3 if else-if else

if condition1
then
        command1
elif condition2
        command2
else
        commandN
fi

5.2 for while

5.2.1 for

for var in item1 item2 ... itemN
do
        command1
        command2
        ...
        commandN
done

写成一行:

for var in item1 item2 ... itemN; do command1; command2… done;

5.2.2 C 风格的 for

for (( EXP1; EXP2; EXP3 ))
do
        command1
        command2
        command3
done

5.2.3 while

while condition
do
        command
done

5.3.4 死循环

while :
do
        command
done


# 或者
while true
do
        command
done

# 或者
for (( ; ; ))

5.3.5 until

until condition
do
        command
done

5.3.6 case 例子

case 的语法和 C family 语言差别很大,它需要一个 esac(就是 case 反过来)作为结束标记,每个 case 分支用右圆括号,用两个分号表示 break。

case "<span class="katex math inline">{opt}" in
        "Install-Puppet-Server" )
                install_master</span>1
                exit
        ;;

        "Install-Puppet-Client" )
                install_client $1
                exit
        ;;

        "Config-Puppet-Server" )
                config_puppet_master
                exit
        ;;

        "Config-Puppet-Client" )
                config_puppet_client
                exit
        ;;

        "Exit" )
                exit
        ;;

        * ) echo "Bad option, please choose again"
esac

6 文件包含

可以使用 source 和 . 关键字,例如:

source ./function.sh
. ./function.sh

在 bash 里,source 和 . 是等效的,他们都是读入 function.sh 的内容并执行其内容,为了更好的可移植性,推荐使用第二种写法。

包含一个文件和执行一个文件一样,也要写这个文件的路径,不能光写文件名。

如果 function.sh 是用户传入的参数,如何获得它的绝对路径呢?方法是:

real_path=`readlink -f <span class="katex math inline">1`#</span>1是用户输入的参数,如function.sh
. $real_path

参考资料

最后编辑:
作者:lwg0452
这个作者貌似有点懒,什么都没有留下。
捐 赠如果您觉得这篇文章有用处,请支持作者!鼓励作者写出更好更多的文章!

留下一个回复

你的email不会被公开。