Shell编程

Posted 苏幕遮_凌枫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shell编程相关的知识,希望对你有一定的参考价值。

shell用于解释执行用户命令,用法分为Interactive和Batch.

shell script中有多条shell命令,批量执行,同时加入编程语言中常用的变量,

流程控制语句,这样看起来写shell script就像编程,但是本质上只是一系列shell命令的集合.

常见的shell版本

  • sh linux unix系统都有
  • tcsh FreeBSD macOS x使用
  • bash linux发行版标配

查看用户对应的shell类型

[email protected]:~$ more /etc/passwd|grep mark
mark:x:1000:1000:mark,,,:/home/mark:/bin/bash
# mark用户对应的shell是bash

输入命令后,一般shell会fork开启新进程exec该命令,shell的内建命令是例外.

执行builtin命令相当于调用shell进程中的一个函数,不fork.

内建命令不fork但是有Exit Status,0表示成功,非0表示失败,状态码可以用$?读取.

常见内建命令:

export  for  while  shift if  eval [

which命令查不到程序文件所在位置的命令都是内建命令,这些命令没有man手册.

要在man手册中查这些命令man bash-builtins

执行脚本

#! /bin/bash
#在shell中#是注释标识符
#在第一行的  #! 是shebang符号  表示以./xx.sh执行时由那个文件来执行
echo hello shell
###########################编写脚本

$ chmod a+x test.sh # 添加执行权限
$ ./test.sh # 由/bin/bash执行
hello shell # echo的输出结果

执行脚本的方式

  • bash/sh test.sh
  • source test.sh 交互式执行 不fork
  • . test.sh 这个.和source一样是内建命令,同样也是Interactive执行

shell脚本一行可以有多个命令,用;分隔.

命令使用()以fork方式执行,如(cd ..;ls -l ),如果没有()会以interactive执行

变量

变量名必须=大写字母+下划线

环境变量

可以从father进程传给son进程

环境变量可以从当前shell进程传给fork出来的进程

printenv查看当前shell进程中的环境变量

本地变量

只处在与当前shell进程,set显示当前shell进程中定义的所有变量(local+env)和函数

环境变量在任何进程中可见

本地变量只在shell进程中可见

在shell中环境变量与本地变量定义及使用方法相似.

定义或赋值一个变量

VAR_NAME=value # 这里的=号两边是绝对不能有空格的

本地变量导出为环境变量

export VAR_NAME

定义一个本地变量同时导出

export PATH=/usr/local

删除变量(local+env)

unset VAR_NAME

取变量的值

${VAR_NAME}  #推荐使用这种
$VAR_NAME
# shell中的变量无需指定类型  本质上都是字符串
# 变量无需先定义后使用  对未定义的变量取值返回空字符串

代换

Wildcard

文件名代换(Globbing)

Wildcard(通配符),和正则规则是一样的

* ? [ ]

$()

命令代换,先执行命令,再将结果赋值给变量,将输出结果代换到命令中

` 或$()

$ DATE=`date`
$ echo $DATE

$ DATE=$(date) # 推荐使用这种方式
$ echo $DATE

$[]

算术代换: $(()) 或 $[]

~$ echo $[8#10+1]  # 10进制 8+1
9
~$ echo $[8#16+1]  # 16进制8+1
15

单引号与双引号

  • 单引号,保存引号中内容的字面值,包括 回车
  • 单引号内不得再出现单引号
  • 单引号必须配对
  • 双引号防止通配符扩展,允许变量扩展
  • 双引号内的变量还是变量,而不是像单引号一样是字面值
~$ DT=$(date)
~$ echo "${DT}"
2019年 01月 25日 星期五 22:07:57 CST
~$ echo ‘${DT}‘
${DT}

所有的取值操作都应该放在双引号中,like "${VAR}"

语法

条件测试

  • test
  • [

测试结果为真 exit status=0 else 1

~$ var=2
~$ test ${var} -lt 1
~$ echo $?
1
~$ test ${var} -lt 8
~$ echo $?
0
~$ test ${var} -lte 2 # 用不了
bash: test: -lte: 需要二元表达式
~$ [ ${var} -lt 2 ] # [空格参数1空格参数2空格参数3空格参数4   参数4是]
~$ echo $?
1

常用测试命令:

[ -d DIR ]              如果DIR存在并且是一个目录则为真
[ -f FILE ]             如果FILE存在且是一个普通文件则为真
[ -z STRING ]           如果STRING的长度为零则为真
[ -n STRING ]           如果STRING的长度非零则为真
[ STRING1 = STRING2 ]   如果两个字符串相同则为真
[ STRING1 != STRING2 ]  如果字符串不相同则为真
[ ARG1 OP ARG2 ]        ARG1和ARG2应该是整数或者取值为整数的变量,OP是-eq(等于)-ne(不等于)-lt(小于)-le(小于等于)-gt(大于)-ge(大于等于)之中的一个

测试条件之间的 或 与 非

带与、或、非的测试命令

[ ! EXPR ]          EXPR可以是上表中的任意一种测试条件,!表示逻辑反
[ EXPR1 -a EXPR2 ]  EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示逻辑与
[ EXPR1 -o EXPR2 ]  EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示逻辑或

if/then/elif/else/fi

#! /bin/bash
echo "Please give me a number >"
read VR # 读取用户输入 赋值给变量VR
# 变量VR的值与100比较
if [ "${VR}" -lt 100 ]; then
    echo "${VR} < 100"
else 
    echo "${VR} > 100"
fi

if [ -f /bin/bash ]
then echo "/bin/bash is a file"
else echo "/bin/bash is not a file"
fi

if :; then echo "always true";fi # : 是特殊命令 返回值为0 永远为真

echo "are you stupid?"
read Y_N
if [ "${Y_N}" = "yes" ]; then
    echo "hi,stupid."
elif [ "${Y_N}" = "no" ]; then
    echo "oh,my deer."
else
    echo "stupid is as stupid does."
    exit 1
fi
exit 0

shell中的&&和||和java中的相似

-a shell中的and -oshell中的or

使用起来和&&和||等价

但是

&&和||还有一种用法,就是由前面部分的测试结果决定后半部分的语句是否执行,相当于

&& if ...then...

|| if not ...then...

test "$(whoami)" != "root" && echo "need root privliges"; exit 1
test "$(whoami)" == "root" || echo "need root privliges"; exit 1

case/esac

Shell的case可以匹配字符串和Wildcard

每个匹配分支可以有若干条命令,末尾必须以;;结束

执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出

echo "something you like..."
read n
case "${n}" in
bee)
    echo "busy as a bee";;
dog)
    echo "日了狗了";;
cat)
    echo "找只猫来";;
*)
    echo "不懂你输入的是什么"
    exit 1;;
esac
exit 0

shell中获取命令行参数${1}取第一个参数,后面的用2,3,4,5取

case "${1}" in
start)
    echo "服务器启动成功.";;
stop)
    echo "成功杀掉服务器进程";;
restart)
    echo "服务器重启好了.";;
*)
    echo "无效的命令行参数."
    exit 1
esac
exit 0

for/do/done

oh,这不是python的for in循环么?

for name in ruhua canglaoshi xiaozhe mihane toshiyuki; do
    if [ "${name}" == "ruhua" ]; then
        echo "I do not like ${name}."
    else
        echo "I like ${name}"
    fi
done
#-------------------
$ ./xm.sh 
I do not like ruhua.
I like canglaoshi
I like xiaozhe
I like mihane
I like toshiyuki
# 列出 /home/mark下的所以file
for fn in "$(ls /home/mark)"; do
    echo "${fn}"
done
# 列出当前路径下所有以x开头的文件名
for fn in x*; do
    echo "${fn}"
done

while/do/done

c=1
while [ "${c}" -lt 3 ]; do
    echo "hi,golang!"
    c=$[${c}+1]
done

函数

函数声明

Shell函数,没有返回值和参数列表

先定义后调用,调用方式是直接使用函数名

注意函数体的左花括号‘{‘和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号‘}‘写在同一行,命令末尾必须有;号。

# 定义函数
fun(){ echo "Function foo is called";} # {空格echo...;}
foo(){
    echo "function in shell..."
    echo "$(pwd)"
}
# 调用函数
fun
foo

函数参数

Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2等变量来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2等变量。函数中可以用return命令返回,如果return后面跟一个数字则表示函数的Exit Status。

fun1(){
    echo "${0}" # 执行脚本的$0参数
    echo "${1}" # x
    echo "${2}" # y
    echo "${@}" # x,y
    return 0
}

fun1 x y
# -----------------
$ ./xm.sh 
./xm.sh
x
y
x y

练习

# 判断是否是一个目录
is_directory(){
    # 接收位置参数1
    DIR_NAME="${1}"
    # 是目录 exit status 为0 else 1
    if [ -d "${DIR_NAME}" ]; then
        return 0
    else
        return 1
    fi
}

# 根据命令行参数创建目录
for DIR in "${@}"; do
    if is_directory "${DIR}"; then :  # 如果目录已存在 什么也不做 :
    else
        echo "${DIR} doest‘t exist. Creating it now."
        mkdir "${DIR}" > /dev/null 2>&1 # 创建目录 标准输出 标准错误都重定向到/dev/null 不显示
        if [ $? -ne 0 ]; then # 如果创建目录失败
            echo "Cannot create directory ${DIR}"
            exit 1
        fi
    fi
done

# for dir in abc def;do
#     rm -rf ${dir}
# done

位置参数和特殊变量

$0  相当于C语言main函数的argv[0]
$1、$2...    这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
$#  相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
[email protected]  表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$*  表示参数列表"$1" "$2" ...,同上
$?  上一条命令的Exit Status
$$  当前进程号

位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。例如:

    #! /bin/sh

    echo "The program $0 is now running"
    echo "The first parameter is $1"
    echo "The second parameter is $2"
    echo "The parameter list is [email protected]"
    shift
    echo "The first parameter is $1"
    echo "The second parameter is $2"
    echo "The parameter list is [email protected]"

其他

输入输出

read V

echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显的内容后面跟一个回车换行。

文件重定向

cmd > file             把标准输出重定向到新文件中
cmd >> file            追加
cmd > file 2>&1        标准出错也重定向到1所指向的file里
cmd >> file 2>&1
cmd < file1 > file2    输入输出都定向到文件里
cmd < &fd              把文件描述符fd作为标准输入
cmd > &fd              把文件描述符fd作为标准输出
cmd < &-               关闭标准输入

tee

将命令的结果输入到标准输出的同时保存到文件

tee -a x.txt -a参数表示追加

cd /home/mark
ls -alh | tee a.txt

find

grep

awk 处理行和列

sed 处理行

以上是关于Shell编程的主要内容,如果未能解决你的问题,请参考以下文章

代码片段:Shell脚本实现重复执行和多进程

Linux bash基础特性二

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

使用 Pygments 检测代码片段的编程语言

面向面试编程代码片段之GC