Linux Shell编程知识介绍(扩展版)

常用命令及符号详解

1. test 命令

test 命令用于检查某个条件是否成立,通常与 if 语句结合使用。它有两种形式:test condition[ condition ]。在 bash 中,[ 关键字本身是一个命令,它是 test 命令的同义词,但最后一个参数必须是 ]

示例

#!/bin/bash
if test -f test.txt; then
    echo "test.txt 是一个文件"
else
    echo "test.txt 不是一个文件"
fi

test 命令支持 man bashCONDITIONAL EXPRESSIONS 小节提到的所有条件表达式,并支持用以下操作符来组合条件表达式,操作符优先级按从高到低排列:
| 操作符 | 含义 |
| — | — |
| ! expr | 如果 expr 条件表达式是 false,则 ! expr 返回 true |
| ( expr ) | 返回 expr 条件表达式的值 |
| expr1 -a expr2 | 当 expr1expr2 条件表达式都为 true 时,整个表达式才是 true |
| expr1 -o expr2 | 当 expr1expr2 条件表达式有一个为 true 时,整个表达式就是 true |

需要注意的是,在 test 命令中,每一个操作符、以及每一个操作符参数之间都必须用空格隔开。另外,在 bash 中,小括号具有特殊含义,(cmd) 表示启动一个子 shell 来执行 cmd 命令,所以这里的 () 要用 \ 转义字符、或者引号来去掉它们的特殊含义,避免被当成命令替换来处理。

2. expr 命令

expr 命令是一个手工命令行计数器,用于在 UNIX/LINUX 下求表达式变量的值,一般用于整数值,也可用于字符串。

语法

expr 表达式

表达式说明

  • 用空格隔开每个项;
  • 用反斜杠 \ 放在 shell 特定的字符前面;
  • 对包含空格和其他特殊字符的字符串要用引号括起来。

实例

# 计算字串长度
expr length "this is a test"
# 抓取字串
expr substr "this is a test" 3 5
# 抓取第一个字符数字串出现的位置
expr index "sarasara"  a
# 整数运算
expr 14 % 9
expr 10 + 10
expr 1000 + 900
expr 30 / 3 / 2
expr 30 \* 3  # 使用乘号时,必须用反斜线屏蔽其特定含义。因为 shell 可能会误解显示星号的意义

3. bc 命令

bc 命令是任意精度计算器语言,通常在 linux 下当计算器用,它类似基本的计算器,使用这个计算器可以做基本的数学运算。

常用的运算

  • + 加法
  • - 减法
  • * 乘法
  • / 除法
  • ^ 指数
  • % 余数

语法

bc(选项)(参数)

选项值

  • -i:强制进入交互式模式;
  • -l:定义使用的标准数学库;
  • -w:对 POSIX bc 的扩展给出警告信息;
  • -q:不打印正常的 GNU bc 环境信息;
  • -v:显示指令版本信息;
  • -h:显示指令的帮助信息。

参数

  • 文件:指定包含计算任务的文件。

实例

# 交互式计算
bc
2+3
5
5-2
3
2+3*1
5
输入 quit 退出。

# 通过管道符计算
echo "15+5" | bc
20

# 设小数位,2 代表保留两位
echo 'scale=2; (2.777 - 1.4744)/1' | bc
1.30

# 进制转换
echo "ibase=2;111" | bc
7
echo "obase=2;192" | bc
11000000
echo "obase=10;ibase=2;11000000" | bc
192

# 计算平方和平方根
echo "10^10" | bc 
10000000000
echo "sqrt(100)" | bc
10

4. 输入重定向和输出重定向

大多数 UNIX 系统命令从终端接受输入并将所产生的输出发送回到终端。一个命令通常从标准输入读取输入,默认情况下,这恰好是终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是终端。

重定向命令列表
| 命令 | 说明 |
| — | — |
| command > file | 将输出重定向到 file。 |
| command < file | 将输入重定向到 file。 |
| command >> file | 将输出以追加的方式重定向到 file。 |
| n > file | 将文件描述符为 n 的文件重定向到 file。 |
| n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
| n >& m | 将输出文件 mn 合并。 |
| n <& m | 将输入文件 mn 合并。 |
| << tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

输出重定向
重定向一般通过在命令间插入特定的符号来实现。command1 > file1 这个命令执行 command1 然后将输出的内容存入 file1。注意任何 file1 内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用 >> 操作符。

实例

# 将 who 命令的输出重定向到 users 文件
who > users
# 查看 users 文件内容
cat users

# 覆盖 users 文件内容
echo "菜鸟教程:www.runoob.com" > users
cat users

# 追加内容到 users 文件
echo "菜鸟教程:www.runoob.com" >> users
cat users

输入重定向
Unix 命令也可以从文件获取输入,语法为:command1 < file1。这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

实例

# 统计 users 文件的行数
wc -l users
# 将输入重定向到 users 文件
wc -l < users

同时替换输入和输出
command1 < infile > outfile 执行 command1,从文件 infile 读取内容,然后将输出写入到 outfile 中。

Here Document
Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。它的基本的形式如下:

command << delimiter
    document
delimiter

它的作用是将两个 delimiter 之间的内容(document)作为输入传递给 command。需要注意的是,结尾的 delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。开始的 delimiter 前后的空格会被忽略掉。

实例

# 在命令行中通过 wc -l 命令计算 Here Document 的行数
wc -l << EOF
    欢迎来到
    菜鸟教程
    www.runoob.com
EOF

# 在脚本中使用 Here Document
#!/bin/bash
cat << EOF
欢迎来到 菜鸟教程

www. runoob .com
EOF

/dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。

实例

command > /dev/null

5. 引号的区别

  • 单引号 ('):单引号内的所有字符都会被原样输出,不会进行变量替换。可以说是所见即所得,即将单引号内的内容原样输出,被单引号括起来的字符都是普通字符,就算特殊字符也不再有特殊含义。
  • 双引号 ("):双引号内的变量会被替换为其值。把双引号内的内容输出出来,如果内容中有变量,会先把变量解析出结果,然后再输出最终内容。被双引号括起来的字符中 $\ 和反引号是拥有特殊含义的,$ 代表引用变量的值,而反引号代表引用命令。
  • 反引号 ():反引号内的命令会被执行,其结果会被替换到相应位置。反引号的作用和 $(命令) 是一样的,但是反引号非常容易和单引号搞混,所以推荐大家使用 $(命令) 的方式引用命令的输出。

示例

#!/bin/bash
name="John"
echo 'My name is $name'  # 输出: My name is $name
echo "My name is $name"  # 输出: My name is John
echo `date`  # 输出当前日期和时间
echo $(date)  # 输出当前日期和时间

6. (())()$[]${}、管道 (|)、$()$(()[][[ ]]&&||

  • (()):用于整数运算和条件判断。例如:num=$((10 + 20))
  • ():用于创建子 shell。例如:(ls; pwd)
  • $[]:用于整数运算。例如:result=$[10 * 20]
  • ${}:用于变量替换和字符串操作。例如:str="hello world"; echo "字符串长度: ${#str}"
  • 管道 (|):将一个命令的输出作为另一个命令的输入。管道是一种强大的机制,它允许将多个命令连接起来,前一个命令的输出会直接作为后一个命令的输入,从而实现数据的流式处理。

管道的使用示例

# 列出当前目录下的所有文件,并统计文件数量
ls | wc -l

# 查找包含特定字符串的文件,并统计匹配的行数
grep 'example' file.txt | wc -l

在使用管道时,需要注意以下几点:

  • 管道只能传递标准输出(STDOUT),标准错误输出(STDERR)不会通过管道传递。如果需要处理标准错误输出,可以使用重定向将其合并到标准输出。

  • 管道中的每个命令都会在一个子 shell 中执行,因此在管道中设置的变量在管道外部是不可见的。

  • $():用于命令替换。例如:current_dir=$(pwd)

  • $(():用于整数运算。

  • []:与 test 命令类似,用于条件判断。例如:if [ $num -gt 15 ]; then echo "$num 大于 15"; fi

  • [[ ]]:增强的条件判断,支持更多的模式匹配。例如:if [[ $str == *world* ]]; then echo "字符串包含 world"; fi

  • &&:逻辑与,只有当前一个命令执行成功时,才会执行后一个命令。例如:mkdir test && cd test

  • ||:逻辑或,当前一个命令执行失败时,才会执行后一个命令。例如:ls non_existent_file || echo "文件不存在"

7. 其他特殊字符

  • ~:主目录的简写。例如:cd ~ 可以转到主目录,cd ~/work/archive 可以转到主目录下的 work/archive 目录。
  • .:表示当前目录。例如:ls -a 可以查看当前目录下的所有文件,./script.sh 可以从当前目录运行一个脚本。
  • ..:代表当前目录的父目录。例如:cd .. 可以在目录树中向上移动一个级别,cd ../gc_help 可以在目录树中上升一个级别,然后进入该级别的另一个目录。
  • /:路径目录分隔符。例如:ls ~/work/archive 可以查看主目录下的 work/archive 目录。
  • #:大多数情况下,用于告诉 shell 以下是注释,它不应对其执行操作。也可以使用散列来修剪字符串变量并从开头删除一些文本。例如:this_string="Dave Geek!"; echo How-To ${this_string#Dave}
  • ?:单字符通配符,代表恰好一个字符。例如:ls badge?.txt 可以列出名称以 徽章 开头且后跟文件扩展名之前的任何单个字符的任何文件。
  • *:字符序列通配符,代表任何字符序列,包括无字符。例如:ls badge* 可以匹配所有以 badge 开头的文件。
  • []:字符集通配符,文件名中的相关字符必须与通配符集中的至少一个字符相匹配。例如:ls badge_0[246].txt 可以匹配任何具有 .txt 扩展名、文件名以 pipes_0 开头且下一个字符为 2、4 或 6 的文件。
  • ;:Shell 命令分隔符,可以在命令行中键入任意数量的命令,只要用分号分隔每条命令即可。例如:ls > count.txt; wc -l count.txt; rm count.txt

反引号执行(

反引号()在 Shell 中用于命令替换,它允许在一个命令中嵌入另一个命令,并将嵌入命令的输出结果替换到原命令中。反引号的作用和 $(命令) 是一样的,但 $(命令) 更推荐使用,因为它的语法更清晰,嵌套使用时也更方便。

反引号执行的示例

# 获取当前日期并赋值给变量
current_date=`date`
echo "当前日期是: $current_date"

# 统计当前目录下的文件数量
file_count=`ls | wc -l`
echo "当前目录下有 $file_count 个文件。"

在使用反引号执行时,需要注意以下几点:

  • 反引号内的命令会被优先执行,其输出结果会替换到原命令中。
  • 反引号内的命令可以是任何合法的 Shell 命令,包括复杂的命令组合。
  • 反引号和单引号容易混淆,使用时要注意区分。单引号会将其中的内容原样输出,不会进行命令替换。

Shell命令的顺序执行、判断语句和循环语句

1. 顺序执行

在 Shell 脚本中,命令会按照顺序依次执行。可以使用分号 (;) 来分隔多个命令。例如:ls; pwd; date

2. 判断语句

2.1 if-else 语句

用于根据条件执行不同的代码块。

语法一:一个条件

if condition
then
    command1
    command2
    ...
fi

语法二:两个条件

if condition
then
    command1
    command2
    ...
else
    command
fi

语法三:多个条件

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

示例

#!/bin/bash
num=10
if [ $num -gt 5 ]; then
    echo "$num 大于 5"
else
    echo "$num 小于等于 5"
fi

3. 循环语句

3.1 for 循环语句

读取不同的变量值,用来逐个执行同一组命令。

格式用法

for 变量名 in 取值列表
do
    命令序列
 done

示例

# 循环输出 1 - 10
for i in {1..10}
do
    echo $i
done

# 输出 1 - 100 的所有整数和
sum=0
for i in {1..100}
do
    sum=$((sum + i))
done
echo "1 - 100 的所有整数和为: $sum"

3.2 while 循环语句

对于要求控制循环次数、操作对象按数字顺序编号、按特定条件执行重复操作等情况,更适用 while 循环语句。

语法

while 条件测试操作
do
    命令序列
done

示例

# 1000 以内的猜数游戏
answer=$((RANDOM % 1000 + 1))
while true
do
    read -p "请输入一个 1 - 1000 之间的整数: " guess
    if [ $guess -eq $answer ]; then
        echo "猜对了!"
        break
    elif [ $guess -lt $answer ]; then
        echo "猜的数字太小了,请再试一次。"
    else
        echo "猜的数字太大了,请再试一次。"
    fi
done

3.3 until 循环语句

当条件为假时,重复执行代码块。

语法

until 条件测试操作
do
    命令序列
done

示例

# 输出 1 - 5
i=1
until [ $i -gt 5 ]
do
    echo $i
    i=$((i + 1))
done

4. read 命令

read 命令用于从标准输入读取数值。它可以用来读取键盘输入,当使用重定向的时候,也可以读取文件中的一行数据。

语法

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

参数说明

  • -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
  • -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
  • -p 后面跟提示信息,即在输入前打印提示信息。
  • -e 在输入的时候可以使用命令补全功能。
  • -n 后跟一个数字,定义输入文本的长度。
  • -r 屏蔽 \,如果没有该选项,则 \ 作为一个转义字符,有的话 \ 就是个正常的字符了。
  • -s 安静模式,在输入字符时不再屏幕上显示,例如 login 时输入密码。
  • -t 后面跟秒数,定义输入字符的等待时间。
  • -u 后面跟 fd,从文件描述符中读入,该文件描述符可以是 exec 新开启的。

实例

# 简单读取
echo "输入网站名: "
read website
echo "你输入的网站名是 $website"

# -p 参数,允许在 read 命令行中直接指定一个提示
read -p "输入网站名:" website
echo "你输入的网站名是 $website"

# -t 参数指定 read 命令等待输入的秒数,当计时满时,read 命令返回一个非零退出状态
if read -t 5 -p "输入网站名:" website
then
    echo "你输入的网站名是 $website"
else
    echo "\n抱歉,你输入超时了。"
fi

# -n 参数设置 read 命令计数输入的字符。当输入的字符数目达到预定数目时,自动退出,并将输入的数据赋值给变量
read -n1 -p "Do you want to continue [Y/N]?" answer
case $answer in
Y | y)
    echo "fine ,continue";;
N | n)
    echo "ok,good bye";;
*)
    echo "error choice";;
esac

# -s 选项能够使 read 命令中输入的数据不显示在命令终端上
read  -s  -p "请输入您的密码:" pass
echo "\n您输入的密码是 $pass"

# 读取文件
count=1
cat test.txt | while read line
do
    echo "Line $count:$line"
    count=$((count + 1))
done
echo "finish"

5. basenamedirname 命令

5.1 basename 命令

basename 命令是一个标准的 Unix 和类 Unix 系统上的程序,它的作用是从给定的文件名或路径名中删除任何前导的目录信息和后缀,只返回文件名的最后一部分。主要用于 shell 脚本中,可以方便地提取文件名或路径名的基本部分。

基本语法

basename NAME [SUFFIX]
basename OPTION... NAME...

常用选项或参数说明
| 选项 | 参数 | 说明 |
| — | — | — |
| -a | –multiple | 支持多个输入,将每个输入都视为一个 NAME |
| -s | –suffix=SUFFIX | 删除一个后缀 SUFFIX,如文件扩展名 |
| -z | –zero | 用 NUL 而不是换行符来分隔输出 |
| –help | 无 | 显示帮助信息并退出 |
| –version | 无 | 显示版本信息并退出 |

实例

# 删除路径名
basename /etc/passwd
# 删除文件扩展名
basename /etc/sysctl.conf .conf
basename -s .conf /etc/sysctl.conf
# 处理多个输入
basename -a /etc/passwd /etc/shadow
# 在 shell 脚本中重命名文件
#!/bin/sh
for file in *.jpeg; do
    if [ ! -f $file ]; then
        exit
    fi
    b=$(basename $file .jpeg)
    echo NOW $b.jpeg is $b.jpg
    mv -- "$file" "$(basename $file .jpeg).jpg"
done

5.2 dirname 命令

dirname 命令可以取给定路径的目录部分。这个命令很少直接在 shell 命令行中使用,一般把它用在 shell 脚本中,用于取得脚本文件所在目录,然后将当前目录切换过去。

实例

# 示例一
dirname /usr/bin/sort
# 示例二
dirname /usr/bin
# 在脚本中获取脚本文件所在的绝对路径
shellPath=$(cd "$(dirname "$0")"; pwd)
echo $shellPath

6. Shell 函数

Shell 函数用于封装一段可重复使用的代码。

示例

#!/bin/bash
# 定义函数
greet() {
    echo "Hello, $1!"
}

# 调用函数
greet "John"

7. 特殊字符

  • $#:表示脚本或函数的参数个数。
  • $?:表示上一个命令的退出状态,0 表示成功,非 0 表示失败。
  • $@:表示所有的参数,每个参数是独立的。
  • $$:表示当前脚本的进程 ID。
  • $*:表示所有的参数,作为一个整体。

示例

#!/bin/bash
echo "参数个数: $#"
echo "上一个命令的退出状态: $?"
echo "所有参数 (独立): $@"
echo "当前脚本的进程 ID: $$"
echo "所有参数 (整体): $*"

以上就是关于 Linux shell 编程的详细介绍,包含了常用命令及符号的作用和区别,以及 shell 命令的顺序执行、判断语句、循环语句等内容。希望这些内容对你有所帮助。

作者:严锋  创建时间:2025-04-27 19:43
最后编辑:严锋  更新时间:2025-04-27 19:45