linux编程-bash

1. 基本语法

1.1 注释

注释以"#"开始, "#"后面的内容被忽略.

说明:

  1. "#" 前可以没有语句, 表示整行被注释.
  2. "#" 前可以有语句, 只注释"#"后面的内容.

1.2 变量

$ VAR_NAME='value'      # 变量赋值, 注意等号两边不能有空格
$ echo "I am $VAR_NAME, ${VAR_NAME}" # 通过$符使用变量
$
$ LIST=$(ls)               # 将shell命令结果赋值给变量,
$ SERVER_NAME=$(hastname)  # 参考子命令扩展

全局变量:在脚本中任何位置都可以使用该变量,shell变量默认都是全局变量。
本地变量:可以在函数内部声明本地变量,使用local关键字。

1.3 数组

数组直接赋值, 注意等号两边不能有空格.

$ myarr=()                        # 定义一个空数组  
$ myarr=(1 2 3 4 5)               # 数组元素都为数字  
$ myarr=(one two three four five) # 数组元素都为字符串  
$ myarr=(1 two 3 four 5)          # 数组元素由数字和字符串组成  
$ myarr=(1 two 3 "a line")        # 数组元素包含空格  

通过数组下标赋值

$ myarr[0]=1         # 通过下标为数组元素赋值  
$ myarr[2]=test  
$ myarr[9]="a line"  # 下标超出原数组长度也可以直接赋值  

获取数组信息

$ myarr=(one two three four five) # 数组元素都为字符串
$ 
$ echo ${myarr[0]}          # 通过下标访问元素
one
$ echo ${myarr[@]}          # 打印整个数组
one two three four five

$ echo ${#myarr[*]}         # 获取数组长度
5
$ echo ${#myarr[@]}         # 同上
5
 
$ echo ${#myarr[9]}         # 获取元素的长度  
0
$ echo ${myarr[@]:3:2}      # 数组切片, 从第3个元素开始, 元素个数为2  
four five  

$ myarr+=(six seven)        # 给数组追加新数组  
$ echo ${myarr[@]}  
one two three four five six seven  

$ unset ${myarr[9]}         # 删除数组某个元素  
$ unset myarr               # 删除整个数组  

通过变量做下标:

$ myarr=(one two three four five)  
$ i=0  
$ echo ${myarr[i]}    # 通过变量做下标  
one  
$ echo ${myarr[i+1]}  # 通过变量运算做下标  
two  

遍历数组: 通过in关键字, ${myarr[@]}加双引号, 元素中有空格不会被拆分. 如果不加双引号, 最后一个元素中有空格, 会被拆分.

$ myarr=(one two three four "a line")
$ for ele in "${myarr[@]}"; do echo $ele ; done
one
two
three
four
a line
$

遍历数组: 通过下标自增

$ myarr=(one two three four "a line")
$ for ((i=0;i<${#myarr[*]};i++)); do echo ${myarr[i]}; done
one
two
three
four
a line
$

遍历数组: 通过下标自增2

$ for ((i=0;i<${#myarr[*]};i+=2)); do echo "${myarr[i]}, ${myarr[i+1]}"; done  
one, two  
three, four  
a line,  
$  

1.4 命令行参数

$0 # 脚本名称
$1 # param1
$2 # param2
...
$@ # 整个参数数组,$1 $2 ...,不包括$0。

1.5 获取用户输入

read命令接收键盘的输入

$ read -p "Please Enter Your Name: " NAME
Please Enter Your Name: ZhangSan
$ echo "Your Name Is: $NAME"
Your Name Is: ZhangSan

1.6 文件测试

注意,方括号与中间内容必须有空格。

[ -d FILE_NAME ] # FILE_NAME是dir
[ -e FILE_NAME ] # FILE_NAME存在
[ -f FILE_NAME ] # FILE_NAME存在且是regular file
[ -r FILE_NAME ] # FILE_NAME是readable
[ -s FILE_NAME ] # FILE_NAME存在且不为空
[ -w FILE_NAME ] # FILE_NAME有write permission
[ -x FILE_NAME ] # FILE_NAME是excutable

1.7 字符串测试

注意,方括号与中间内容必须有空格。

[ -z STRING ]         # STRING是空的
[ -n STRING ]         # STRING不是空的
[ STRING1 = STRING2 ] # 字符串相等
[ STRING1 != STRING2 ]# 字符串不相等

1.8 算术测试

注意,方括号与中间内容必须有空格。

[ var1 -eq var2 ] # 相等
[ var1 -ne var2 ] # 不相等
[ var1 -lt var2 ] # var1 less than var2
[ var1 -le var2 ] # var1 less than or equal to var2
[ var1 -gt var2 ] # var1 greater than var2
[ var1 -ge var2 ] # var1 greater than or equal to var2

1.9 逻辑运算 &&, ||

逻辑与: &&
逻辑或: ||

例子:创建目录tempDir成功后,进入该目录并创建子目录subTempDir

mkdir tempDir && cd tempDir && mkdir subTempDir

1.10 if条件判断

语法1: 只有一个if分支

if [ condition-is-true ]
then # 注意,then不能直接写在if语句行, 除非在if语句行尾加分号后再使用then
    command 1
    …
    command N
fi

语法2: if带elif分支

if [ condition-is-true ]; then
    command 1
elif [ condition-is-true ]; then
    command 2
elif [ condition-is-true ]; then
    command 3
else
    command 4
fi

1.11 case条件判断

语法:

case "$VAR" in
  pattern_1)
    # commands when $VAR matches pattern 1
    ;;
  pattern_2)
    # commands when $VAR matches pattern 2
    ;;
  *)
    # This will run if $VAR doesnt match any of the given patterns
    ;;
esac

例子:

read -p “Enter the answer in Y/N: ” ANSWER
case “$ANSWER” in
    [yY] | [yY][eE][sS])
        echo “the answer is Yes.”
        ;;
    [nN] | [nN][oO])
        echo “the answer is No.”
        ;;
    *)
        echo “invalid answer .”
        ;;
esac

1.12 for循环

语法1:

for VARIABLE_NAME in ITEM_1 ITEM_N
do
  command 1
  command 2
    ...
    ...
  command N
done

例:

$ for V in a b c; do echo $V; done
a
b
c
$ 
$ COLORS="red green blue"
$ for C in $COLORS; do echo $C ; done
red
green
blue
$
FIELS=$(ls *txt)
NEW="new"
for FILE in $FILES
do
    echo "Renaming $FILE to $NEW-$FILE"
    mv $FILE $NEW-$FILE
done

语法2:

for (( VAR=1;VAR<N;VAR++ )) # 注意, 有两对小括号, 参考算术扩展.
do
  command 1
  command 2
    ...
    ...
  command N
done

$ for ((I=0;I<5;I++)); do echo $I; done
0
1
2
3
4

1.13 while循环

语法:

while [ condition-is-true ]
do
    command 1
    command 2
    …
    command N
done

例: 按行读取文件/ect/passwd的内容

LINE=1
while read CURRENT_LINE; do       # CURRENT_LINE是内置变量, 当前行的内容.
    echo "${LINE}: $CURRENT_LINE"
    ((LINE++))                    # 变量自增, 见模式扩展
done < /etc/passwd

1.14 循环控制

continue # 结束本次循环节,进入下一个循环节
break    # 结束整个循环

1.15 函数

语法:

function func_name() {
    command 1
    command 2
    ...
    command N
}

例子:声明一个函数并调用

#!/bin/bash
function myFunc() {
    echo "Shell Scripting Is Fun!"
}

myFunc # 函数调用

函数参数

$1 第一个参数
$2 第二个参数
$@ 参数数组

参数之间使用空格分隔

#!/bin/bash
function add() {
    let "SUM=$1+$2"
    return $SUM
}

A=$(add 3 4)
echo "3+4=${A}"

2. 通配符 or 模式扩展

共有8种模式扩展,按优先级顺序分为:

序号 名称 说明
1 brace expansion 大括号 {}
2 tilde expansion 波浪号 ~
3 parameter and variable expansion 参数? 变量?
4 arithmetic expansion 算术扩展
5 command substitution 子命令扩展
6 word splitting 词分割
7 pathname/filename expansion 路径名扩展
8 process substitution 过程替换

2.1 brace expansion {}

大括号扩展。

有两种形式:

  1. pre + {str1,str2[,…,strN]} + post
  2. pre + {START..END[..INCR]} + post # 扩展成一个序列

使用注意事项:

  1. 这是单纯对字符串的扩展,即使不存在对应的文件名,也会扩展成功。
  2. 逗号两边都不允许有空格。
  3. 大括号与中间内容之间不允许在空格。
  4. 逗号前可以为空,表示待扩展的为空字符,见后面例子。
  5. 大括号可以嵌套使用,见后面例子。
  6. 大括号可以与其它扩展联用,见后面例子。
  7. START..END用于扩展为一个序列,一般用于循环。
  8. START..END支持逆序。
  9. START..END支持添加前导字符。
  10. START..END支持指定步长: START..END..INCR。
  11. START..END可以级联使用,类似于多重循环。

例子1: {str1,str2[,…,strN]}:

$ echo g{o,oo,ra}d
god good grad

$ cp a.log{,.bak}     # 逗号前为空,扩展为
cp a.log a.log.bak

$ echo a{1, 2}b       # 逗号两边有空格,不作为扩展处理,当作以空格分隔的两个str。
a{1, 2}b

$ echo {j{p,pe}, pn}g # 嵌套使用大括号
jpg jpeg png

$ echo {cat, d*}      # 大括号与其它扩展联用
cat dog door          # 注意,d*是文件名扩展,只有存在以d开头的文件名时才会扩展成功,
                      #      否则,会直接将d*当作变通str而不进行扩展。

例子2: {START..END[..INCR]}:

$ echo d{a..c}g       # 普通范围扩展
dag dbg dcg

$ echo Num{5..1}      # 逆序扩展
Num5 Num4 Num3 Num2 Num1

$ echo {08..12}       # 添加前导0,将所有结果扩展成等宽的字符串
08 09 10 11 12

$ echo {08..17..2}    # 对数字序列指定步长
08 10 12 14 16

$ echo {a..g..2}      # 对字母序列也可以指定步长
a c e g

$ echo {x..z}{0..2}
x0 x1 x2 y0 y1 y2 z0 z1 z2

2.2 tilde expansion ~

波浪号扩展。
主要形式如下:

待扩展字符 扩展后字符 说明
~ /home/`whoami` 当前用户的主目录
~username /home/username 特定用户的主目录
~+ `pwd` 当前路径, 相当于pwd
~root /root /root目录

注意:

  1. ~到/之间的字符都称为tilde-prefix。
  2. 波浪号用于匹配目录,对于~username,如果username不存在该用户,则扩展不成功。按原样输出字符。

2.3. parameter and variable expansion

参数和变量扩展。

两个符号

符号 说明
$ 可用于参数和变量扩展,也可以用于命令替换和算术扩展。它是参数、变量、命令、算术扩展的“引导符”
{} 在参数(参数小于10时)和变量扩展中,{}不是必须的,但它可以起到保护变量的作用,当变量名后跟着其它字符时,用于区分变量名与字符。

注意:

  1. 使用!可以将变量的值又当作变量,多做一次扩展。
  2. 可以使用${VAR:=defaultvalue}的方式指定默认值

例子:普通变量扩展

$ NAME=LINUX
$ echo pre${NAME}post # 变量扩展,将${NAME}替换为LINUX。
preLINUXpost
$ 
$ echo pre$NAMEpost   # 没使用{},认为$NAMEpost是一个变量,为空,所以扩展为pre。
pre

例子:使用!

$ NAME=LINUX
$ LINUX=Ubuntu
$ echo pre${NAME}post
preLINUXpost
$ echo pre${!NAME}post # 加一个!,将NAME的值LINUX又当作变量名,扩展为Ubuntu。
preUbuntupost

例子:自定义变量默认值

$ NAME=      # 清空NAME的值,由于bash未定义的变量值都是空,所以直接赋值为空。
$ echo pre{NAME:=WINDOWS}post # NAME未定义,NAME赋值为默认值WINDOWS。
preWINDOWSpost

$ echo $NAME # 在上一语句中,NAME已经被赋值为指定的默认值
WINDOWS

2.4 arithmetic expansion

算术扩展。
语法: $((EXPRESSION))

注意:

  1. (())可以进行算术运算。
  2. (())可以将非10进制数转为10进制数。
  3. (())中可以有空格,也可以没空格。
  4. (())中可以使用变量。

例子:

$ echo $(( 3 + 4 ))
7

$ A=3
$ echo $(($A+4))
7

$ A=$((011)); echo $A # 以0开头的是8进制数
9

$ A=$((0x11)); echo $A # 以0x开头的是16进制数
17

2.5. command substitution

命令替换。
语法:$(command) 或 `command`
作用:将command的输出作为当前命令的一部分。

注意:

  1. $(command) 使用小括号,是命令替换,${VAR}使用大括号是变量扩展。$((expr))是算术扩展。
  2. $(command) 将command的输出作为当前命令,而不只是执行command
$ date
2020年 08月 12日 星期三 11:06:11 CST

$ $(date) # 本条命令相当于在命令行执行“2020年 08月 12日 星期三 11:06:11 CST”。
2020年: command not found

2.6 word splitting

词分离。

如果参数扩展、命令替换、算术扩展没有在双引号中,shell将对其结果进行“词分离”操作。

shell把$IFS中的每一个字符看做是分隔符。
默认情况下$IFS的值是“
可以修改$IFS的值。

$ IFS=’123’     # 1,2,3都作为词分隔符
$ echo a1b2c3d4 # 在命令行中,不进行“word splitting”操作
a1b2c3d4

$ echo $(echo a1b2c3d4) # 将echo a1b2c3d4放到命令替换语句中
a b c d4                # 以1,2,3将a1b2c3d4分割为a b c d4共4个份
$ ls $(echo a1b2c3d4)   # 将echo a1b2c3d4放到命令替换语句中
ls: canot access ‘a’: No such file or directory # a b c d4放到ls命令后面
ls: canot access ‘b’: No such file or directory # 相当于执行 ls a b c d4
ls: canot access ‘c’: No such file or directory # 
ls: canot access ‘d4’: No such file or directory# 

2.7 filename expansion

文件名扩展.

全为3种

扩展名 说明
* 匹配0~N个任意字符, 不能匹配空格 ls .txt, ls .
? 匹配1个任意字符,不能匹配空格 ls ?.txt
[abc] 匹配a、b、c中任意一个字符 ls [ab].txt
[^abc]或[!abd] 匹配a、b、c以外的任意一个字符 ls [!ab].txt
[start-end] 匹配连续范围内的任意一个字符 ls [a-c].txt
[^start-end] 匹配连续范围外的任意一个字符 ls [^1-3].txt

注意:

  1. 只有对应文件名存在时才能扩展成功,否则原样输出。比如 ls *.txt如果路径下没有.txt文件,则直接将 .txt作为ls的参数,会报告没有“.txt”这个文件。
  2. *和?不能匹配空格。

例子:

$ ls *          # 所有文件,但不包含隐藏文件
$ ls .*         # 所有隐藏文件,但包含了’.’和’..’这两个目录
$ ls .[!.]*     # 所有隐藏文件,不包括’.’和’..’这两个目录
$ ls [a-c].txt  # a.txt b.txt c.txt,只扩展为已经存在的文件。
                #    比如如果b.txt不存在,则只扩展为a.txt c.txt

2.8. process substitution

过程替换。

过程替换与重定向比较容易搞混:重定向是一口入一口出,过程替换是多入(实际是并没有“入”)。看如下试验:

$ ls <(true) # 注意,”<”和”(“之间不能有空格,否则认为是重定向,语法错误。
/dev/fd/63   # 可以认为<(true)就是文件/dev/fd/63

$ vim <(ls)  # 通过vim窗口底部显示,确认文件名为/dev/fd/63.
1 a.txt
2 c.txt
3 ex.sh

“/dev/fd/63” [readonly] 3L, 18C

$ ls <(true) >(true) # 可以认为<(LIST) 和 >(LIST)分别都是一个文件名。
/dev/fd/62 /dev/fd/63

if the “>(LIST)” form is used, writing to the file will provide input for LIST.
if the “<(LIST)” form is used, the file passed as an argument should be read to obtain the output of LIST

举例说明:

$ cat <(ls) # <(ls)是一个文件,文件内容是ls的结果,cat这个文件。
$ ls > >(cat) # >(cat)是一个文件,文件的内容是ls重定向来的,cat这个文件。

例子:将两个文件分别sort并排序,然后找出在两个文件中都出现的行。

$ cat a.txt
d3
d4
a0
a1
a2
$ cat b.txt
c0
c1
c2
d3
d4

$ comm <(sort -u a.txt) <(sort -u b.txt) # 给comm传两个文件,文件内容是sort -u结果。
a0         #---第一列,仅出现在第一个文件中的行
a1
a2
    c0     #---第二列,仅出现在第二个文件中的行
    c1
    c2
        d3 #---第三列,在两个文件中都出现的行
        d4
原文地址:https://www.cnblogs.com/gaiqingfeng/p/13632744.html