shell 脚本

字符串与基本运算

1 变量赋值=与变量取值 $
变量名直接后接等于号=表示赋值。注意等于号两边不能有空格。
直接用$跟变量名,表示取值。

# 简单字符串可以raw表示,无需加引号
a=hello
echo $a

# 带空格的字符串需要以引号包裹
a=hello world  # 报错 world: command not found
b="hello world"
echo $a
echo $b

2 命令运算$()
()表示运行括号里的命令,$表示拦截这个命令的输出给左边的变量。
命令运算等效于在terminal里直接输入command然后捕获标准输出流。

# 打印当前目录
a=$(pwd) 
echo $a  # /root

3 整数运算$(( ))
整数运算满足in和out都是整数。

  • 操作数必须是整数。
  • 结果会自动取整(floor)。
a=1
b=2
c=0.7
res1=$(( a+b ))
res2=$(( a/b ))
echo $res1  # 30
echo $res2  # 0 = floor(0.5)

# 操作数不是整数,报错
# syntax error: invalid arithmetic operator (error token is ".7")
res3=$(( b*c ))

# 还支持其他运算
res4=$(( a|b ))
res5=$(( a&b ))
res6=$(( a^b ))
res7=$(( a<<b ))
res8=$(( b>>1 ))

4 变量替换${}
和命令运算$()不同,变量替换${}不执行command,仅仅将此处替换为某个变量的字符串值。
和变量取值$不同,变量替换${}除了取值功能,还能定义默认值,以及访问数组。

name="apple"
aaa="name is: ${name}"
#在双引号环境下,等价于
aaa="name is: $name"

# 但替换操作符支持默认值
myname=""
aaa="name is: ${myname:-'dft'}"
echo $aaa

# 还支持数组顺序访问
array=(1 2 3)
aaa="name is: ${array[0]}"
echo $aaa  # name is: 1

# 感受一下为什么不用变量取值`$`
bbb="name is: $array[0]"
echo $bbb # name is: 1[0]

位置参数

举例:

sh demo.sh 123 456

$0 表示 demo.sh
$1 表示123

$0 : 在用sh 或者 ./执行脚本时,指的是脚本名,用source或.执行时,永运是bash,这也反应了sh 或者 ./执行脚本的原理和source的方式是不同的.
$1 : 第一个参数 123
$2 : 第二个参数 456
依次类推
$# : 参数的个数,不包括命令本身,上例中为2
$@ : 参数本身的列表,也不包括命令本身,如上例为[123,456]
$* : 参数本身的列表,也不包括命令本身, "$*“将所有的参数解释成一个字符串,而”$@"是一个参数数组。

注意,函数内的参数是独立的,不与整个脚本的参数关联。

命名参数

命名参数的传递通常使用getopt/getopts。

getopts只能处理短参数格式,兼容linux和mac。
getopt能处理长参数和短参数格式,mac上默认不支持。
因为mac下的getopt由BSD实现,Linux则是GNU,导致一些不通用,建议在mac上安装GNU-getopt。

getopts 是 Shell 内建命令,getopt 是一个独立外部工具。

为了保持跨系统兼容,我先使用getopts,仅支持单字母。

语法

单字母,bool参数,默认false。
单字母加冒号:,值参数。
如果命令行中包含了没有在getopts列表中的选项,会有警告信息,如果在整个getopts字符串前面也加上个:,就能消除警告信息了。

使用getopts识别出各个选项之后,操作中有两个相对固定的“常量”,一个是OPTARG,用来取当前选项的值,另外一个是OPTIND,
getopts在处理参数的时候,处理一个开关型选项,OPTIND加1,处理一个带值的选项参数,OPTIND则会加2。

#!/bin/bash
echo 初始 OPTIND: $OPTIND
  
while getopts "a:b:c" arg #选项后面的冒号表示该选项需要参数
do
    case $arg in
        a)
            echo "a's arg:$OPTARG" #参数存在$OPTARG中
            ;;
        b)
            echo "b's arg:$OPTARG"
            ;;
        c)
            echo "c's arg:$OPTARG"
            ;;
        ?)  #当有不认识的选项的时候arg为?
            echo "unkonw argument"
            exit 1
        ;;
    esac
done
  
echo 处理完参数后的 OPTIND$OPTIND
echo 移除已处理参数个数:$((OPTIND-1))
shift $((OPTIND-1))
echo 参数索引位置:$OPTIND
echo 准备处理余下的参数:
echo "Other Params: $@"

字符串运算

根据变量值,动态计算字符串。

直接相邻时,有没有引号是一样的。

$script_dir/repos.txt
"$script_dir/repos.txt"

不直接相邻时,带引号才能完成格式化拼接。

in $script_dir there is  repos.txt
"in $script_dir there is repos.txt"

如果直接跟着合法变量名字符,需要加括号

"$script_diris dir"
"${script_dir}is dir"

如果跟的不是合法变量名字符,不需要加,或者说加不加等价。

"$script_dir/aa.txt"
"${script_dir}/aa.txt"

条件判断

if [ expression ]
then
   Statement(s) to be executed if expression is true
else
   Statement(s) to be executed if expression is not true
fi

结合上面的知识,可以得到几个应用

参数判空

这个需求挺常见的吧。比如find搜索目标文件。检查搜索结果是否为空。

变量判空

if [ ! -n "$arg_dir" ] ; then
    repos_dir=$script_dir/repos
else
    repos_dir=$(readlink -f $arg_dir)
fi

注意 "$var_name"外面的双引号很重要。不加就不能正确判空。

我还不知道为什么这里必须加双引号。

数组判空

if [ -z "${array[@]}" ]; then
    echo "数组为空"
else
    echo "数组不为空"
fi

判断变量存在

test $this_val

判断文件/目录存在

-d判断目录
-f判断文件

#如果文件夹不存在,创建文件夹
if [ ! -d "myfolder" ]; then
  mkdir myfolder
fi

比较两个字符串是否相等

if [ "$test"x = "test"x ]; then
dosomethinghere
fi

这里的关键有几点:

1 使用单个等号

2 注意到等号两边各有一个空格:这是unix shell的要求

3 注意到"$test"x最后的x,这是防止出错特意安排的。因为当$test为空的时候,上面的表达式就变成了x = testx,显然是不相等的。而如果没有这个x,表达式就会报错:[: =: unary operator expected

循环读取行

function clone(){
    while read line || [[ -n ${line} ]]
    do
        echo "read $line"
    done < $1
}

clone 1.txt

注意 || 后面的条件判断,是为了防止while读最后一行内容时遇到EOF直接退出,导致最后一行内容不能进入do循环体的问题。

参照//https://www.cnblogs.com/Braveliu/p/10573389.html

或者用for

IFS=$'\n'  # 修正换行符 
function clone(){
    for line in `cat $1`
    do
        echo "read $line"
    done
}

clone 1.txt

路径计算

当前脚本的路径

script_path=$(readlink -f $0)
script_dir=$(dirname $(readlink -f $0))

修改文件内容

sed指令。

指定模式串匹配
两个冒号之间是要查找的内容
第二个冒号对之间是替换的内容

sed -i -e “s:AllowUsers.*:AllowUsers root:g” /etc/ssh/sshd_config

sed 后的指令最好单引号包裹。

sed的指令模式中 直接用数字表示行号。
例如删除第四行, sed -e '4d' test.txt
行返回用逗号分割,例如 sed -e '2,4d' test.txt 表示删除2~4行。
$ 表示文档结尾。 sed -e '2,$d' test.txt 表示从2删到结尾。

插入
sed '2i drink tea' , 第二行后 插入 drink tea

sed '2a Drink tea or \ drink beer' 反斜杠标记新行。

s指令代表替换,是最常用的。 s通常和正则表达式一起出现。

sed -e 's:{匹配表达式}:{想要替换的内容}'

shell字符串处理练习

Q:请输出 ====start{换行} hello {换行}end===

猜测下面哪个写法是对的?

echo ===\n hello \n===
echo "===\n hello \n==="
echo ===\\n hello \\n===
echo "===\\n hello \\n==="

打印结果 都不对

===n hello n===
===\n hello \n===
===\n hello \n===
===\n hello \n===

需要使用echo -e 开启转义

echo -e ===\n hello \n===
echo -e "===\n hello \n==="
echo -e ===\\n hello \\n===
echo -e "===\\n hello \\n==="
===n hello n===
# 第二个
===
 hello 
===
# 第三个
===
 hello 
===
# 第四个
===
 hello 
===

输出文本到文件

常见需求,输出命令

# 先make -p ${parent_dir}确认路径是存在的

# 输出单行,内容覆盖
echo "export PATH=/root/my/:$PATH" > /root/test/myscript.sh
# 输出单行,内容追加到文件尾
echo "export PATH=/root/my/:$PATH" >> /root/test/myscript.sh

# 输出多条命令,可以用分号隔开
echo "export PATH=/root/my/:$PATH; echo 456;" > /root/test/myscript.sh

# 输出多条命令,可以用换行符
# 注意转义字符需要-e开启
echo -e "export PATH=/root/my/:$PATH\n echo 456" > /root/test/myscript.sh

额外介绍一下多行文本重定向。
语法: <<{标识符}
标识符EOF之间的内容将被作为多行文本重定向给前面的命令

# 输出多条命令,使用多行文本重定向
cat /root/test/myscript.sh<<EOF
this is a file created by shell.
we want to make a good world.
EOF

标识符的名字可以任意更改,通常约定使用全大写。标识符前后不能有空格。

# 使用多行文本重定向,修改标识符名字
cat /root/test/myscript.sh<<END
this is a file created by shell.
we want to make a good world.
END

cat /root/test/myscript.sh<<HELLO
this is a file created by shell.
we want to make a good world.
HELLO

可以接受多行文本重定向的指令还有一些

cat /root/test/myscript.sh<<EOF
this is a file created by shell.
we want to make a good world.
EOF

grep /root/test/myscript.sh<<EOF
this is a file created by shell.
we want to make a good world.
EOF

python <<EOF
this is a file created by shell.
we want to make a good world.
EOF
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值