Linux基础-Shell脚本

四、Shell脚本

[TOC]

编写自定义脚本的时候,可以在用户的家目录下的创建一个 bin 目录,然后将自定义脚本文件加入到刚创建的 bin目录中,然后我们就可以直接在当前用户下执行脚本文件了,因为在用户的家目录下的 用户环境变量文件(.bash_profile) 中已经执行 $HOME/bin 了。所以可以直接使用!

2.shell基本运算

由于原生的bash是不支持数学运算的,所以可以使用 expr看来实现数学运算,使用基本方式:使用反引号将expr表达式包裹起来;

==注意==:条件表达式要放在方括号之间,并且要有空格,例如: [a==b] 是错误的,必须写成 [
a ==b ]。

vi a.sh

#!/bin/bash
# 定义一个变量名叫 val,要计算的两个字符之间要有空格进行分隔
val=`expr 2 + 2`
# 调用自定义变量进行输出的时候使用 $ 符号
echo "两数字之和: $val"

(1).算数运算符

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ a ==b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ a !=b ] 返回 true。

(2).关系运算符

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ a -eqb ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ a -neb ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ a -gtb ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ a -ltb ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ a -geb ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ a -leb ] 返回 true。

(3).布尔运算符

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ a -lt 20 -o \b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ a -lt 20 -a \b -gt 100 ] 返回 false。

(4).逻辑运算符

运算符 说明 举例
&& 逻辑的 AND [[ Misplaced &a -lt 100 &&b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ a -lt 100 ||b -gt 100 ]] 返回 true

(5).字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ a =b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ a !=b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n "$a" ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

3.shell流程控制

(1),if/then/elif/else/fi 结构

# 编写脚本
$ vi equals.sh
# 指定脚本解析器的类型是bash
#!/bin/bash

a=10
b=20

if [ $a != $b ]
then
  echo "$a != $b : a不等于b"
else
  echo "$a == $b : a等于b"
fi

# 2,编写完成脚本之后,需要对脚本文件赋予可执行的权限
$ chmod 755 equals.sh
# 3,执行脚本
$ sh equals.sh

(2).case/esac 结构

# 1,编写脚本内容
$ vi case.sh 

#!/bin/bash
# 打印提示信息
echo "现在是早上吗?请输入你的选择:"
# 表示需要用户输入一个变量
read yes_or_no
# 根据用户输入的变量,进行case的判断,然后in之后的每个选项指定命令最后都使用 ;; 表示结束
case "$yes_or_no" in
yes|y|Yes|YES) # 表示输入以以下选项的
  echo "Good Morning!";;
[nN]*) # 表示你输入以n开头的都可以提示你下午好
  echo "Good Afternoon!";;
*) # 输入其他字符的时候,提示输入有误
  echo "对不起,输入有误!"
  exit 1;;
# case语句的结束
esac
exit 0

# 2,编写完成脚本之后,需要对脚本文件赋予可执行的权限
$ chmod 755 case.sh
# 3,执行脚本
$ sh case.sh

(3).for/do/done 结构

1).案例需求

请使用for循环在指定目录下"/root/test"目录下创建是个文件。

test-1.sh
test-2.sh
test-3.sh
test-4.sh
test-5.sh
test-6.sh
test-7.sh
test-8.sh
test-9.sh
test-10.sh

2),shell脚本

#!/bin/sh

# 先判断当前指定的文件夹是否存在
if [ ! -d /root/forTest ]
 then mkdir -p /root/forTest
fi
# 循环1-10之间的所有数
for num in {1..10}
 # 在循环内执行以下命令
 do
  touch /root/forTest/test-${num}.sh    #因为创建文件到指定目录,所以创建时最好用全路径;
 done

(4),while/do/done 结构

1),案例需求

需求1:验证用户输入的密码,直到用户输入正确为止;

需求2:验证用户输入的密码,用户输十次还是不正确则退出;

2),shell脚本

shell脚本1

#!/bin/bash

echo "请输入密码:"
# 获取用户输入的字符
read try
while [ "$try" != "111111" ];
do
  echo "输入错误,请重新输入:"
  read try
done

shell脚本2

#!/bin/bash

echo "请输入密码:"
read try
# 定义计数器,控制变量
counter=1
# 此时的 "-a" 表示 "and","-lt" 表示 "小于"
while [ "$try" != "111111" -a "$counter" -lt 3 ];
do
  echo "输入错误,请重新输入:"
  # 其中 $(()) 中的Shell变量取值将转换成整数,另外 $[] 表示同样的含义
  counter=$(($counter+1))
  # 在循环中再次读入用户输入的变量
  read try
done
# 当循环结束之后,弹出提示信息
echo "3次机会用尽!!"

(5).break 与 continue

break跳出,continue跳过。

break[n]可以指定跳出几层循环,continue跳过本次循环步,没跳出整个循环。

(6),Shell中对字符串的处理

1),字符串的截取

格式 说明
${string: start :length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: start} 从 string 字符串的左边第 start 个字符开始截取,直到最后。
${string: 0-start :length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start} 从 string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
${string%%*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

2),获取字符串长度等操作

  • 示例代码 与 执行结果

    str="http://www.fengbohello.xin3e.com/blog/shell-truncating-string"
    echo "string : [${str}]"
    
    length=${#str}
    echo "length : [${length}]"
    
    ## 执行结果
    string : [http://www.fengbohello.xin3e.com/blog/shell-truncating-string]
    length : [61]
    

4.shell参数 和 特殊变量

$0 就是你写的shell脚本本身的名字,$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的shell脚本传的第二个参数.

其他常用特殊变量

$#  # 相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
$@  # 表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$*  # 表示参数列表"$1" "$2" ...,同上
$?  # 上一条命令的Exit Status
$$  # 当前进程号
# 编写脚本文件
$ vi special.sh

#!/bin/bash
echo "shell脚本本身的名字: $0"
echo "传给shell脚本的第一个参数: $1"
echo "传给shell脚本的第二个参数: $2"

# 赋予权限、执行脚本
$ chmod 755 special.sh
$ sh special.sh xxx01 xxx02

5.shell的文件包含

. filename   # 注意点号(.)和文件名中间有一空格
或者
source filename

6.脚本的执行

一个规范的Shell脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash的编程一般为:

#!/bin/bash

或者

#!/bin/sh

注意:

(1)在Shell中如果一行的第一个字母是#,则是注释,但是上面两个是写在第一行,所以不是脚本注释行,如果写在某个命令之后,则变成注释行。

(2)sh为bash的软链接,大多数情况下,脚本的开头使用“#!/bin/bash”和“#!/bin/sh”是没有区别的,但更规范的写法是在脚本的开头使用“#!/bin/bash”。

(1),使用"./脚本"的方式运行

# 进入脚本所在的工作目录,使用 ./脚本方式执行
./test.sh

(2),"sh 脚本"执行

# 使用脚本对应的sh或者bash来接着脚本执行
sh test.sh
bash test.sh

(3),shell环境运行

# 在当前shell环境中执行,可以使用 ". 脚本" 或者 "source 脚本"
. test.sh
source test.sh

7.定时任务(清理日志)

1.使用命令:cat /etc/crontab查看定时任务的时间格式,
2.使用命令:vi /etc/crontab编辑你的定时任务,
3.使用命令:service crond status查看当前定时任务是否启动.
4.使用命令:service crond start 来启动crond任务.
# 常见的定时任务
# 0 0 * * * sh /root/bin/demo02.sh
每分钟执行              */1 * * * *     command
每小时0分执行           0 * * * *       command
每天0点执行             0 0 * * *       command
每周日0点执行           0 0 * * 0       command
每月1号执行             0 0 1 * *       command
每年1月1日执行          0 0 1 1 *       command

[图片上传失败...(image-e6a4b8-1659579371764)]

(1),定时清理日志

1),找到对应的日志目录

## find 对应目录 -mtime +天数 -name "文件名" -exec rm -rf {} \;

find /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ -mtime +10  -name "*.log.*" -exec rm -rf {} \;

find /app/datacheck/logs/ -mtime +10  -name "*.log.*"


## 说明:
find:Linux查找命令,用户查找指定条件的文件
/home/apache-tomcat-7.0.55-13-990*/webapps/HollyProxyServer2.0/WEB-INF/logs/:需要进行清理的目标目录
-mtime:标准语句写法
+10 :数字代表天数
-name "*.log.*":目标文件的类型,带有log的所有文件
-exec:固定写法
rm -rf:强制删除包括目录在内的文件
{} \;:固定写法,一对大括号+空格+\+;

2),设置定时任务

  • 创建shell脚本

    touch  /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/bin/auto-del-log.sh
     
    chmod u+x auto-del-log.sh
    
    vi auto-del-log.sh
    #!/bin/sh
    find /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ -mtime +10  -name "*.log.*" -exec rm -rf {} \;
    
  • 编辑定时任务

    # 进入定时任务编辑页面,按i底部出现INSERT,开始进行任务脚本编辑,将auto-del-log.sh执行脚本加入到系统计划任务,到点自动执行:
    crontab e
     
    # 追加如下内容
    30 0 * * * /home/apache-tomcat-7.0.55-13-990*/webapps/ProxyServer2.0/WEB-INF/logs/ auto-del-log.sh
    设置每天凌晨0:30执行auto-del-log.sh文件进行数据清理任务。
    编辑完成按Ctrl+c,输入:quit,之后再输入:w!完成保存,ctrl+c,:quit退出。
    

(2),根据文件大小清理日志

1),编辑文件

vi xxx.sh
## 追加以下内容,判断/app/datacheck/logs/dev.log文件的大小
#!/bin/bash
if [ `/bin/ls -lt /app/datacheck/logs/dev.log | head -1 | /bin/awk '{print $5}'` -gt $((1024*1024*10)) ]
then
  echo > /app/datacheck/logs/dev.log
fi

2),编辑定时任务

crontab -e
## 追加以下内容: 每个六个小时执行一次
0 */6 * * * sh /root/bin/clean_log.sh

(3),将大的日志文件进行拆分

#!/bin/bash 
current_date=`date -d "-1 day" "+%Y%m%d"` 
split -b 65535000 -d -a 4 /home/alvin/nohup.out   /home/alvin/log/log_${current_date}_ 
cat /dev/null > nohup.out


# 1,先获取当前时间的前一天
date -d "-1 day"表示获取前一天的日期,就是说我们今天操作的话是切割昨天的日志。+%Y%m%d是具体的日期格式,也就是年月日格式,比如:20181005。
# 2,然后对日志文件进行切分
其中,65535000是60M,也就是日志文件按60M大小进行切割,可自定义大小。-d -a 4表示文件后缀是4位。我们将文件切割后要按次序进行编号,比如0000,0001,0002……这个4就代表编号的位数。
# 3,将根据指定大小拆分之后的日志输出的文件中
再之后的./log/log${current_date}就是切割后日志文件的前缀,里面就带入了当前日期。所以,最终的输出格式类似于:log_20181005_0001。
# 4,最后将原始文件中内容置空

8,循环开启端口

#!/bin/sh
Count=0;
cat ./xxx | while read line
do
    Count=`expr $Count + 1`;
    echo "Test($Count),$line...";
    firewall-cmd --add-port="${line}"/tcp --permanent;
    sleep 0.1;
done;
echo "重新加载规则:"
firewall-cmd --reload;
echo "当前已开启的端口有:"
firewall-cmd --list-port;

9,合并清理日志文件脚本

  • 生成测试文件脚本
#!/bin/sh

# 循环生成文件,因为创建文件到指定目录,所以创建时最好用全路径;
for num in {1..99}
 # 在循环内执行以下命令
 do
  touch /root/aaa/test-${num}.sh    
 done

  • 实际合并清理日志文件脚本

1,时间格式化

  • -d "day ago" 获取前一天; 不加的话就直接取当天

2,判断文件夹是否存在

3,移动文件的时候,防止出现,移动文件过多,导致报错(mv argument list too long错误)

#!/bin/sh
# 获取前一天的时候生成
TODAY_DATE=`date -d "day ago" +"%Y%m%d"`;
if [  -d /root/aaa/dir-1 ]
 then mkdir -p /root/aaa/dir-1/bak-${TODAY_DATE};
find /root/aaa/dir-1 -type f -name '*.sh' -not -path "/root/aaa/dir-1/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-1/bak-${TODAY_DATE} \;
fi

if [  -d /root/aaa/dir-2 ]
 then mkdir -p /root/aaa/dir-2/bak-${TODAY_DATE};
find /root/aaa/dir-2 -type f -name '*.sh' -not -path "/root/aaa/dir-2/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-2/bak-${TODAY_DATE} \;
fi

if [  -d /root/aaa/dir-3 ]
 then mkdir -p /root/aaa/dir-3/bak-${TODAY_DATE};
find /root/aaa/dir-3 -type f -name '*.sh' -not -path "/root/aaa/dir-3/bak-${TODAY_DATE}*"  -exec mv {} /root/aaa/dir-3/bak-${TODAY_DATE} \;
fi

# 定时任务: 每天0点之执行 crontab -e
# 0 0 * * * sh /root/bin/demo02.sh
  • 补充脚本说明
#!/bin/sh
# 获取当前时间
TODAY_DATE=`date +"%Y%m%d"`;
if [  -d /home/zazt/yuanxun/ATOSLog ]
 then mkdir -p /home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/yuanxun/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/yuanxun/ATOSLog/bak-${TODAY_DATE} \;
fi

if [  -d /home/zazt/yuanxun20002/ATOSLog ]
 then mkdir -p /home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/yuanxun20002/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/yuanxun20002/ATOSLog/bak-${TODAY_DATE} \;
fi


if [  -d /home/zazt/card/ATOSLog ]
 then mkdir -p /home/zazt/card/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/card/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/card/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/card/ATOSLog/bak-${TODAY_DATE} \;
fi


if [  -d /home/zazt/review/ATOSLog ]
 then mkdir -p /home/zazt/review/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/review/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/review/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/review/ATOSLog/bak-${TODAY_DATE} \;
fi


if [  -d /home/zazt/BackgroundCheck/ATOSLog ]
 then mkdir -p /home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE};
find /home/zazt/BackgroundCheck/ATOSLog -maxdepth 1 -type f -name '*.json' -not -path "/home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE}*"  -exec mv {} /home/zazt/BackgroundCheck/ATOSLog/bak-${TODAY_DATE} \;
fi

#!/bin/sh
# 获取当前时间
MONTH_DATE=`date +"%Y%m"`;
if [  -d /home/zazt/yuanxun/ATOSLog ]
 then mkdir -p /home/zazt/yuanxun/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/yuanxun/ATOSLog/bak-* /home/zazt/yuanxun/ATOSLog/month-bak-${MONTH_DATE};
fi

if [  -d /home/zazt/yuanxun20002/ATOSLog ]
 then mkdir -p /home/zazt/yuanxun20002/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/yuanxun20002/ATOSLog/bak-* /home/zazt/yuanxun20002/ATOSLog/month-bak-${MONTH_DATE};
fi

if [  -d /home/zazt/card/ATOSLog ]
 then mkdir -p /home/zazt/card/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/card/ATOSLog/bak-* /home/zazt/card/ATOSLog/month-bak-${MONTH_DATE};
fi

if [  -d /home/zazt/review/ATOSLog ]
 then mkdir -p /home/zazt/review/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/review/ATOSLog/bak-* /home/zazt/review/ATOSLog/month-bak-${MONTH_DATE};
fi

if [  -d /home/zazt/BackgroundCheck/ATOSLog ]
 then mkdir -p /home/zazt/BackgroundCheck/ATOSLog/month-bak-${MONTH_DATE};
 mv /home/zazt/BackgroundCheck/ATOSLog/bak-* /home/zazt/BackgroundCheck/ATOSLog/month-bak-${MONTH_DATE};
fi

  • 补充时间处理参考脚本
#!/bin/sh
 
echo "打印今天之前连续的十个时间戳"
TODAY_DATE=`date`
date_range=$(seq 10)  # 数组
#echo ${date_range[@]}
for i in ${date_range[@]}
do 
    INSTANCE_DATE=`date -d "${TODAY_DATE} $i day ago" +"%Y%m%d"`;
    echo ${INSTANCE_DATE}
done
 
echo "打印指定时间之前的十天"
date_var=20170909
for i in $(seq 20)
do 
    current=`date -d "${date_var} $i day ago" +"%Y%m%d"`
    echo ${current}
done
 
#TODAY_DATE=`date +%Y%m%d`
#INSTANCE_DATE=`date -d "${TODAY_DATE} 2 day ago"`;
#INSTANCE_DATE_NEXT=`date -d "${INSTANCE_DATE} 1 day" +"%Y%m%d"`;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容