Shell:再学shell

#!/bin/bash  # 标明用哪种解析器去处理, 固定格式

# 定义变量: =左右不要有空格
# 一般情况下,定义的变量都是字符串; 如果没有空格, 右侧的值可以不用引号包裹
# my_name="Lynn Zhang"
# age=18

# 取变量的值, 可以是 $var 或 ${var}的形式
# 如果做字符串拼接,一般使用 ${var}的格式, 如: "${var}loveyou"
# echo ${my_name} ${age}

# echo '删除变量age'
# unset age
# echo '$age没有打印了' $age 


# 常用的环境变量
# $PATH: 设置命令的搜索路径, 以 : 分割
# $HOME: 当前的登录用户
# $SHELL: 当前shell解析器类型
# $LANG: 当前系统语言环境
# printf '$PATH: %s, \n$HOME: %s, \n$SHELL: %s, \n$LANG: %s', $PATH, $HOME, $SHELL, $LANG


# 字符串截取
# ${变量名:start:length}: 从str左边第start开始,截取length的长度(start值从0开始)
# ${变量名:start}: 从str左边第start开始,截取到最后
# ${变量名:0-start:length}: 从str右边第start个字符开始,向右截取length个字符串(start从1开始)
# ${变量名:0-start}: 从str右边第start个字符开始,截取到最后
# ${变量名#*substr}: 从str左侧开始搜索第一个匹配的子串开始,向右截取
# ${变量名##*substr}: 从str左侧开始搜索最后一个匹配的子串开始,向右截取
# ${变量名%substr*}: 从str右侧开始搜索第一个匹配的子串开始,向左截取
# ${变量名%%substr*}: 从str左侧开始搜索最后一个匹配的子串开始,向左截取

# 以下为字符串截取的示例:
# addr="广东省深圳市宝安区广东省南山区"
# echo '从第3个字符开始,截取3个字符' ${addr:3:3}
# echo '从第3个字符开始截取' ${addr:3}
# echo '从倒数第6个字符开始,往后截取3个字符' ${addr:0-6:3}
# echo '从倒数第6个字符开始,往后截取' ${addr:0-6}
# *在哪,决定从哪开始查找
# #代表截取右边的, 一个#代表查找第一个,##代码查找最后一个
# ##代表查找最后一个,截取右边的
# %代表截取左边的 %代表查找第一个, %%代表查找最后一个
# echo '从左侧第一个"省"开始,往右截取' ${addr#*省}
# echo '从左侧查找最后一个"省",往右截取' ${addr##*省}
# echo '从右侧开始查找第一个省,往左边截取' ${addr%省*}
# echo '从右侧开始查找最后一个省,往左边截取' ${addr%%省*}



# 定义数组: 用()包裹, 元素用空格分割
# nums=(1 22 '333' "4444")
# echo '打印所有的元素 ${array_num[@]} 或 ${array_name[*]}' ${nums[@]}
# echo '获取数组的个数 ${#array_name[*]}' ${#nums[@]}
# echo '获取指定元素的字符个数 ${#array_name[下标]}' ${#nums[0]} ${#nums[2]}

# arr=('55555' 666666)
# 拼接数组
# new_arr=(${nums[@]} ${arr[@]})
# new_arr=(${nums[*]} ${arr[*]})
# echo ${new_arr[@]}



# 内置命令
# 1. 设置别名
# alias hello='echo hello'
# unalias hello

# 2. 打印
# -n 不换行输出
# echo -n 'hello '
# echo 'world'

# -e 转义
# echo -e '你了解我吗?\n你这个三炮'

# -e \c 清除换行
# echo -e '你这是太搞笑了\c'
# 换行
# echo



# 3.读取输入
# -a array 把读取到的数据赋值给数组
# -d delimiter 指定读取结束的位置,而不是一个换行符
# -n num 读取num个字符
# -p prompt 显示prompt的提示信息
# -s 静默模式,不会再屏幕上显示输入的字符
# -t seconds 设置超时时间,单位s. 

# 3.1 读取多个值给多个变量
# read -p '请输入姓名 年龄 爱好:' name age hobby
# echo "您输入的是 ${name} ${age} ${hobby}"

# 3.2 读取一个字符
# read -n 1 -p "您确定要升级吗?(请输入y/n): " char
# printf "\n"
# echo "您输入的是: $char"

# 3.3 限制时间输入 -s为静默: 输入的不在屏幕显示
# read -n 1 -t 5 -p '请输入是否要升级: y/n' answer
# printf '\n'
# echo $answer

# 退出进程, 并返回错误码
# exit 44



# declare
# 语法: declare [+/-][aArxif] [变量名称=值]
# +/-: -指定变量的属性; +取消变量所设的属性
# a: 设置为普通索引数组
# A: 设置key-value的关联数组
# r: 设置为只读的变量,也可以用readonly
# x: 设置变量为环境变量, 也用export
# i: 设置为int类型的变量
# f: 设置为一个函数变量

# 关联数组
# declare -A map=([one]='one_v' [two]='two_v')
# echo ${map[@]} ${#map[@]}
# 获取指定可以的值: ${关联数组变量名[key]}
# echo ${map['one']} ${map['two']}

# declare -i m_age=18
# echo ${m_age}
# m_age='ab'
# echo ${m_age}

# m_age:只读变量,接下来赋值会报错
# declare -i -r m_age=18
# m_age='abcd'



# expr表达式
# echo `expr 1 + 1`
# result=`expr 2 \* 2`
# echo $result



# 比较运算符
# 推荐使用 [[]], 既可以实现数字和字符串比较,且不需要转义,也不会发生word splitting
# 比较操作符有: ==或=、 !=、 <、 >、 -z、 -n、 $
# if [[ 1 -eq 1 ]]; then 
#   echo '成立'
# else
#   echo '不成立'
# fi



# 布尔运算符
# 布尔运算符有 !(非运算符,表示取反)、-o(or)、-a(and)
# 注意: 布尔运算符必须放在[]或与test配合使用才有效


# 逻辑运算符
# 逻辑运算符有: !(逻辑非)、&&(逻辑的AND)、||(逻辑的OR)
# 使用 && 或 || 必须放在 [[]] 或 (())中才有效,否则报错



# (())计算,里面的值不用$
# ((a=6+1))
# ((a=6+1,b=a+12))
# echo $a $b
# ((a++))
# echo $((6+4))
# 计算结果赋值
# abc=$((9*8))
# echo $abc

# let 用于赋值, 是最简洁的整数赋值命令
# a=4 b=5 c=6
# let d=a+b
# let e=b+c 
# echo $d $e

# $[]:括号里只能用于计算,不能用于赋值; 而且只能放一个表达式
# a=$[1+6]
# echo $a



# 文件测试运算符
# -d: 是否是目录
# -e: 检测文件(或目录)是否存在
# -f: 是否是普通的文件(既不是目录,也不是设备文件)
# -r: 文件是否是可读的
# -s: 检测文件是否为空(不为空返回true)
# -w: 文件是否是可写的
# -x: 文件是否是可执行的

# if test -e ~/Desktop; then 
#   echo '存在'
# else 
#   echo '不存在'
# fi


# test整数比较
# -eq: 等于则为真
# -ne: 不等于则为真
# -lt: 小于
# -gt: 大于
# -le: 小于等于
# -ge: 大于等于
# if test -8 -gt -9; then 
#   echo '成立'
# else 
#   echo '不成立'
# fi

# test字符串比较
# ==: 等于返回0,表示成功
# !=: 不等于
# \<: 小于
# \>: 大于
# -z: 字符串长度为0, 则为真
# -n: 字符串长度不为0, 则为真
# if test "a" \> "b"; then 
#   echo '成立'
# else 
#   echo '不成立'
# fi


# bc计算命令: bc [options] [参数]
# -l: mathlib, 使用标准数学库
# -q: quiet, 不显示欢迎信息
# -v: 获取版本信息
# -

# 管道符号的使用: 获取本机IP地址
# 定义函数
# ip() {
#   ifconfig | grep 'broadcast' | awk '{print $2}'
# }
# 调用函数
# ip



# switch语句:
# 匹配模式可以使用简单的正则: 如 *、[a-zA-Z]、8|9、[89]
# 格式如下
# case 值 in
# 匹配模式1)
#   语句...
#   ;;
# 匹配模式2)
#   语句...
#   ;;
# *)
#   语句...
#   ;;
# esac

# 示例:
# read -n 1 -p '请输入星期几(0-7): ' week
# case $week in
# 1)
#   printf '\n周一\n'
#   ;;
# 2)
#   printf '\n周二\n'
#   ;;
# 3)
#   printf '\n周三\n'
#   ;;
# 4)
#   printf '\n周四\n'
#   ;;
# 5)
#   printf '\n周五\n'
#   ;;
# 6)
#   printf '\n周六\n'
#   ;;
# # 0|7) # |表示多重选择
# [07])
#   printf '\n周日\n'
#   ;;
# [a-zA-Z])
#   printf '\n输入的是字母\n'
#   ;;
# *)
#   printf '\n输入的不合法\n'
#   ;;
# esac



# while语句
# 格式如下:
# while 条件; do 
#   命令...
# done

# 示例;
# read -n 1 -p '请输入一个数字:' num
# echo
# i=0
# while ((i < num)); do 
#   # let i++
#   ((i++))

#   if ((i==7)); then 
#     continue # break
#   fi

#   echo -e "打印 ${i}"
# done


# select
# 注意: select是无限循环, 输入空值, 或者无效值, 都不会结束循环;
#       只有当遇到break 或按 ctrl+c才能结束循环
# select hobby in '篮球' '足球' '排球'; do
#   if test -z $hobby; then 
#     echo "您没选择"
#   else 
#     echo "您选择了${hobby}"
#   fi

#   break
# done


# 不符合条件的循环选择
# offerChoose() {
#   echo '请选择您的兴趣:'
#   select hobby in '篮球' '足球' '排球'; do
#   if test -z $hobby; then 
#     offerChoose
#     break
#   else 
#     echo "您选择了${hobby}"
#     break
#   fi

# done
# }
# offerChoose



# 系统函数
# 1. basename: 用户获取文件名的函数
# 语法: basename [string/pathname] [sufix]
# basename './abc.txt'
# basename './abc.txt' '.txt'
# basename '/Users/LynnZhang/Desktop/abc.txt'

# 2. dirname: 返回目录
# 语法: dirname 路径
# dirname '/Users/LynnZhang/Desktop/abc.txt'
# dirname "$HOME/Desktop/abc.txt"



# 函数
# 语法:
# [ function ] funcname() {
#   命令...
#    [return 返回值]
# }
# # 调用
# funcname 参数1 参数2 ...

# 示例:
# function hello() {
#   # echo "参数的个数: $#"
#   # echo "参数有: $@"

#   temp=""
#   for v in "$@"; do
#     temp="${temp}_${v}"
#   done

#   # 如果返回值为数字,可以在调用函数后, 通过$?获取
#   # return 100

#   # 如果返回值非数字, 则需要通过 $()来获取返回值
#   # 需要特别注意的是: 通过 $()来接收到的返回值, 为函数体内的第一个echo的内容
#   echo $temp
# }
# res=$(hello '11' '22' '33' '44')
# echo "res=============${res}"



# 默认输入输出文件
# Unix/Linux命令运行时, 都会打开三个文件: 标准输入stdin 0, 标准输出stdout 1, 标准错误stderr 2

# 重定向语法
# 命令 > file: 将标准输出数据重定向file, 覆盖方式
# 命令 >> file: 将标准输出的数据重定向到file, 追加的方式
# 命令 < file: 将输入重定向到从file读取数据
# 命令 < file > file2: 将输入重定向到从file1读取数据, 以覆盖的方式将输出重定向到file2. 如: cat < abc.txt > aaa.txt
# 命令 fd>[空格可选]file: 将指定文件描述符将数据重定向输出到file中, 覆盖式
# 命令 fd>>[空格可选]file: 将指定文件描述符将数据重定向输出到file中, 追加式
# 命令 > file fd1>& fd2: 将fd1和fd2文件描述符合并输出到file (需要特别注意的是: 2要写在前面. 如: echo 'hello world' >> err.txt 2>&1 和 echoxxx 'hello world' >> err.txt 2>&1 )
# fd1 <& fd2: 将fd1和fd2文件描述符合并从文件读取
# < ljalf 
# heredoc> i 
# heredoc> EOF       
#        2



# cut命令
# 语法: cut [options] filename
# -f: 提取范围, 获取第几列
#   n-: 提取指定第n列 或 字符 或 字节 后面的数据
#   n-m: 提取指定第n列 或 字符 或 字节 到 第m列 或 字符 或 字节 直接的所有数据
#   n1,n2...: 提取枚举列的所有数据
#   -m: 提取指定第m列 或 字符 或 字节 之前的数据
# -d: 自定义分隔符, 默认为制表符
# -c: 提取范围, 以字节为单位进行分割
# -b: 提取范围, 以字节为单位进行分割, 将忽略字符边界, 除非指定 `-n`
# -n: 与 `-b`选项连用, 不分割多字节字符
# 

# 示例:
# ifconfig | grep 'broadcast' | cut -d ' ' -f 2
# echo '我和我的祖国,一刻也不能分割' | cut -nb 4-


#### sort命令
# 常用的options
# -n: number, 依照数值的大小排序
# -r: reverse, 以相反的顺序来排序
# -t: 分割字符, 设置排序时所用的分割字符, 默认是空格
# -k: 指定需要排序的列
# -o: 将排序好的结果存入指定的文件
# -u: 输出去重后的结果

# 示例:
# abc.txt的内容如下:
# 王五 50 77
# 张三 30 44
# 李四 40 55
# 赵六 60 66
# 老七 70 22
# 老吴 50 33

# cat './abc.txt' | sort -nr -k2
# cat './abc.txt' | sort -k2nr
# cat './abc.txt' | sort -k2n -k3n




# awk命令
# -F: 指定输入文件拆分分隔符
# -v: 赋值一个用户定义变量

# 内置变量
# ARGC: 命令行参数个数
# FILENAME: awk浏览的文件名
# FNR: 浏览文件的记录数
# FS: 设置输入域分隔符, 等同于 -F 选项
# NF: 根据分隔符分割后的列数
# NR: 行号
# $n: 整条记录的第n个域
# $NF: 最后一列的信息

# 示例:
# abc.txt内容如下:
# 我爱你 亲爱的祖国
# 再回首 云隔断归途
# 我想你 可爱的 童年        
# 再回首 荆棘密布
# 你永远不懂 我的爱 我的痛 我内心深处的love

# awk '{ print NF $NF }' abc.txt
# awk '/爱/{print NR $0}' abc.txt
# ifconfig | awk '/broadcast/ { print $2 }' # 打印当前IP
# awk '/^我/' abc.txt # 打印以'我'开头的行
# echo -e 'abc\ndef' | awk '{print $0}'

# echo -e 'abc\ndef' | awk 'BEGIN{print "开始了"}{print $0}END{print "结束了"}'
# echo -e 'abc\ndef' | awk 'BEGIN{print "开始了"}END{print "结束了"}{print $0}' # BEGIN和END的顺序不影响打印

# echo -e 'abc\ndef' | awk -v aaa=0 'BEGIN{print "开始了"} {aaa++} END{print aaa}' # 设置变量
# echo -e 'I love you     abc' | awk '{ print $1"&&"$2"&&"$3"&&"$4 }' # 输出域拼接
# echo 'I     love you abc   ' | awk -v res="" '{for(i=1;i<=NF;i++){res=res$i}} END{print res}' # 输出域拼接

# calc.txt
# 张三 30
# 李四 60
# 王五 40
# awk '{sum+=$2} END{ print sum}' calc.txt # 求和

# 筛选出长度大于3的单词
# echo 'I may not change [the] past, but I can learn from it.' | awk -F "[ ,.]" '{for(i=1;i3) {print $i}}}'

# 单词及字母去重排序: 先对文本进行切割, 遍历出各个单词及其出现的次数, 按照number进行逆序排序, 再取出出现靠前的两个单词及出现频率
# echo 'I may not change the past, but I can learn from it.' | awk -F "[ ,.]+" '{for(i=1;i hello_${i}.txt
#   let i++
# done
# filenames=$(ls)
# for name in $filenames; do
#   printf '执行命令前: %s' $name
#   mv $name "后${name}"
# done




# sed命令
# sed [选项参数] [模式匹配/sed程序命令] [文件名]
# 模式匹配: sed会读取每一行数据到模式空间中, 之后判断当前行是否符合模式匹配的要求, 符合要求就会执行sed程序命令, 否则不会执行sed程序命令; 如果不谢匹配模式, 那么每一行都会执行sed程序命令
# -e: 直接在指令列模式上进行sed的动作编辑, 它告诉sed将下一个参数解释为一个sed指令, 只有当命令行上给出多个sed指令时才需要加 -e 选项;
# -i: 直接怼内容进行修改, 不加 -i 则只是预览, 不会对文件做实质修改
# -f: 后跟保存了sed指令的文件
# -n: 取消默认输出, sed默认会输出所有的文本内容, 使用 -n 后只显示处理过的行
# -r: 使用正则表达式, 默认情况下, 只识别基本的正则表达式

# sed命令功能描述
# -a: add新增, a的后面可以接字符串, 在下一行出现
# -c: change修改, 更改匹配行的内容
# -d: delete删除, 删除匹配的内容
# -i: insert插入, 向匹配行前插入内容
# -p: print打印, 打印出匹配的内容, 通常与 -n 选项合用
# -s: substitute替换, 替换掉匹配的内容
# -=: 用来打印匹配行的行号
# -n: 读取下一行, 遇到n时会自动跳入下一行

sed -e '4aabc' abc.txt

你可能感兴趣的:(Shell:再学shell)