Shell脚本:快速入门

常用指令

linux命令实在有点多,目前感觉比较好的学习方法是先了解和整理一下命令的分类,了解每个分类下一些常用命令功能,然后根据实际需求去查具体用法。使用-h是一种方式,不过有些可能过于抽象,可以使用一些专门的网站查询命令用法,比如:www.linuxcool.com等。

通常执行一个sh脚本只需要运行如下命令:./test.sh,这种方式往往会报没有运行权限的错误,这时候可以:1)进行授权chmod a+x ./test.sh;2)换个方式:sh ./test.sh。后者还可以通过sh +x ./test.sh来查看脚本执行过程,方便调试。

文件(夹)

指令描述示例
pwd打印当前文件路径pwd
file确定类型的文件file .
ls
ll
列出目录下的文件,ll是ls -l的别名
-a:显示指定路径中的所有文件,包括隐藏文件
-l:显示文件的详细信息,包括文件类型,权限,所属用户,所属用户组,文件大小,上一次修改时间等
-h:文件大小以KBytes为单位显示
-S:按照文件大小顺序显示,默认从大到小;若要从小到大,可使用-Sr
-r:将文件以相反次序显示(ls默认依英文字母a-z次序)
-t:以最后一次修改时间排序
-d:将目录象文件一样显示,而不是显示其下的文件,只显示当前文件夹
-R:递归列出所有目录的文件
cd改变目录cd . .#进入上级目录
dirname获取指定路径的父目录dirname $(pwd)#返回的是指定路径父目录的绝对路径
mkdir创建目录mkdir ./test
rmdir删除空目录rmdir ./test
rm删除文件(夹)rm -r ./test
chmod设置访问权限chmod a+x test.sh
chmod 777 test.sh
ln链接文件目录
cmp比较二进制文件
diff比较两个文件的差异
mv移动文件/目录mv src dst
cp复制文件/目录cp src dst
chgrp变更目录文件所属组
chown变更文件目录的所有者
dd复制文件并格式化
touch修改文件时间或新建文件
find查找文件
locate快速查找文件
which查找可执行程序文件
whereis查找执行程序的相关文件

关于文件查找命令哪家强,请看这篇:Linux的五个查找命令

文件内容

指令描述示例
nl添加行号
echo回显变量,几个常用参数:
-n:表示内容不换行,默认会换行
-e:表示激活转义字符
echo $SHELL
printf格式化输出,几个常用参数:
%s:字符串占位符
%-ns:n个字符的字符串占位符,-表示左对齐,默认右对齐
%c:字符占位符
%d:整数占位符
%f:浮点数占位符,默认小数点后6位
%-n.mf:浮点数占位符,- 表示左对齐, n 表示显示长度, m 表示小数的位数
printf "%s %d\n" "Hello World" 123
cat打印文件所有内容至控制台cat test.txt
more打印文件内容至控制台,如果内容超过阈值则部分显示,通过enter查看更多more test.txt
less逐页显示文件内容
head显示文件开头内容
tail显示文件后面内容,在查看日志时使用比较多tail -f /usr/local/nginx/log/error.log
od查看特殊格式的文件内容
grep查找文本内容,配合管道符使用的场合较多
-E:正则匹配,支持多个-e
-ve:正则匹配取反,即不包含
-a :将 binary 文件以 text 文件的方式搜寻数据
-c :计算找到 ‘搜寻字符串’ 的次数
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 ‘搜寻字符串’ 内容的那一行
--color=auto :可以将找到的关键词部分加上颜色
patch修补文件

cat配合<<EOF可以实现多行文本输入到指定文件中:

# 将多行文本添加到文件末尾,如果>>改为>就变成覆盖了
cat >> ./text.txt <<EOF
line1
line2
line3
EOF

echo转义字符:

  • \n 换行
  • \r 回车
  • \t 制表符 tab
  • \b 退格
  • \v 纵向制表符

vi/vim

操作描述示例
模式切换编辑模式
命令模式
插入模式
按o:进入编辑模式
按ESC:进入命令模式
按i:进入插入模式
文件保存:w保存文件但不退出vi 编辑
:w! 强制保存,不退出vi 编辑
:w file将修改另存到file中,不退出vi 编辑
:wq保存文件并退出vi 编辑
:wq!强制保存文件并退出vi 编辑
:q不保存文件并退出vi 编辑
:q!不保存文件并强制退出vi 编辑
:e!放弃所有修改,从上次保存文件开始在编辑

账号相关

指令描述示例
whoami查看当前用户whoami

进程管理

指令描述示例
psProcess Status,打印进程信息ps
kill杀进程kill -9 <pid>

任务调度

指令描述示例
jobs查看当前正在执行的脚本任务jobs
fb/bg切换任务到控制台前台/后台bg 1 #1为调用jobs命令时任务看到的任务序号
nohupno hangup,忽略HUP信号,让指令在当前会话结束或者当前用户退出登录后可以继续运行;并且将当前指令的输出内容添加到当前目录或者home目录的"nohup.out"文件中nohup ping baidu.com
&让当前指令在后台运行,不会阻塞当前用户交互。并且会在指令执行完成后,立即返回到前台,同时输出后台执行时的PID和指令输出的内容cat nohup.out &

来源:Linux中的nohup和& | Linux指令后台执行

ctrl+z可以挂起(暂停)当前正在执行的的任务

linux软链接

创建软链接命令: ln -s [dir1] [dir2]

  • dir1是真实的文件夹
  • dir2是dir1的软链接

软链接可以理解为,dir2是dir1的快捷方式,进入了dir2,就会自动进入dir1。

例子:

 ln -s /home/datasets/JHMDB /home/MOC_detector/data/JHMDB 

真正的JHMDB数据集保存在/home/datasets/JHMDB中,/home/MOC_detector/data/JHMDB 是 /home/datasets/JHMDB的软链接。

软链接不仅对文件夹适用,对文件也同样适用。

常用场景

环境变量

配置文件

  1. zsh:文件位于~/.zshrc
  2. Mac bash:文件位于~/.bash_profile
  3. Linux bash:文件位于~/.bashrc

进入编辑

通常需要管理员权限才能修改

通过vim修改

打开配置文件:

sudo vi ~/.bash_profile

设置变量:

# 方式1
export http_proxy=http://127.0.0.1:1087
export https_proxy=$http_proxy

# 方式2
export http_proxy=http://127.0.0.1:1087 https_proxy=http://127.0.0.1:1087
通过echo追加修改
echo  'export PS1="\[\033]2;\h:\u \w\007\033[33;1m\]\u \033[35;1m\t\033[0m \[\033[36;1m\]\w\[\033[0m\]\n\[\e[32;1m\]$ \[\e[0m\]"' >> ~/.bash_profile

使修改生效

修改完执行source才能在当前会话立即生效

# 对于bash
source ~/.bash_profile

hosts

位于/etc/hosts

系统信息

系统判断

#!/bin/bash

uNames=`uname -s`
osName=${uNames: 0: 4}
if [ "$osName" == "Darw" ] # Darwin
then
	echo "Mac OS X"
elif [ "$osName" == "Linu" ] # Linux
then
	echo "GNU/Linux"
elif [ "$osName" == "MING" ] # MINGW, windows, git-bash
then 
	echo "Windows, git-bash" 
else
	echo "unknown os"
fi

软件是否安装

思路一:使用pm软件查询指定软件是否安装
示例:使用rpm判断是否安装了flutter

#!/bin/bash
check_result=`rpm -qa | grep "flutter"`
echo "check flutter result: $check_result"

不推荐,各个操作系统pm软件不同,很难做到通用

思路二:直接运行软件,检查输出是否符合预期

#!/bin/bash
check_result=$(flutter --version | grep flutter)
echo "check flutter result: $check_result, code:$?"
if [[ $check_result =~ "flutter" ]];
then
    echo "flutter has installed."
else
    echo "flutter not installed"
fi

check_result=`git --version | grep git`
echo "check git result: $check_result, code:$?"
if [[ $check_result =~ "Git" ]];
then
    echo "git has installed."
else
    echo "git not installed"
fi

在我的设备中,没有安装flutter,安装了git,运行效果如下:

line 7: flutter: command not found
check flutter result: �� code:1
flutter not installed
check git result: git version 2.32.1 (Apple Git-133), code:0
git has installed.

多了一行报错的命令,这不是我们预期的效果,改进方法如下:

check_result=$(flutter --version 2>&1)
echo "check flutter result: $check_result, code:$?"
if [[ $check_result =~ "flutter" ]];
then
    echo "flutter has installed."
else
    echo "flutter not installed"
fi

输出效果:没有报错信息了

check flutter result: �� code:127
git has installed.

补充知识点:
0 – stdin (standard input,标准输入)
1 – stdout (standard output,标准输出)
2 – stderr (standard error,标准错误输出)
希望执行某个命令,又不希望在平面上显示结果,可以将输出重定向到/dev/null,如 :echo ‘hello' > /dev/null

简单轮询逻辑

  • 实现一个简单的轮训逻辑:
while ((1)) do
> dumpsys cpuinfo
> sleep 0.01    //默认单位为s,也支持m和h
> done

命令行技巧

  • 输入\可以换行;

编写脚本

掌握上述基本命令,我们可以通过命令行实现我们想要的各种功能,很多重复性流程工作还可以进一步编成脚本,下面介绍如何编写shell脚本。
先查看下自己系统默认的shell:

echo $SHELL

输出:
/bin/bash

查看系统支持的shell:

cat /etc/shells

输出:
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

第一个脚本

下面看一个最简单的shell脚本(test.sh):

#! /bin/bash
echo "hello world shell"

我们运行这个脚本:

bash test.sh

输出:
hello world shell

分析一下:

  • 第一行:#! /bin/bash
    指定这个脚本用什么解释器执行,就是前面我们提到过的系统支持的shell。#!是一个约定的标记,固定写法。
  • 第二行:脚本内容
    这部分就是你要实现的工作内容,本示例中仅回显一个字符串

关于授权
如果执行上述命令报错:bash ./test.sh: Permission denied,则表示用户没有脚本的执行权限,进行授权即可:chmod +x test.sh

脚本参数:

  • $0 获取当前执行脚本的文件名
  • $n 获取脚本的参数,n=1…9 10以后就要${10}
  • $# 脚本后边的总参数
  • $* 获取当前脚本的所有参数"$1 $2 $3"
  • $@ 获取当前脚本的所有参数,加了双引号"$@" “$1” “$2” “$3”
  • $? 获取上一个命令的执行状态的返回值,成功为0 不成功非0
  • $$ 获取当前执行shell脚本进程的pid
  • $! 获取上一个在后台工作的pid
  • $_ 获取在次之前执行命令或脚本的最后一个参数

符号和运算符

符合含义示例
$变量引用符,用于引用变量
$1命令参数,
$0是脚本本身的名字;
$1表示第一个参数,比如bash test.sh a b c,那么$1就是a
  • -ge 大于等于
  • -le 小于等于
  • -gt 大于
  • -lt 小于
  • -eq 等于
  • -ne 不等于
算数比较符
  • >= 大于等于
  • <= 小于等于
  • > 大于
  • < 小于
  • == 等于
  • != 不等于
算数比较符
(())一种数学计算命令,它除了可以进行最基本的加减乘除运算,
还可以进行大于、小于、等于等关系运算,以及与、或、非逻辑运算
if (( $a == $b ))
  • &&
  • `
或</li><li>!` 非

变量

申明变量

定义变量无需特定关键词,直接赋值即可:

# 示例:变量的基本使用

# 定义变量
name=hello

# 使用变量
echo $name
echo ${name}

bj=world
${#bj} #按照字符返回长度
${bj:offset} #从offset位置开始切割到结尾
${bj:offset:length} #从offset位置开始切割指定的长度

删除变量

unset name

只读变量

# 定义只读变量,只读变量不可删除
readonly age=10

# 尝试修改只读变量将会报错:age: readonly variable
# age=11

键盘输入

示例:从键盘接收输入,并赋值给变量

#!/bin/bash
# 带提示语
read -p "Please input a number:" a
# 不带提示语
read b
if (( $a == $b ))
then
    echo "a和b相等"
fi

数字运算

(1) let var=算术运算表达式
(2) ((var=算术运算表达式)) 和上面等价,最高效
(3) var=$[算术运算表达式]
(4) var=$((算术运算表达式))
(5) var=$( expr arg1 arg2 arg3 ... )  # 注意两边的空格
(6) declare -i var = 数值
(7) echo '算术表达式' | bc

数学计算要用$((…))或者$[...]括起来,记得前面要加上符号$。

a=1
b=2
# 实测这种方式也可以:sum=$[$a+$b]或者sum=$[a+b]
sum=$((a+b))
echo "$a+$b=$sum"

也可以使用expr表达式:

a=1
# 注意+号两边必须要有空格,否则会被成普通字符串
a=`expr $a + 2`
# 打印a的值,此时变为3
echo $a
  1. $((…))$[...]的区别是:
    前者内部数字运算可以使用>、<、=这样的符号,而后者要使用-lt(小于)、-gt(大于)、-le(小于或等于)、-ge(大于或等于)、-eq(等于)、-ne(不等于)。
  2. 内置RANDOM生成器取值范围:0-32767,示例: [ [ [RANDOM%50]

数组

  • 声明
# 枚举式
abi_list=(armeabi-v7a arm64-v8a x86 x86_64)

# 定义一个空数组
array=()		
array1[0]=a
array1[1]=b
array1[2]=c

# 字符串强转
string="12:34:56"
array=(${string/:/ })


# 使用key替代index来访问数组
declare -A user_info
user_info[name]=devops
user_info[age]=18
# 或者
user_info=([name]=devops [age]=18)

  • 访问元素
echo ${array[0]}	#获取第一个元素的值
echo ${array[-1]}	#获取最后一个元素的值

echo ${array[*]}	#所有元素作为一行输出
echo ${array[@]}    #所有元素作为多行输出

echo ${#array[*]}   #数组的长度

  • 遍历元素
# 不推荐
for((i=0; i<4; i++));do echo ${arr[i]}; done
# 推荐
for((i=0; i<${#arr[*]}; i++));do echo ${arr[i]}; done
# 推荐
for i in ${!arr[@]}; do echo ${arr[i]}; done
# 推荐
for item in "${arr[@]}"; do echo $item; done

  • 删除元素及数组
unset array2[2]			#删除索引数组的第三个元素
unset user_info[age]	#删除关联数组中索引为age的元素
unset array2			#删除数组

命令结果

# 获取当前目录赋值给变量,注意变量名可以是关键词,不过不推荐
pwd=`pwd`
# 获取当前目录的父目录并赋值给变量
my_dir=`dirname $(pwd)`

# 获取当前脚本路径,比如:./test.sh,注意这里并不是返回绝对路径
script_dir=$0

流程控制

if语句

if语法格式:

if condition;then # 必须
    ...
elif condition;then # 可选
    ...
else # 可选
    ...
fi   # 必须
condition

通常condition有三种写法:

  1. [ ... ];[[ ]]:通用,前者兼容性好,后者功能多(支持正则);
  2. (( ... )):仅数值逻辑表达式;
  3. test ...;:通用;

可以用condition快速测试脚本逻辑:[[ " c a s e 1 " =   ∗   e n c o d e r   c a p a b i l i t i e s [   ] ? ∗ case1" =~ ^*\ encoder\ capabilities[\ ]?* case1"=  encoder capabilities[ ]? ]];echo $?

也支持取反:[! ...]。下面以[ ... ]为例介绍不同变量类型的条件表达式写法:

  • 数值相关
条件写法
数字小于60if[ $var -lt 60];
if ((a<60));
if [[ a > 60]];
  • 字符串相关
条件写法
长度非0if[ -n $var]; `
长度为0if[ -z $var]; `
相同if[ $a = $b];
不相同if[ $a != $b]; `
模式匹配相同if [[ $a == $b ]]; `
正则匹配相同if [[ $a =~ $b ]]; `
  • 文件相关
条件写法
文件或目录是否存在if[ -e filename];if[ ! -e filename
目录是否存在if[ -d filename];
文件是否存在if[ -f filename];
是否有读权限if[ -r filename];
是否有写权限if[ -w filename];
是否有执行权限if[ -x filename];
是否是一个字符链接,通常与-d/-f配合使用if[ -d filename];
字符链接示例:ls -l /bin/sh
rwxrwxrwx 1 root root 4 Mar 16 2009 /bin/sh -> bash
  • 逻辑运算
条件写法
[ a = a -a b = b] && echo Y || echo N
[[ a==a && b==b ]] && echo Y || echo N
[ a = a -o b = b] && echo Y || echo N
[[ a==a || b==b ]] && echo Y || echo N
示例

示例1:数字比较

if [ $1 -gt 2 ];
then
    echo "greater then 2"
else
    echo "less or equals 2"
    exit
fi

示例2:字符串判断

# 逐行读取users.list文件
for name in $(more users.list)
do
if [ -n "$name"]; then
echo "name is '$name'"
else
echo "name is null"
fi
done

示例3:字符串模式/正则匹配

[[ 7.6 =~ 7.6.* ]]
echo $?
#输出0
[[ 7.6 == 7.6.* ]]
echo $?
#输出1
[[ apple == a* ]]
echo $?
#输出0
[[ 7.6 =~ 7.6 ]]
echo $?
#输出0
[[ 7.6 == 7.6 ]]
echo $?
#输出0

重要知识点:

  1. $?表示获取上一行命令的输出;
  2. 使用[[ a =~ b]]可以表达a.contains(b)的关系,非常实用;

case语句

语法格式:

case value in
pattern1)
    ...
    ;;
pattern2)
    ...
    ;;
*)
	...
	;;
esac
示例

示例1:字符串判断

case $1 in
start)
    echo "let's start"
    ;;
stop)
    echo "let's stop"
    ;;
esac

示例2:数字判断

read -p "Input a number: " n
a=$[n%2]
case $a in
1)
echo "odd number."
;;
0)
ehco "even number."
;;
*)
echo "not number."
;;
esac

for语句

  • 语法格式1:穷举式
for i in item1 item2 ... itemN
do
command
done

这种是比较常见的,我们已知所有值进行逐个遍历

  • 语法格式2:范围式
for ((i=1;i<=len;i++))
do 
command
done
  • 语法格式3:迭代式
for var in sequence
do 
command
done
示例

示例:打印给定范围的数字

# 打印从1到5的数字
for i in `seq 1 5`; do
	echo "$i"
done
# 打印从1到任意输入数字之间的数字
len=$1
for ((i=1;i<=len;i++))
do 
    echo $i
done

示例:遍历数组的方法

# 定义一个数组,要用空格不要用逗号!!!,用逗号会被当成一个元素
arr=(1 2 3 4)
# 方式一:索引方式遍历
len=${#aar[@]}
for ((i=0;i<len;i++))
do
	printf "%s\t%d\n" "$i" ${arr[i]}
done
# 方式二:迭代方式遍历
for item in ${aar[@]}
do
	echo $item
done 
# 方式三:索引序列迭代方式遍历
for i in "${!arr[@]}"
do 
    printf "%s-%s\n" "$i" "${arr[$i]}"  
done

在上述示例中,@都可以被*替换

示例:索引和迭代的结合

pos=0
array=( 20200630 20210731 20200831 )
for element in ${array[@]}
do
   end_date=$element
   start_date="${element: 0: 6}01"
   let pos++
   echo "序号: echo ${pos}, start_date: ${start_date}, end_date: ${end_date}"
done

示例:遍历单层目录,判断文件是否是sh脚本

#!/bin/bash
 
TESTCHARS=2
head="#!"
PATHNAME=$(pwd)
 
if [ "$#" -eq 1 -a -d "$1" ]
then
    PATHNAME=$1
fi
 
for file in $(ls $PATHNAME)
do
    if test -f $file ; then
    	# 获取文件头2个字符
        headchar=`head -c$TESTCHARS $file`
        # 判断是否是“#!”
        if test $headchar="$head"
        then
            echo "file $file is a script"
        else
            echo "file $file is not a script"
        fi
    else
        echo "file $file is not a script"
    fi
done
exit 0

while语句

语法格式:

while condition
do
    ...
done
示例
  • 示例:数字
i=6
while [$i -ge 1] do
echo $i
i=$[$a-1]
done
  • 示例:遍历数组
i=0  
while [ $i -lt ${#array[@]} ]  
do  
 echo ${array[$i]}  
 let i++  
done
  • 示例:打印九九乘法表
i=1
j=1
while (($i <= 9))
do
    while (($j<=$i))
    do
        let "k=i*j"
        echo -n "$i*$j=$k "
        let j++
    done
    let i++
    let j=1
    echo "" #换行
done

函数

自定义函数

#!/bin/bash
# 获取字符串长度
function strLen() {
    local str=$1
    local ret=0
    if [ -n "$str" ]; then
      ret=${#str}
    fi
    # 使用echo把结果返回
    echo "$ret"
}
# 函数一定要在调用之前定义,否则报错:command not found
result=$(strLen "haha")
echo "result=$result"

# 数组元素乘以3
function array() {
    local arr=($@)
    for ((i=0;i<$#;i++))
      do
        out[i]=$((${arr[i]}*3))
      done
    echo "${out[@]}"
}
nums=(1 2 3)
result=$(array ${nums[*]})
echo "result=$result"

字符串

正则表达式

参考资料:shell正则

cut

将文件按行进行剪切字节字符并将结果输出。
命令格式:cut [options] filename
选项:

  • -f:指定按列号裁剪,从1开始,需要通过-d来分割和定义列
  • -c:指定按字符裁剪
  • -b:指定按字节裁剪
    • 单列:-f n
    • 多列:-f n,m
    • 开始到n:-f -n
    • n到结束:-f n-
    • m到n:-f m-n
    • 取反:-f n --complement,除n列之外的其他列
  • -d:指定分隔符,默认是制表符(\t),仅支持单个字符

简单示例:

# 以空格为分隔符,输出每行的第1列
cut -d " " -f 1 test.txt

# 以空格为分隔符,输出每行的第2、3列
cut -d " " -f 2,3 test.txt

应用示例1:选取系统PATH变量值,第2个“:”开始后的所有路径

[bd@localServer ~]$ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/bd/bin
 
#### 其中的2-表示取2以及之后所有的
[bd@localServer ~]$ echo $PATH | cut -d : -f 2-
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/bd/bin

应用示例2:切割ifconfig打印的IP地址

[bd@localServer ~]$ ifconfig eth0 | grep "inet addr" | cut -d : -f 2 | cut -d " " -f 1
192.168.1.102
awk

命令格式:awk [options] ‘pattern{action}’ filename
awk的执行过程是扫描文件的每一行,如果有和模式匹配(pattern)的,就执行后面的一系列动作(acttion)。动作的花括号‘{}’并不是一定要写的,用于和pattern进行分组。
默认情况下,回车换行默认是一行的结束,$表示第几列,举个例子:
awk '{print $3}'就是输出第三列,$0表示整行输出。

选项:

  • -F:指定字段分隔符,默认是空格
  • -v:初始化绑定变量
  • -f:从文件读取awk命令,这种适合命令较长的情况

模式:

  • 正则:
  • BEGIN : 处理开始前执行的操作,可以用来初始化变量等,只会执行一次
  • END: 处理完成后执行的操作,只会执行一次

动作:

  • print

变量:
在这里插入图片描述

除此之外,还有内置变量ARGV数组,ARGV[0]为awk,ARGV[N]下标从1开始是输入的参数

简单示例:

# 输出第一行
awk ‘NR == 1{print;}’ demo.text

# 输出最后一行
awk ‘END{print;}’ demo.text

# 输出第二列
awk{print $2}’ demo.txt

# 输出第一列和第三列
awk{print $1,$3}’ demo.txt

# 输出第二列中包含字符“zhang”的行
awk$2~/^zhang/{print;}’ demo.text

# 输出第二列不包含“yong”的行
awk$2!~/^yong/{print;}’ demo.text

# 输出第二列不包含字符“yong”并且第三列大于70的列
awk$2!~/^yong/ && $3>70{print;}’ demo.text

# 输出第二列不包含字符“yong”并且第三列大于70的列,并且处理开始前和处理完成后输出信息
awk ‘BEGIN{print “start”}$2!~/^yong/ && $3>70{print;}END{print “end”}’ demo.text

# 指定分隔符为 :,并且分别打印第一列,第二列和第三列
awk -F “:” ‘{print $1,$2,$3}’ demo1.txt

进阶用法:

# 支持一维数组、二维数组的创建
awk 'BEGIN {
sites["runoob"]="www.runoob.com";
sites["google"]="www.google.com"
delete sites["google"];
print fruits["google"]
}'

# 使用外部变量
awk -v n=1 -v m=2 'BEGIN{print n, m}'

更多awk使用示例

sed

参考文章:https://blog.csdn.net/l675655077/article/details/88808133

sed [选项] [动作] [inputfile]

  • 选项与参数:
    -n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
    -e :直接在命令列模式上进行 sed 的动作编辑;
    -f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
    -r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
    -i :直接修改读取的文件内容,而不是输出到终端。

  • function:
    a :后插行, a 的后面可以是字串,而这些字串会在新的一行出现(目前的下一行)
    c :取代行, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行
    d :删除行,因为是删除,所以 d 后面通常不接任何参数,直接删除地址表示的行;
    i :前插行, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
    p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行
    s :替换,可以直接进行替换的工作,通常这个 s 的动作可以搭配正规表示法,例如 1,20s/old/new/g 一般是替换符合条件的字符串而不是整行

一般function的前面会有一个地址的限制,例如 [地址]function,表示我们的动作要操作的行。

提示:获取行号可以用grep -n xxx

示例:

# 行号筛选
sed '1d' test.xx  # 删除第1行
sed '$d' test.xx  # 删除最后一行
sed '1,2d' test.xx  # 删除第1、2两行
sed '3,$d' test.xx  # 删除第3行及之后的所有行


# 正则筛选/regex/
sed '/2/d' test.txt  # 删除所有包含2的行
sed '/^2/d' test.txt  # 删除所有2开头的行

# 插入行
sed '1a hello world' test.txt  # 在第1行后插入一行,内容为"hello world"
sed '1a \    hello world' test.txt  # 在第1行后插入一行,内容为"    hello world"
sed '1i hello world' test.txt  # 在第1行前插入一行,内容为"hello world"

# 整行替换
sed '1c hello world' test.txt   # 替换第1行内容为"hello world"

# 行内部分替换
sed 's/aa/AA/' test.txt  # 替换行内第一个aa为AA
sed 's/aa/AA/g' test.txt # 替换行内所有aa为AA
sed '1s/aa/AA/g' test.txt  # 替换第1行行内所有aa为AA
sed '5,$s/aa/AA/g' test.txt # 替换第5行及之后所有行的行内所有aa为AA
sed '/^[0-9]/s/aa/AA/g' test.txt # 替换所有数字开头行的行内所有aa为AA

# 函数分隔符自定义
echo 'aabbccaadd' | sed s#aa#AA#g  # 其中#作用相当于/

# 仅筛选无动作
sed -n '2p' test.txt   #p表示仅输出满足筛选条件的行,加-n是防止全文输出(默认会全文输出)

# 将修改保存到源文件中
sed -i '2d' test.txt  # 命令运行之后发现test.txt的第2行没有了
  • sed正则支持
    在这里插入图片描述
expr

整数计算:

注意:数字的左右必须要有空格
expr 2 + 2
expr 2 \* 2
expr 2 / 2

i=5
i=`expr $i + 4`
echo $i

字符串提取:

#!/bin/bash
if expr "$1" : ".*\.pub"* &>/dev/null
then
   echo  " you are using $1"
else
   echo "you .gile" 
fi

计算字符的长度(Mac不支持):

expr length "$char"

日期和时间

相关命令
# 2022年 6月20日 星期一 00时05分17秒 CST
date
sleep 3s
统计脚本耗时

用 date 相减:

#!/bin/bash
startTime=`date +%Y%m%d-%H:%M:%S`
startTime_s=`date +%s`
#-------------------------------
echo "code body"
#-------------------------------
endTime=`date +%Y%m%d-%H:%M:%S`
endTime_s=`date +%s`
sumTime=$[ $endTime_s - $startTime_s ]
echo "$startTime ---> $endTime" "Total:$sumTime seconds"

用 time 工具

time sh xxx.sh
# 会返回3个时间数据
# real 该命令的总耗时, 包括user和sys及io等待, 时间片切换等待等等
# user 该命令在用户模式下的CPU耗时,也就是内核外的CPU耗时,不含IO等待这些时间
# sys  该命令在内核中的CPU耗时,不含IO,时间片切换耗时.
时间和时间戳转换

来源:Shell 时间与时间戳相换(Mac)

  • 时间戳转时间 timestamp2date.sh
#!/bin/sh

function usage(){
    echo "-h --help \n" \
         "  将10/13位时间戳转换为本地时间 \n"\
         "  参数:时间戳,支持10/13位两种 \n"\
         "  默认值:当前时间向后5min \n"\
         "  e.g. 1483430400(10位秒时间戳),1483430400000(13位毫秒时间戳) \n"
    exit 1
}

###
os_platform=`uname -s`
if [[ $# -le 0 ]]; then
    echo "默认按照当前时间向后5min取值"
    if [[ "${os_platform}" = "Darwin" ]];then
        echo `date -v+5M +"%Y-%m-%d %H:%M:%S"`
    elif [[ "${os_platform}" = "Linux" ]];then
        echo `date -d +5min +"%Y-%m-%d %H:%M:%S"`
    fi
else
    case $1 in
      -h|--help)
          usage
      ;;
      *)
          timestampStr=${1}
          length=`echo ${#timestampStr}`
          if [[ ${length} -ne 10 ]] && [[ ${length} -ne 13 ]];then
              echo "请输入10/13位数字时间戳"
              exit 1
          elif [[ ${length} -eq 13 ]];then
              timestampStr=${timestampStr:0:10}
          fi
          echo "时间戳位:${timestampStr}"
      if [[ "${os_platform}" = "Darwin" ]];then
              dateStr=`date -r${timestampStr} +"%Y-%m-%d %H:%M:%S"`
          elif [[ "${os_platform}" = "Linux" ]];then
              dateStr=`date -d @${timestampStr} +"%Y-%m-%d %H:%M:%S"`
          fi
          echo "${1}对应的本地时间为${dateStr}"
      ;;
    esac
fi

命令行执行

sh timestamp2date.sh 1507704300000
时间戳位:1507704300
1507704300000对应的本地时间为2017-10-11 14:45:00
  • 时间转时间戳 date2timestamp.sh
#!/bin/sh

function usage(){
    echo "-h --help \n" \
         "  将本地时间转换为13位时间戳(毫秒时间戳) \n"\
         "  只有1个参数:本地时间,参数格式:'%Y-%m-%d %H:%M:%S' \n"\
         "  默认值:当前时间向后5min \n"\
         "  e.g. 2017-01-01 16:00:00 \n"
    exit 1
}


##时间采用正则匹配
time_pattern="^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}"
os_platform=`uname -s`

if [[ $# -le 0 ]]; then
    echo "默认按照当前时间向后5min取值"
    if [[ "${os_platform}" = "Darwin" ]];then
        echo `date -v+5M +%s`000
    elif [[ "${os_platform}" = "Linux" ]];then
        echo `date -d +5min +%s`000
    fi
else
    case $1 in
      -h|--help)
          usage
      ;;
      *)
          dateStr=${1}
          echo ${dateStr}
          if [[ "${dateStr}" =~ ${time_pattern} ]];then
              if [[ "${os_platform}" = "Darwin" ]];then
                  echo `date -j -f "%Y-%m-%d %H:%M:%S" "${dateStr}" +%s`000
              elif [[ "${os_platform}" = "Linux" ]];then
                  echo `date -d "${dateStr}" +%s`000
              fi
          else
              echo "时间格式不正确,请按照'%Y-%m-%d %H:%M:%S'格式输入,如'2017-01-01 16:00:00' "
          fi
      ;;
    esac
fi

命令行执行

sh date2timestamp.sh '2017-10-13 14:38:30'
2017-10-13 14:38:30
1507876710000

其他

数字序列

seq 1 5:生成1到5的数字序列

脚本调用

脚本入参

shell脚本与batch脚本一样支持调用传参,与bat等其他脚本语言类似,shell预定义了一些参数索引:

  • $0:脚本本身
  • $1:第一个参数
  • $2:第二个参数,以此类推
  • $?:上个命令的退出状态(正常退出为0),或函数的返回值
  • $$#:传参个数

下面以一个简单的脚本为例,演示脚本的调用。

有如下脚本adduser.sh,用于添加系统用户

#! /bin/bash
# Usage: ./adduser.sh username [password]

# 参数校验:如果参数数量等于0则终止脚本,返回错误码1
[ $# -eq 0 ] && echo "At least one parameter is required!" && exit 1
# 参数校验:如果用户已存在则终止脚本,返回错误码2
id $1 >& /dev/null && echo "User '$1' already exist!" && exit 2
# 创建用户,如果没有传密码则默认使用用户名作为密码
useradd $1 && if test $# -eq 1; then echo $1; else echo $2; fi | passwd --stdin $1 &> /dev/null && echo "Add user $1 success" && exit 0
# 在useradd 或者 passwd时出现了异常,很有可能是权限不足所导致
echo "Something above goes wrong." && exit 3

脚本非常干练,这里进行一下解释:

  • $#表示参数数量;
  • [ arg... ]是条件表达式,所以[ $# -eq 0 ]是判断是否传参(数量等于0表示未传参);
  • id指令查询需要创建的用户是否存在,如果用户存在,表达式id $1的返回值($?)为0;
  • >& /dev/null可以隐藏表达式id $1的输出信息(重定向到空设备),batch中也有相应的操作:pause >null
  • if test $# -eq 1; then echo $1; else echo $2; fi判断用户是否设置了第二个参数,如果没有参数二,使用用户名作为密码,这里使用了管道“|”将表达式结果流转给passwd指令;
  • useradd username passwd password添加系统用户的命令,成功则返回0;

脚本选项

现有以下脚本test.sh

#!/bin/sh
for option
do
    echo $option
done

然后我们调用该脚本:./test.sh,输出:

-a
a
-b
b

看一个更完整的示例:

for option
do 
	case $option in
	-help | --help | -h)
		want_help=yes
		;;
	-exec-prefix=* | --exec-prefix=*)
	    EPREFIX=`expr "x$option" : "x-*exec-prefix=\(.*\)"`
	    ;;
	-*)
		{ echo "error: unrecognized option: $option Try \`$0 --help' for more information." >&2
      { (exit 1); exit 1; }; 
      }
      ;; 
	esac
done

扩展阅读

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Shell脚本是一种通过编写一系列命令来实现自动化任务的脚本语言。它是一种在Unix或类Unix操作系统中常见的解释性编程语言。 Shell脚本是一种非常实用的技术,可以用于各种任务,例如批量处理文件、管理系统配置和自动化任务等。 学习Shell脚本技术的入门步骤如下: 1. 学习Shell语法:Shell脚本使用的是Shell语言的语法,因此需要先掌握Shell语法规则。可以通过在线教程或书籍学习Shell语法,理解脚本的结构、变量、条件判断、循环等基本概念。 2. 选择合适的Shell:常见的Shell有Bash(Bourne Again SHell)、Csh(C SHell)、Ksh(Korn SHell)等。Bash是最常用的Shell,适合大多数任务。选择一个Shell并深入学习它的功能和用法。 3. 编写简单的脚本:从简单的任务开始,例如创建一个Hello World脚本,尝试运行并查看结果。逐步增加复杂性,编写一些实用的脚本。可以使用编辑器,例如Vi或Nano,在终端中编写脚本。 4. 理解常用命令:Shell脚本是通过命令来完成任务的,因此需要熟悉一些常用的命令,例如ls、cd、grep、awk、sed等。可以通过查阅Shell命令文档或在线教程来学习这些常用命令的用法。 5. 学习脚本编程技巧:掌握一些高级的脚本编程技巧,例如重定向输入输出、处理命令行参数、使用函数和循环等。这些技巧可以增强脚本的功能和灵活性。 总之,学习Shell脚本技术需要掌握Shell语法,选择合适的Shell,并通过编写简单的脚本、学习常用命令和掌握脚本编程技巧来逐步提高技能。随着经验的积累,可以处理更加复杂的任务,并更高效地管理和自动化系统。 ### 回答2: shell脚本技术是一种编程语言技术,用于操作和自动化Linux和Unix操作系统下的命令行界面。它可以帮助用户通过一系列的命令组合和逻辑控制实现复杂的任务和操作。 要使用shell脚本技术,首先要了解基本的shell语法和命令。shell脚本是由一系列的命令和控制结构组成的文本文件。常见的shell是Bash,可以在终端中输入"bash"命令来启动。 在脚本中,我们可以使用变量来存储数据和结果。变量可以通过赋值来创建,例如: ``` name="John" ``` 然后可以通过"$"符号进行引用,例如: ``` echo "My name is $name" ``` 除了变量,我们还可以使用条件语句和循环结构来控制程序的执行流程。条件语句可以根据条件的真假执行不同的操作,例如: ``` if [ $age -ge 18 ]; then echo "You are an adult" else echo "You are a minor" fi ``` 循环结构可以用来重复执行一段代码,例如: ``` for i in {1..5}; do echo "Number: $i" done ``` 另外,shell脚本还支持函数的定义和调用。函数是一段可以被重复使用的代码块,例如: ``` function greet { echo "Hello, $1!" } greet "Alice" ``` shell脚本技术可以用于实现很多自动化任务,比如文件操作、系统管理、数据处理等。通过编写简单的脚本,我们可以减少重复的工作和简化复杂的操作流程。 总之,掌握shell脚本技术可以帮助我们更好地管理和操作Linux和Unix系统,提高工作效率。希望这个简单的入门介绍能给您一些帮助。 ### 回答3: Shell脚本是一种编程语言,通常用于自动化执行系统任务和命令。它是在操作系统上运行的命令行解释器,可以直接与操作系统交互。下面是Shell脚本技术的入门指南。 首先,了解Shell脚本的基本语法和结构非常重要。Shell脚本使用一系列命令来执行任务,可以通过编写一系列的命令来实现特定目的。可以使用任何文本编辑器编写Shell脚本,并将其保存为以.sh为扩展名的文件。 熟悉Shell脚本的变量和参数也很重要。Shell脚本中可以使用变量来存储和处理数据。变量使用$符号进行引用,例如$变量名。同时,还可以使用特殊变量(如$0表示脚本名称,$1表示第一个参数)来获取脚本的参数。 掌握条件判断和循环结构也是必不可少的。Shell脚本可以使用if语句来进行条件判断,根据条件的结果执行不同的命令。同时,可以使用for循环和while循环进行重复执行一系列的命令。 了解Shell脚本中的输入和输出是非常重要的。可以使用特定命令来获取用户的输入,如read命令。同时,可以使用echo命令将数据输出到屏幕上。还可以使用重定向和管道来重定向命令的输出或将多个命令连接起来。 熟悉Shell脚本中的函数和文件操作也是必要的。可以使用函数来组织和重复使用特定命令序列。同时,可以使用命令来对文件进行读取、写入和操作。 最后,要实践并练习Shell脚本技术。可以编写一些简单的脚本来完成常见的系统任务,如文件处理、文本处理、系统管理等。通过不断地实践和尝试,逐渐提高对Shell脚本的理解和掌握。 总之,通过学习Shell脚本的基本语法、变量和参数、条件判断和循环结构、输入和输出、函数和文件操作,并进行实践和练习,可以逐步掌握Shell脚本技术,从而能够更好地利用它来简化和自动化系统任务与操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值