title: shell
date: 2020-09-08 15:07:04
categories:
- [学习, shell]
tags: - shell
介绍
{% note info %}
Shell 教程,主要基于bash基本等同于sh
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
本文基本上是个简要教程,复杂的请参阅网上教程。
<span style="color:red;">简单手册</span> <span style="color:red;">Shell脚本编程30分钟入门</span>
{% endnote %}
基本使用
#!/bin/bash 指定脚本解释器,这里是用/bin/bash做解释器的
echo "hello world of shell" # 打印文本
语法简介
变量
# 单行注释
:<<EOF
这是多行注释
可以多行
不过我看采用的人不多
EOF
myvar="test" # 定义变量
readonly myvar # 设置变量可读
echo "myvar is ${myvar}" # 打印变量
unset myvar # unset 命令不能删除只读变量。
echo "myvar is ${myvar}" # 打印变量,变量还和原来一样
your_name='cxl' # 单引号原样
str="Hello, I know you are \"$your_name\"! \n" # 双引号里面允许变量,可以转义
echo -e $str # 打印变量,-e 启用反斜杠转义的解释
echo 'hello'${your_name}' !' # 变量可以加大括号确认范围
echo ${#your_name} # 打印字符串长度
echo ${your_name:1:2} # 截取部分字符串
arr=(1 2 3 4 5) # 数组声明
echo ${arr[0]} # 根据下标打印数组
echo ${arr[@]} # @符号打印所有数组元素
length=${#arr[@]} # 取得数组长度
echo ${length} # 打印数组长度
for ((i=0;i<$length;i++)); do # 遍历打印数组
echo "数组$i值:${arr[$i]}"
done
for var in ${arr[@]}; do # 遍历数组
echo "数组值:$var"
done
传递参数
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
echo "Shell 传递参数实例!";
echo "参数总数:$#"
echo "当前进程id号:$$"
echo "所有参数,相当于多个字符串:$@"
echo "所有参数,相当于一个字符串:$*"
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
# 和$@相同,但"$*" 和 "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组
./helloshell.sh 1 2 3
Shell 传递参数实例!
参数总数:3
当前进程id号:1633
所有参数,相当于多个字符串:1 2 3
所有参数,相当于一个字符串:1 2 3
执行的文件名:./helloshell.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3
# getopts
#!/bin/bash
while getopts "a:bc" arg #选项后面的冒号表示该选项需要参数
do
case $arg in
a)
echo "a's arg:$optarg" #参数存在$optarg中
b)
echo "b"
c)
echo "c"
?) #当有不认识的选项的时候arg为?
echo "unkonw argument"
exit 1
esac
done
# getopt
要点:
1. getopt 用法
语法:getopt [options] [--] optstring parameters
例如:getopt ab:cd -a -b he free cat
输出:-a -b he -- free cat
getopt 根据 ab:cd 将选项和参数 -a -b he free cat 解析为如下格式:
-a -b he -- free cat
其中 -- 将选项与非选项参数分开 free 和 cat 就时非选项参数
2. set --
-- Do not change any of the flags; useful in setting $1 to -.
set -- 主要是影响特殊变量$1 $2 等,其实在上面的脚本中就是将$1 $2 等参数变量重新组合
例如:
set -- a b c
shell中的特殊位置变量$1 为a $2 为 b $3 为 c
3.如上脚本为build.sh 用法如下:
./build.sh -a -c -d -e dog
./build.sh -acde dog
上面两个命令执行结果相同。
运算符
val=`expr 2 + 2` # 2+2不对中间必须有空格,写成2 + 2
echo "2+2=$val"
val=`expr 2 / 2`
echo "2/2=$val"
val=`expr 2 - 2`
echo "2-2=$val"
val=`expr 2 \* 2` # 乘号*必须转义
echo "2*2=$val"
val=`expr 2 % 2`
echo "2%2=$val"
a=10
b=20
if [ $a == $b]; then
echo "a 等于 b"
fi
if [ $a != $b]; then
echo "a 不等于 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]; then
echo "a 小于 100 且 b 大于 15,-a表示且,也可以使用&&"
fi
if [ $a -lt 15 -o $b -lt 15 ]; then
echo "a 小于 15 或 b 小于 15,-o表示或,也可以使用||"
fi
if [ ! false ]; then
echo "非运算为true"
fi
str="welcome to shell"
if [ "$str" = "welcome to shell" ]; then
echo "=号测试成功,==号和=号在[]中表示判断(字符串比较)时是等价的,!=是测试字符串不等于"
fi
if [ -z "$str1" ]; then
echo "str1字符串不存在,所以使用-z返回true,最好用双引号括起来"
fi
if [ -n "$str" ]; then
echo "str字符串已存在,长度大于0,-n返回true,最好用双引号括起来"
fi
if [ "$str" ]; then
echo "使用$来检查字符串是否为空,如果字符串里面有空格必须用双引号括起来"
fi
# 文件测试运算符
[ -b /dev/sda ] && echo "is Block Device Driver 块设备文件"
[ -c /dev/tty7 ] && echo "is Character Device Drive 字符设备文件"
[ -d /dev/ ] && echo "is directory 目录"
[ -f /etc/nginx/nginx.conf ] && echo "is file 文件"
[ -g /etc/passwd ] && echo "is SUID/SGID set group id"
[ -k /etc/passwd ] && echo "is Sticky Bit"
[ -p /etc/passwd ] && echo "is 有名管道"
[ -u /etc/passwd ] && echo "is SUID 位 set user id"
[ -r /etc/passwd ] && echo "is 可读"
[ -w /etc/passwd ] && echo "is 可写"
[ -x /etc/passwd ] && echo "is 可执行"
[ -s /etc/passwd ] && echo "is 不为空"
[ -e /etc/passwd ] && echo "is 文件或目录已存在"
[ -L /etc/passwd ] && echo "is 符号链接"
[ -S /etc/passwd ] && echo "is socket"
命令实例
#!/bin/sh
read name
echo "$name It is a test"
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
echo `date`
printf "Hello, Shell\n"
# %s %c %d %f都是格式替代符
# %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
# %-4.2f 指格式化为小数,其中.2指保留2位小数。
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
printf "%s\n" abc def
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"
test 和 [] 等效
if test -e ./notFile -o -e ./bash
then
echo "至少存在一个文件"
else
echo "两个文件都不存在"
fi
if [ -e ./notFile -o -e ./bash ]
then
echo "至少存在一个文件"
else
echo "两个文件都不存在"
fi
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
int=1
while(( $int<=5 ))
do
echo $int
let "int++" # let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量
done
# 无限循环
while :
do
command
done
while true
do
command
done
for (( ; ; ))
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
break
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
continue # 永远不会结束
break # 保留break跳出循环
;;
esac
done
# 函数
function demoFun1(){
echo "这是我的第一个 shell 函数!"
return `expr 1 + 1`
}
demoFun1
echo $?
init(){ :; } # 空函数
输入/输出重定向
wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
dddddd
EOF
# 0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
# 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写
command > file 2>&1 # 替换
command >> file 2>&1 # 追加
command > /dev/null 2>&1 # 直接丢弃结果
文件包含
. filename # 注意点号(.)和文件名中间有一空格
source filename
linux 命令
awk
- 遍历文件相加
echo -e "1\n2\n3\n4" > test.txt
awk '{ sum += $1 }; END { print sum }' test.txt
10
- 按:分割,打印所有用户名
awk -F: '{ print $1 }' /etc/passwd
- 读取配置文件,并输出到变量里
echo -e "DB_PWD=123456\nDB_USER=root" > test.txt
cat test.txt |sed "s#//# #g" | sed "s/=/ /g" | awk '{if(NF>1){printf("%s=\"%s\";",$1,$2)}}'
DB_PWD="123456";DB_USER="root";
eval $(cat test.txt |sed "s#//# #g" | sed "s/=/ /g" | awk '{if(NF>1){printf("%s=\"%s\";",$1,$2)}}')
echo $DB_PWD
123456
- 分析url
param=`echo "$redirectUrl" | awk -F '?' '{print $2}'`;
echo "$param" | awk -F '&' '{i=1;while(i<=NF){n=split($i,array,"=");if(array[1]=="code"){print array[2];break;};i++;}}'
- 分析php配置文件,找出session节host配置项
awk -F '=>' '{if($0~/\047session\047/){find=1;fl=0;fr=0}if(find){if($0~/\[/){fl++;};if($0~/\]/){fr++;};if($1~/\047host\047/){print $2;exit 0;}if(fl==fr){exit 99}}}' ../application/config.php | awk -F '\047' '{print $2,exit 0}'
'127.0.0.1',
curl
- 静默方式-s,-k不检查证书 -c 创建cookie文件
result=`curl -s -k -c cxl_cookie https://10.0.0.42:1066`
echo $result
{"status":0,"info":"调用接口成功","data":"home"}
- 通过使用 -v 和 -trace获取更多的链接信息
curl -v -k -c "" -d "" -H "" https://10.0.0.42:1066
- 打印返回的header(-i)
curl -i -k -c "" -d "" -H "" https://10.0.0.42:1066
cut
- 按字节剪切
echo "test" | cut --b 2-4
est
- 按字符剪切
echo "test" | cut -c 2-4
est
- 多个分段用逗号分开
echo "test test"|cut -b 3-5,8
st s
- 从文件获取
echo -e "星期一\n星期二\n星期三" > test.txt
cut -b 1-3 test.txt
星
星
星
- 中文
echo "星期四 星期三" | cut -c 1-3
星
- 按特殊字符分割
echo "root:test:ok" | cut -d : -f 1
root
- 综合应用
echo "root:test:ok" | cut -d : -f 3 | cut -c 1
o
sed
- 替换字符
echo "this ReplaceWord success" | sed "s/ReplaceWord/is/"
this is success
- 替换文件
echo "this ReplaceWord success" > test.txt
cat test.txt
sed -i "s/ReplaceWord/is/" test.txt
cat test.txt
- 使用正则
echo "this 1234 success" | sed -r "s/[0-9]+/is/"
this is success
- -g表示所有匹配的都执行
echo "this 1234 success 1234" | sed -r "s/[0-9]+/is/g"
this is success is
- 按行号删除文件内容
sed -i '1,50000d' "$cxl_log_file" # 删除50000万行
sed -i '1d' a.txt删首行
sed -i '$d' b.txt删尾行
sed -i 's/[ ]*//g' c.txt删空格
sed -i '/^$/d' d.txt删空行
sed -i ‘/^[0-9]*$/d' a.txt删包含数字的行
sed -i ‘1,2d’a.txt删2行
sed -i ‘/love/d’ a.txt删包含string的行
- 修改指定行内容
sed -r 's/( 'host' *)./1"test"/' ../application/config.php
tr
# 转换为大写
echo "hello world" | tr [:lower:] [:upper:]
# 删除空格、数字和-号
echo "hello-123-world empty" | tr -d '[:blank:][:digit:]-'
# 删除除空格、数字和-号以外的字符,和上一例子正相反
echo "hello - 123-world empty" | tr -d -c '[:blank:][:digit:]-'
# 去掉连续重复字符
echo "helloooo oooo isssso ookk" | tr -s " os"
# 删除window文件造成^M字符
cat file | tr -s "\r" "\n" > new_file
cat file | tr -d "\r" > new_file
uname
# 打印全部
uname -a
Linux 1604developer 4.4.0-89-generic #112-Ubuntu SMP Mon Jul 31 19:38:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
# 打印kernel-name
uname -s
Linux
# 打印机器网络名
uname -n
1604developer
# 打印kernel-release
uname -r
4.4.0-89-generic
# 打印kernel-version
uname -v
#112-Ubuntu SMP Mon Jul 31 19:38:41 UTC 2017
# 打印机器硬件名称
uname -m
x86_64
# 打印处理器类型
uname -p
x86_64
# 打印硬件平台
uname -i
x86_64
# 打印操作系统
uname -o
GNU/Linux
mktemp
# 生成随机文件
mktemp ttXXXX.tmp
# 生成随机目录
mktemp -d tempXXXXXX
jq
jq不是标准命令,是用来解析json字符串的命令,使用apt install jq -y 来安装
# get value
echo '{"status":0,"info":"we are success","data":{"client":{"client_id":"123456","client_secret":"567890"}}}' | jq ".status"
0
echo '{"status":0,"info":"we are success","data":{"client":{"client_id":"123456","client_secret":"567890"}}}' | jq ".info" | sed 's/"//g'
we are success
echo '{"status":0,"info":"we are success","data":{"client":{"client_id":"123456","client_secret":"567890"}}}' | jq ".data.client.client_id" | sed 's/"//g'
123456
# filter data
echo '{"status":0,"info":"we are success","data":{"client":{"client_id":"123456","client_secret":"567890"}}}' | jq '.data|.client'
{
"client_id": "123456",
"client_secret": "567890"
}
xargs
# 当你尝试用rm 删除太多的文件,你可能得到一个错误信息:/bin/rm Argument list too long. 用xargs 去避免这个问题
find ~ -name '*.log' -print0 | xargs -0 rm -f
# 获得/etc/ 下所有*.conf 结尾的文件列表,有几种不同的方法能得到相同的结果,下面的例子仅仅是示范怎么实用xargs ,在这个例子中实用 xargs将find 命令的输出传递给ls -l
find /etc -name "*.conf" | xargs ls –l
# 假如你有一个文件包含了很多你希望下载的URL, 你能够使用xargs 下载所有链接
cat url-list.txt | xargs wget –c
# 查找所有的jpg 文件,并且压缩它
find / -name *.jpg -type f -print | xargs tar -cvzf images.tar.gz
# 拷贝所有的图片文件到一个外部的硬盘驱动
ls *.jpg | xargs -n1 -i cp {} /external-hard-drive/directory
# 查找另一个目录同名文件,-n1表示每行一个参数 -i表示用{}表示输出的每行记录
ls /home/cxl/git-svn/spi/spi-php/bin/res/supervisor | xargs -n1 -i echo {}