shell 基础
shell 分类
常见的shell 主要分为以下几种
bash ,dash,csh,tcsh,zsh等
shell 的内建命令与外部命令
外部命令(有时也称为文件系统命令)是存在于bash shell之外的程序。也就是说,它并不属于shell程序的一部分。
外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin目录中。
内建命令
嵌入到shell内部的命令,内建命令已经和shell编译成一体,作为shell的组成部分存在,无须借助外部程序文件来执行
查看内建与外部命令
[gbasedbt@gbase170 ~]$ type -a echo
echo 是 shell 内嵌
echo 是 /usr/bin/echo
[gbasedbt@gbase170 ~]$ type -a ps
ps 是 /usr/bin/ps
[gbasedbt@gbase170 ~]$ type -a cd
cd 是 shell 内嵌
cd 是 /usr/bin/cd
[gbasedbt@gbase170 ~]$ type -a exit
exit 是 shell 内嵌
可以发现 ,有些为内建命令,有些为外部命令,也有一部分为都有
在shell 中默认使用为 内建命令,如使用外部命令,可以加上全路径
例如
echo a --内建命令
/usr/bin/echo a --外部命令
区别
每当执行外部命令时,就会创建一个子进程。这种操作称为衍生(forking)
在使用内建命令时,不需要衍生子进程。因此,内建命令的系统开销较低。
构建基础脚本
1 基本格式
通常以 .sh结尾
开头指明shell 类型
#!/bin/bash
#!/bin/env bash
2 显示输出
echo aaa
echo "aaa"
echo "a'b'a"
3 使用变量
环境变量
用户自定义变量
var1=10
var2=-57
var3=testing
var4="still more testing"
用户自定义变量可以通过$引用
#!/bin/bash
days=10
guest="Katie"
echo "$guest checked in $days days ago"
4 命令替换
shell 脚本中最有用的特性之一是可以从命令输出中提取信息并将其赋给变量。把输出赋给变量之后,就可以随意在脚本中使用了。
testing=`date`
testing=$(date)
5 重定向输入和输出
有时候,你想要保存命令的输出而不只是在屏幕上显示。bash shell 提供了几个运算符,它们可以将命令的输出重定向到其他位置(比如文件)。
重定向既可用于输入,也可用于输出,例如将文件重定向,作为命令输入。
输出重定向
date > test6
输出追加到已有文件中
date >> test6
输入重定向
wc -l < /etc/passwd
内联输入重定向
这种方法无须使用文件进行重定向,只需在命令行中指定用于输入重定向的数据即可
[gbasedbt@gbase170 ~]$ wc -l <<EOF
> a
> b
> c
> EOF
3
6 管道
rpm -qa | sort | more
7 执行数学运算
expr 命令
尽管标准运算符在 expr 命令中工作得很好,但在脚本或命令行中使用时仍有问题出现。
使用方括号
bash shell保留了 expr 命令,但同时也提供了另一种更简单的方法来执行数学运算。在bash中,要将数学运算结果赋给变量,可以使用$和方括号($[ operation ])
var1=$[1 + 5]
var2=$[$var1 * 2]
bash shell 的数学运算符只支持整数运算。
8 浮点数解决方案
有几种解决方案能够克服bash只支持整数运算的限制。最常见的做法是使用内建的bash计算器bc。
[root@gbase170 ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
12 * 2.2
26.4
在脚本中使用bc
#!/bin/bash
var1=20
var2=3.14159
var3=$(echo "scale=4; $var1 * $var1" | bc)
var4=$(echo "scale=4; $var3 * $var2" | bc)
#!/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
)
在bash计算器中创建的变量仅在计算器中有效,不能在shell脚本中使用
9 退出脚本
shell 中运行的每个命令都使用退出状态码来告诉shell自己已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由其传给shell。
查看退出状态码
echo $?
exit 命令
在默认情况下,shell脚本会以脚本中的最后一个命令的退出状态码退出。exit命令允许在脚本结束时指定一个退出状态码
exit 1
var1=10
exit $var1
流程控制
if-then语句
最基本的结构化命令是if-then语句。if-then语句的格式如下:
if command
then
commands
fi
if command; then
commands
fi
bash shell 的 if 语句会运行if 之后的命令。如果该命令的退出状态码为0(命令成功运行),那么位于then部分的命令就会被执行。如果该命令的退出状态码是其他值,则then 部分的命令不会被执行,bash shell会接着处理脚本中的下一条命令。fi 语句用来表示if-then 语句到此结束。
#!/bin/bash
if pwd
then
echo "it worked"
fi
if-then-else 语句
if command
then
commands
else
commands
fi
if-then-elif 语句
if command1
then
command set 1
elif command2
then
command set 2
elif command3
then
command set 3
elif command4
then
command set 4
fi
test 命令
if-then语句不能接受除命令退出状态码之外的条件。
但是,在bash shell中有个好用的工具可以帮你使用if-then语句测试其他条件。
test 命令可以在if-then语句中测试不同的条件。如果test命令中列出的条件成立,那么test 命令就会退出并返回退出状态码0。这样 if-then语句的工作方式就和其他编程语言中的if-then 语句差不多了。如果条件不成立,那么test命令就会退出并返回非0的退出状态码,这使得if-then语句不会再被执行。
if test condition
then
commands
fi
[ condition ]
bash shell 提供了另一种条件测试方式,无须在if-then语句中写明test命令:
if [ condition ]
then
commands
fi
方括号定义了测试条件。注意,第一个方括号之后和第二个方括号之前必须留有空格,否则
就会报错。
test 命令和测试条件可以判断3类条件。
数值比较
字符串比较
文件比较
数值比较
n1 -eq n2 检查n1是否等于n2
n1 -ge n2 检查n1是否大于或等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于或等于n2
n1 -lt n2 检查n1是否小于n2
n1 -ne n2 检查n1是否不等于n2
字符串比较
str1 = str2 检查str1是否和str2相同
str1 != str2 检查str1是否和str2不同
str1 < str2 检查str1是否小于str2
str1 > str2 检查str1是否大于str2
-n str1 检查str1的长度是否不为0
-z str1 检查str1的长度是否为0
字符串顺序
大于号和小于号必须转义,否则shell会将其视为重定向符,将字符串值当作文件名
string1=soccer
string2=zorbfootball
if [ $string1 \> $string2 ]
then
echo "$string1 is greater than $string2"
else
echo "$string1 is less than $string2"
fi
文件比较
它允许测试 Linux 文件系统中文件和目录的状态。
-d file 检查file是否存在且为目录
-e file 检查file是否存在
-f file 检查file是否存在且为文件
-r file 检查file是否存在且可读
-s file 检查file是否存在且非空
-w file 检查file是否存在且可写
-x file 检查file是否存在且可执行
-O file 检查file是否存在且属当前用户所有
-G file 检查file是否存在且默认组与当前用户相同
file1 -nt file2 检查file1是否比file2新
file1 -ot file2 检查file1是否比file2旧
复合条件测试
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2 ]
第一种布尔运算使用布尔运算符AND来组合两个条件。要执行then部分的命令,两个条件都必须满足。
第二种布尔运算使用OR布尔运算符来组合两个条件。如果任意条件为真,那么then部分的命令就会执行。
if-then 的高级特性
bash shell 还提供了 3个可在if-then 语句中使用的高级特性。
在子shell中执行命令的单括号。
用于数学表达式的双括号。
用于高级字符串处理功能的双方括号。
使用单括号
单括号允许在if语句中使用子shell。单括号形式的test 命令格式如下:
(command)
在bash shell 执行 command 之前,会先创建一个子shell,然后在其中执行命令。如果命令成功结束,则退出状态码会被设为0,then部分的命令就会被执行。如果命令的退出状态码不为0,则不执行then部分的命令。
if (echo $BASH_SUBSHELL)
then
echo "The subshell command operated successfully."
else
echo "The subshell command was NOT successful."
fi
使用双括号
双括号命令允许在比较过程中使用高级数学表达式。test命令在进行比较的时候只能使用简单的算术操作。双括号命令提供了更多的数学符号
(( expression ))
expression可以是任意的数学赋值或比较表达式
val++ 后增
val-- 后减
++val 先增 --val 先减
! 逻辑求反
~ 位求反
** 幂运算
<< 左位移
>> 右位移
& 位布尔AND
| 位布尔OR
&& 逻辑AND
|| 逻辑OR
双括号中表达式的大于号不用转义
使用双方括号
双方括号命令提供了针对字符串比较的高级特性。
[[ expression ]]
expression 可以使用test 命令中的标准字符串比较。除此之外,它还提供了test命令所不具备的另一个特性——模式匹配。
双方括号在bash shell中运行良好。不过要小心,不是所有的shell都支持双方括号。
if [[ $BASH_VERSION == 5.* ]]
then
echo "You are using the Bash Shell version 5 series."
fi
当在双中括号内使用==运算符或!=运算符时,运算符的右侧被视为通配符。如果使用的是=~运算符,则运算符的右侧被视为POSIX扩展正则表达式。
case 命令
有了case 命令,就无须再写大量的elif 语句来检查同一个变量的值了。case 命令会采用列表格式来检查变量的多个值
case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
for 命令
基础语法
for var in list
do
commands
done
读取列表中的值
for test in Alabama Alaska Arizona Arkansas California Colorado
do
echo The next state is $test
done
读取列表中的复杂值
for test in I don\'t know if "this'll" work
do
echo "word:$test"
done
for test in Nevada "New Hampshire" "New Mexico" "New York"
do
echo "Now going to $test"
done
从变量中读取值列表
list="Alabama Alaska Arizona Arkansas Colorado"
for state in $list
do
echo "Have you ever visited $state?"
done
从命令中读取值列表
file="states.txt"
for state in $(cat $file)
do
echo "Visit beautiful $state"
done
更改字段分隔符
IFS 环境变量
IFS 定义了bash shell用作字段分隔符的一系列字符。在默认情况下,bash shell会将下列字
符视为字段分隔符。
空格
制表符
换行符
如果bash shell在数据中看到了这些字符中的任意一个,那么它就会认为这是列表中的一个
新字段的开始。在处理可能含有空格的数据(比如文件名)时会出现问题
解决这个问题的办法是在shell脚本中临时更改IFS环境变量的值来限制被bash shell视为字
段分隔符的字符。如果想修改IFS的值,使其只能识别换行符,可以这么做:
IFS=$'\n'
将该语句加入脚本,告诉bash shell忽略数据中的空格和制表符。
IFS.OLD=$IFS
IFS=$'\n'
<在代码中使用新的IFS值>
IFS=$IFS.OLD
使用通配符读取目录
for file in /home/rich/test/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
也可以在for命令中列出多个目录通配符
for file in /home/rich/.b* /home/rich/badtest
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
else
echo "$file doesn't exist"
fi
done
C语言风格的for命令
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
使用多个变量
尽管可以使用多个变量,但只能在for循环中定义一种迭代条件
for (( a=1, b=10; a <= 10; a++, b-- ))
do
echo "$a - $b"
done
while 命令
while 命令在某种程度上糅合了if-then语句和for循环。while命令允许定义一个要测试的命令,只要该命令返回的退出状态码为0,就循环执行一组命令。它会在每次迭代开始时测试test命令,如果test命令返回非0退出状态码,while命令就会停止执行循环。
while test command
do
other commands
done
until 命令
与while 命令工作的方式完全相反,until 命令要求指定一个返回非0退出状态码的测试命令。只要测试命令的退出状态码不为0,bash shell就会执行循环中列出的命令。一旦测试命令返回了退出状态码0,循环就结束了。
until test commands
do
other commands
done
循环控制
break命令
continue命令
break命令
break命令是退出循环的一种简单方法。你可以用break命令退出任意类型的循环,包括while循环和until循环。
1. 跳出单个循环
#!/bin/bash
# breaking out of a for loop
for var1 in 1 2 3 4 5 6 7 8 9 10
do
if [ $var1 -eq 5 ]
then
break
fi
echo "Iteration number: $var1"
done
2. 跳出内层循环
for (( a = 1; a < 4; a++ ))
do
echo "Outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -eq 5 ]
then
break
fi
echo " Inner loop: $b"
done
done
3. 跳出外层循环
有时你位于内层循环,但需要结束外层循环。break命令接受单个命令行参数:
break n
for (( a = 1; a < 4; a++ ))
do
echo "Outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -gt 4 ]
then
break 2
fi
echo " Inner loop: $b"
done
done
continue 命令
continue 命令可以提前中止某次循环,但不会结束整个循环。
for (( var1 = 1; var1 < 15; var1++ ))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue
fi
echo "Iteration number: $var1"
done
和break 命令一样,continue命令也允许通过命令行参数指定要继续执行哪一级循环:
continue n
for (( a = 1; a <= 5; a++ ))
do
echo "Iteration $a:"
for (( b = 1; b < 3; b++ ))
do
if [ $a -gt 2 ] && [ $a -lt 4 ]
then
continue 2
fi
var3=$[ $a * $b ]
echo " The result of $a * $b is $var3"
done
done
处理循环的输出
for file in /home/rich/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif
echo "$file is a file"
fi
done > output.txt
这种方法同样适用于将循环的结果传输到另一个命令
for state in "North Dakota" Connecticut Illinois Alabama Tennessee
do
echo "$state is the next place to go"
done | sort