linux之shell基础

本文详细介绍了Linux Shell脚本的基础知识,包括变量定义、使用、只读和删除,字符串操作,数组,注释,参数传递,流程控制结构如if、for、while等,以及输出命令echo和printf的用法。还讲解了函数、重定向、文件包含、declare命令、xargs工具以及shell中的特殊符号。内容全面,适合初学者入门。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

shell脚本基础

shell变量
变量定义

变量定义,无需加美元符

变量命名规则(和python差不多):

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

定义过的变量,只需要在变量名面前加美元符号即可

花括号可加可不加,加是为了识别变量的边界

只读变量

只读变量不能改变,使用 readonly 将变量定位为只读变量

删除变量

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

变量类型

局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量

环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量

shell变量:shell变量是由shell程序设置的特殊变量,shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

shell字符串

字符串是shell中最常用的数据类型,字符串可以是单引号,也可以是双引号

单引号字符串限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量无效
  • 单引号字符串中不能出现一个的单引号(对单引号使用转义字符也不行),但可成对出现,作为字符串拼接使用
双引号字符串优点:
  • 双引号里面可以有变量
  • 双引号可用出现转义字符
常见字符串操作
string=lebronjames

# 获取字符串长度
echo ${#string} 
> 11

# 提取子字符串 从第二个字符开始截取四个
echo ${string:1:4} 
> ebro

# 查找字符位置(io哪个先出现就先计算哪个)
echo `expr index "$string" io`
> 5
shell数组

bash仅支持一维数组(不支持多维数组),并且没有限定数组的大小;数组元素的下标从0开始编号;

# 定义数组:数组名=(值1,值2 ... 值n)
# 读取数组: ${数组名[下标]}
# 使用@符号可用获取数组中的所有元素,例如:
echo ${array_name[@]}
# 获取数组元素的个数
length=${#array_name[@]}
length=${#array_name[*]}
# 获取数组单个元素的长度
lengthn=${#array_name[n]}
shell注释

单行使用 # 进行注释

多行注释

# 格式1
:<<EOF
注释内容
EOF	

# 格式2
:<<'
注释内容
'

# 格式3
:<<!
注释内容
!
shell传递参数

执行shell脚本,向脚本传递参数,脚本内或取参数的格式为:$n; $n表示第n个参数;

参数处理传递给脚本的参数个数
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数;如$*用"“括起来,以”$1 $2 … $n"的形式输出所有参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*类似,但是使用时加引号,并在引号中返回每个参数:以"$1" " 2 " . . . " 2" ... " 2"..."n"的形式输出所有参数
$-显示shell使用的当前选项,与set命令功能相同
$?显示最后命令的退出状态。0表示没有错误,其他值表示有错误

$* 与 $@区别

#### 演示脚本

# !/bin/bash
echo "-- \$* 演示---"
for i in "$*";do
    echo $i
done

echo "-- \$@ 演示---"
for i in "$@";do
    echo $i
done

#### 执行脚本
$ chmod +x test.sh
$ ./test.sh 1 2 3


#### 执行结果

-- $* 演示---
1 2 3 4
-- $@ 演示---
1
2
3
4
shell基本运算符

原生bash不支持简单的数学运算,但可以通过其他命令来实现,例如awk和expr,expr最常用;

expr是一款表达式计算工具,使用它能完成表达式的求值操作;

shell支持多种运算符,包括:

  • 算术运算符:+ - *(注意转义) / % =(赋值) == !=
  • 关系运算符:只支持数字;-eq -ne -gt -lt -ge -le
  • 布尔运算符: !(非) -o(或) -a(与)
  • 逻辑运算符: &&(逻辑AND) ||(逻辑or)
  • 字符串运算符: = != -z(长度为0返回True) -n(长度不为0返回true) $()
  • 文件测试运算符: 用于检测Unix的各种属性
#!/bin/bash

val=`expr 2 + 2`
echo "两数之和为:"

注意:表达式和运算符之间要有空格!!“2 + 2”

shell流程控制
if else
### 格式
if condition1
then 
    command1
elif condition2
then
    command2
else
    commandN
fi

### 示例
$ cat>if-else-fi_test.sh <<EOF
> # !/bin/bash
> a=10
> b=20
> if [ $a == $b ]
> then
>    echo "a 等于 b"
> elif [ $a -gt $b ]
> then
>    echo "a 大于 b"
> elif [ $a -lt $b ]
> then
>    echo "a 小于 b"
> else
>    echo "没有符合的条件"
> fi
> EOF
$ ./if-else-fi_test.sh
a 等于 b

for循环
### 格式
for var in item1 item2 ... itemN
do 
    command1
    command2
    command3
done

### 示例
$ for loop in 1 2 3 4 5;do echo "value is: $loop";done;
value is: 1
value is: 2
value is: 3
value is: 4
value is: 5
while循环
### 格式
while condition
do
    command
done

### 无限循环
while True
do
    command
done

### 示例
$ cat>while_test.sh <<EOF
> echo '按下 <CTRL-D> 退出'
> echo -n '输入你最喜欢的网站名: '
> while read FILM
> do
>     echo "是的!$FILM 是一个好网站"
> done
> EOF
$ ./while_test.sh
按下 <CTRL-D> 退出
输入你最喜欢的网站名: google
是的! google是一个好网站

until循环

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用

### 格式
until condition
do
    command
done

### 示例
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done
case

case为多选择语句,使用case语句一个值与一个模式匹配

### 格式
casein
模式1)
	command1
	...
	commandN
	;;
模式2)
	command1
	...
	commandN
	;;
esac

### 示例
$ cat>case_test.sh <<EOF
> echo '输入 1 到 4 之间的数字:'
> echo '你输入的数字为:'
> read aNum
> case $aNum in
>     1)  echo '你选择了 1'
>     ;;
>     2)  echo '你选择了 2'
>     ;;
>     3)  echo '你选择了 3'
>     ;;
>     4)  echo '你选择了 4'
>     ;;
>     *)  echo '你没有输入 1 到 4 之间的数字'
>     ;;
> esac
> EOF
$ ./case_test.sh
输入 1 到 4 之间的数字:
你输入的数字为:
1
echo '你选择了 1''

跳出循环:break continue

break:跳出所有循环

continue: 跳出当前循环

输出命令
echo

用于字符串的输出

# 1 显示普通字符串(没有引号直接字符串也可以)
echo 'it is a test'
# 2 显示转义字符
echo \"it is a test\"
# 3  显示变量
#!/bin/sh
read name  #表示从输入读取并赋值给变量
echo "$name"
# 4 显示换行/不换行
echo -e "OK! \n" # -e 开始转义 \n换行 \c不换行
echo -e "OK! \c"
# 结果定向至文件
echo "niubi" > test.txt
# 使用单引号原样输出,不进行转义或取变量
echo '$name\"'
> $name\"
# 显示命令执行结果
echo `date`  # `` 反引号
printf

printf同echo一样,也是个输出命令;echo是标准输出,但printf拥有更好的格式控制效果

printf使用引用文本或空格分隔的参数,外面可以在printf中使用格式化字符串,还可以指定字符串的宽度、左右对齐方式等,且不会像echo那样自动添加换行符

#### 语法
printf format-string [arguments]
常用转义字符

\" -转义后的双引号

\\ -转义后的反斜杠

\b -退格符

\n -换行符

\r -回车符

\t -水平指标符

\v -垂直制表符

常用的类型转换符

%d -将参数打印为十进制整数

%f -将参数打印为浮点数

%s -将参数打印为字符串

%x -将参数打印为十六进制整数

%o -将参数打印为八进制整数

$ printf "%.2f\n" 34.244444
34.24

# %-10s, 其中-表示左对齐,没有默认右对齐,10表示占位的字符宽度
$ printf "-10s %8d\n" "小明" "18"
小明           18
函数
[function] funcname()
{
    action;
    [return int;]
}

# 可以function funcname()定义;也可以funcname()直接定义
# 参数返回,可以加return返回,return后跟数值0-255;没有return 以最后一条命令运行结果作为返回值,函数返回值在调用该函数后用 $?访问
# 函数的调用必须放在函数的定义之后
函数参数:
  • n 获 取 第 n 个 参 数 , n > = 10 时 , 使 用 n 获取第n个参数,n>=10时,使用 nnn>=10使{n}
  • $# 参数个数
  • $$ 脚本运行当前ID号
  • $! 后台运行的最后一个进程ID号
  • $* 以一个单字符串显示所有脚本传递的参数
  • $@ 使用加引号,返回每一个参数,单个返回
  • $? 显示最后命令的退出状态,0表示没有错误,其他任何值表明有错误
  • $- 显示Shell使用的当前选项,与set命令功能相同
# 设置不同用户拷贝文件夹时产生的权限所属问题
alias bemy='function _bemy(){ echo $1; sudo chown -R `whoami` $@; sudo chgrp -R `whoami` $@; };_bemy'
重定向
命令说明
command>file将输出重定向到file(>>表示追加方式)
command<file将输入重定向到file
n>file将文件描述符为n的文件重定向到file(>>表示追加方式)
n>&m将输出文件m和n合并
n<&m将输入文件m和n合并
<<tag将开始标记tag和结束标记tag之间的内容作为输入
# 输出重定向
$ who > who.txt
$ cat who.txt
zhengang pts/20       2020-11-28 10:50 (10.33.29.250)

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

# stderr重定向到file
command 2>file

# stderr追击到到file
command 2>>file

# 将stdout和stderr合并后重定向到file
command > file 2>&1
# 2>不能分开,以上经常用于打log

# command命令将stdin重定向到file1,将stdout重定向到file2
$ command < file1 >file2

# 将两个delimeter之间的document作为输入传递给command
$ command << delimeter
	 document
  delimeter
# 后面的delimeter前后不能有任何字符

$ wc -l << EOF
	欢迎来到
	菜鸟教程
	www.runoob.com
   EOF
3
$

# 将不需要或者需要屏蔽的输出重定向到 /dev/null
$ command > /dev/null
# 屏蔽stdout和stderr;0是标准输入(STDIN),1是标准输出(STDOUT),2是标准错误输出(STDERR)
# 2> 不能分开,合在一起才表示错误输出
$ command > /dev/null 2>&1
shell文件包含
# 在一个shell中包含另外一个shell
# 注意.后面要有空格
. filename
# 或者
source filename
declare

用来设置变量值和属性

declare [+][-aAfFgilrtux] [-p][变量名称=设置值] 
# +/-: "-"用来指定变量的属性,"+"则是取消变量所设的属性
# -f[name]:列出之前由用户在脚本中定义的函数名称和函数体
# -F[name]: 仅列出自定义函数名称
# -g name:在shell脚本中可创建全局变量
# -p [name]:显示指定变量的属性和值
# -a name:声明变量为普通数组
# -A name:声明变量为关联数组
# -i name:将变量定义为整数型
# -r [name[=value]]:将变量定义为只读(不可修改和删除)
# -x name[=value] 等同于export name[=value]:将变量设置为环境变量

xargs

获取一个命令的输出传递给另外一个参数:

find ./ -name "diversity_run.py" | xargs -i rm {}

转换文件格式,多行变单行,单行变多行

xargs常与cat find ls 搭配使用

-t: 表示先打印命令,然后再执行

cat test.txt | xargs  #全部读成一行

cat test.txt | xargs -n3  #按每行三个输出

echo "nameXnameXnameXname" | xargs -dX -n2 #按X元素分割 -n num 表示单行输出数量
-》
name name
name name

# 复制所有图片到/data/images 目录下面
# -I参数用{}替换前命令的每一个结果,后面命令会针对每一个{}执行
ls *.jpg | xargs -n1 -i {} cp {} /data/images

# find xx -print0 与 xargs -0结合使用
# 原因xargs默认按空白字符(空格,TAB,换行符)分割记录,防止文件名被分割成两个文件无法识别,使用0来做分割辨识
find . -type f -name "*.log" -print0 | xargs -0 rm -f
find . -type f -name "*.php" -print0 | xargs -0 rm -f
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz
cat url-list.txt | xargs wget -c

注意:xargs 与重定向同时使用时要使用 sh -c 将 xargs 后面执行的内容用sh -c " xx "处理

find /data/ -name test.log  -size +10G  | xargs  -i sh -c "echo > {} "
shell中的符号

(1) .(source):

.与source命令一样,从文件中读取并执行命令,无论该文件是否都有可执行权限都能正确执行;且是在当前shell下执行,而不是产生一个子shell来执行(./filename.sh便是在当前shell下产生一个shell去执行的)。所以在设置bash的环境变量的时候,就必须用该命令或者source命令去执行设置的环境变量才会对当前shell生效,如下:

for i in /etc/profile.d/*.sh ;do
	if [ -r "$i" ];then
		. $i
	fi
done

(2) :

该命令什么都不做,但执行后会返回一个正确的退出码,即exit 0;比如在if 语句中,then后面不想做任何操作,但是又不能空着,这时就可以使用’:'来解决

if [ "$i" -ne 1];then
	:
else
	echo "$i is not equal 1"
fi

(3) ():

()将多个命令组合在一起执行,相当于一个命令组

(4) {}:

{}和()类似,也是将多个命令组合在一起,他们之间的区别是,()是在产生的子shell下执行,而{}是在当前的shell下执行;这与前面讲到的使用". filename.sh"和"./filename.sh"区别一样

A=123
(A=abc;echo $A);echo $A
> abc 
> 123

{ A=abc;echo $A; };echo $A
>abc
>abc

注意:()里面两边可以不适用空格,{}里面两边必须使用空格,且最后一个命令也需要以";"作为结尾结束,表示命令结束。

(5) [] (test)

[]与test命令一样,用于比较值以及检查文件类型,如下:

["$A" = 123]:字符串测试

[" A " − e q 123 ] : 整 数 测 试 , 测 试 A" -eq 123]:整数测试,测试 A"eq123]A是否等123这个值

[-e “$A” ]:文件测试,测试这份文件是否存在

(6) [[]] 增强版的[],能够将多个test命令支持的测试组合起来

[[ (-d "$HOME") && (-w "$HOME")]] && echo "home is a writable directory"

两者区别于异同:

数字测试: -eq -ne -lt -le -gt -ge , [[ ]] 与 [] 一致

文件测试: -r -l -w -x -f -s -nt -ot ,[[]] 与 []一致

字符串测试:> < =(同==) != -n -z,不可使用"<=“和”>=", [[ ]] 同 []<

在[]中,> < 必须使用\进行转义,\>和\<

逻辑测试: []为 -a -o ! [[ ]] 为 && || !

数学运算:[] 不可以使用 [[ ]] 可以使用 + - * / %

组合:均可用各自逻辑符号连接的数字(运算)测试、文件测试、字符测试

[ a \> 1 ] && echo ture || echo false
> ture
[[ a > 1 ]] && echo ture || echo false
> ture

(7) (())

(()) 专门用来做数值运算,如果表达式求值为0,则设置退出状态为1;如果求值为非0值,则设置为0;不需要对 ((和))之间的操作符转义。算术只对整数进行,除0会产生错误,但不会产生溢出,可以执行C语言中常见的算术、逻辑和位操作,如下:

((i=1+99));echo $i
>100

i=99;((i++));echo $i
>100

# 也可以使用$(())直接进行数值运算
echo $((2**3))
>8

注意,使用(())时,不需要空格分隔各值和运算符,使用[]和[[]]时需要空格分隔各值和运算符

if [ $a -ne 0] && [$b -lt 3] || [ $c -gt 5]
then
# 其他操作
fi

表1 test 条件表达式表

[-a FILE] 如果file存在则为真

[ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真。

[ -c FILE ] 如果 FILE 存在且是一个字特殊文件则为真。

[ -d FILE ] 如果 FILE 存在且是一个目录则为真。
[ -e FILE ] 如果 FILE 存在则为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则为真。
[ -g FILE ] 如果 FILE 存在且已经设置了SGID则为真。 [ -h FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -k FILE ] 如果 FILE 存在且已经设置了粘制位则为真。
[ -p FILE ] 如果 FILE 存在且是一个名字管道(F如果O)则为真。
[ -r FILE ] 如果 FILE 存在且是可读的则为真。
[ -s FILE ] 如果 FILE 存在且大小不为0则为真。
[ -t FD ] 如果文件描述符 FD 打开且指向一个终端则为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID (set user ID)则为真。
[ -w FILE ] 如果 FILE 如果 FILE 存在且是可写的则为真。
[ -x FILE ] 如果 FILE 存在且是可执行的则为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则为真。
[ -G FILE ] 如果 FILE 存在且属有效用户组则为真。
[ -L FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。

[ -o OPTIONNAME ] 如果 shell选项 “OPTIONNAME” 开启则为真。
[ -z STRING ] “STRING” 的长度为零则为真。
[ -n STRING ] or [ STRING ] “STRING” 的长度为非零 non-zero则为真。

[ STRING1 == STRING2 ] 如果2个字符串相同。 “=” may be used instead of “==” for strict POSIX compliance则为真。
[ STRING1 != STRING2 ] 如果字符串不相等则为真。
[ STRING1 < STRING2 ] 如果 “STRING1” sorts before “STRING2” lexicographically in the current locale则为真。
[ STRING1 > STRING2 ] 如果 “STRING1” sorts after “STRING2” lexicographically in the current locale则为真。

[ ARG1 OP ARG2 ] “OP” is one of -eq, -ne, -lt, -le, -gt or -ge.These arithmetic binary operators return true if “ARG1” is equal to,not equal to, less than, less than or equal to, greater than, orgreater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” areintegers. 表达式可以借以下操作符组合起来,以降序列出:listed in decreasing order ofprecedence

表2 组合表达式操作 效果
[ ! EXPR ] 如果 EXPR 是false则为真。
[ ( EXPR ) ] 返回 EXPR的值。这样可以用来忽略正常的操作符优先级。
[ EXPR1 -a EXPR2 ] 如果 EXPR1 and EXPR2 全真则为真。
[ EXPR1 -o EXPR2 ] 如果 EXPR1 或者 EXPR2 为真则为真。 [ (或作 test)内建命令对条件表达式使用一系列基于参数数量的规则来求值。

-eq 等于 -ne 不等于 -gt 大于 -lt 小于 -le 小于等于 -ge 大于等于 -z 空串 * = 两个字符相等 * != 两个字符不等 * -n 非空串

(7) 保留字符及其含义

$ shell变量名的开始,如$var

| 管道,将标准输出转到下一个命令的标准输入

# 注释

& 在后台执行一个进程

? 匹配一个字符

* 匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)

$- 使用set及执行时传递给shell的标志位

$! 最后一个子进程的进程号

$# 传递给shell script的参数个数

$* 传递给shell script的参数

$@ 所有参数,个别的用双引号括起来

$? 上一个命令的返回代码

$0 当前shell的名字

$n (n:1-) 位置参数

$$ 进程标识号(Process Identifier Number, PID)

> file 输出重定向

<file 输入重定向

`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`

>>fiile 输出重定向,append

转义符及单引号:

$echo "$HOME $PATH" 
> /home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin: 

$echo '$HOME $PATH' 
> $HOME $PATH 

$echo \\$HOME $PATH
> $HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbw

注:shell中单引号和双引号的区别

对于常规的字符串定义变量值应添加双引号,并且等号后面不能用空格,需要强引用的,则需要单引号,需要命令引用的使用反引号;

单引号:所见即所得,即输出时会将单引号内的所有内容都原样输出,或者描述为单引号里面看到的什么就输出什么,这成为强引用。

双引号:输出双引号的所有内容;如果内容中有命令(要反引)、变量、特殊转义,会先把变量、命令、转义字符解析出结果,然后在输出最终内容,这称为弱引。

反引号:一般用于命令,执行的时候命令会被执行,相当于$(),赋值和输出都要用反引号引起来。

time=date
echo $time
> date

time=`date`
echo $time
> 2020年 08月 01日 星期四 08:14:11 CST

time="`date`"
echo $time
> 2020年 08月 01日 星期四 08:14:11 CST

time='`date`'
echo $time
> `date`

# 如果使用 echo -e 无论单引号双引号都能进行转义
time1 = '这一行\n下一行'
time2 = "这一行\n下一行"
echo -e $time1
> 这一行
> 下一行
echo -e $time2
> 这一行
> 下一行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值