当前位置:   article > 正文

linux之shell语法大全,超详细走一波_linux中shell的语法

linux中shell的语法
  1. 1、先查看脚本解释器
  2. [es@bigdata-senior01 ~]$ echo $SHELL
  3. /bin/bash
  4. 2、编写最简单的脚本
  5. vi test.sh
  6. #第一行的脚本声明(#!)用来告诉系统使用哪种 Shell 解释器来执行该脚本
  7. #!/bin/bash
  8. #查看当前目录,按文件大小列出文件
  9. pwd
  10. ls -lSh
  11. Shell 脚本文件的名称可以任意,但为了避免被误以为是普通文件,将.sh 后缀加上,以表示是一个脚本文件。
  12. 3、执行脚本的方式
  13. 第一种:用解释器执行,sh/bash或都可以,用subshell方式开打
  14. [es@bigdata-senior01 ~]$ bash test.sh
  15. 第二种:通过./方式直接执行,用subshell方式开打,需要有“执行权限”,通过chmod来添加
  16. [es@bigdata-senior01 ~]$ ./test.sh
  17. 第三种:用source来执行,在当前shell内去读取,变量会被设置到当前shell环境中
  18. [es@bigdata-senior01 ~]$ source test.sh
  19. source可以省略为"."
  20. [es@bigdata-senior01 ~]$ . test.sh #注意.后面有空格
  21. 注意:第二种方式如果出现权限不足的情况,那么注意看看文件有没有执行权限
  22. [es@bigdata-senior01 ~]$ ./test.sh
  23. -bash: ./test.sh: 权限不够
  24. 因为test.sh没有执行权限,我们查看一下
  25. [es@bigdata-senior01 ~]$ ll test.sh
  26. -rw------- 1 es es 167 1月 8 15:25 test.sh
  27. 没有x,没有执行权限,所以加一下就可以
  28. [es@bigdata-senior01 ~]$ chmod 700 test.sh
  29. 如果要给所有人权限
  30. [es@bigdata-senior01 ~]$ chmod 777 test.sh
  31. 4、脚本参数,例如:./test02.sh one two three four five
  32. $0 表示当前 Shell 脚本程序的名称
  33. $# 表示总共有几个参数
  34. $* 表示所有位置的参数值
  35. $? 表示shell返回值
  36. $1$2$3……则分别对应着第 N 个位置的参数值
  37. 用例
  38. [es@bigdata-senior01 ~]$ cat test02.sh
  39. #!/bin/bash
  40. echo "脚本名称:$0"
  41. echo "脚本共有参数$#个"
  42. echo "脚本参数:$*"
  43. echo "脚本第一个参数:$1"
  44. echo "脚本第三个参数:$3"
  45. [es@bigdata-senior01 ~]$ bash test02.sh one two three four five
  46. 脚本名称:test02.sh
  47. 脚本共有参数5个
  48. 脚本参数:one two three four five
  49. 脚本第一个参数:one
  50. 脚本第三个参数:three
  51. 5、条件测试,Shell 脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回其他随机数值。
  52. [ 条件表达式 ] #注意表达式两端各有一个空格
  53. 5.1 文件测试:
  54. -d 测试文件是否为目录类型
  55. -e 测试文件是否存在
  56. -f 判断是否为一般文件
  57. -r 测试当前用户是否有权限读取
  58. -w 测试当前用户是否有权限写入
  59. -x 测试当前用户是否有权限执行
  60. 用例:
  61. 判断test02.sh是否是目录,结果非0,为不符合
  62. 这个判断原则其实与程序(列入java程序)退出代码一致,退出代码为0表示正常退出,退出代码>0就是异常退出
  63. [es@bigdata-senior01 ~]$ [ -d test02.sh ]
  64. [es@bigdata-senior01 ~]$ echo $?
  65. 1
  66. 判断test02.sh是否是文件,结果为0,符合
  67. [es@bigdata-senior01 ~]$ [ -f test02.sh ]
  68. [es@bigdata-senior01 ~]$ echo $?
  69. 0
  70. 5.2 逻辑与(&&),在 Shell终端中逻辑“与”的运算符号是&&,它表示当前面的命令执行成功后才会执行它后面的命令
  71. [es@bigdata-senior01 ~]$ [ -f test02.sh ] && echo "ok"
  72. ok
  73. 5.3 逻辑或(||),表示当前面的命令执行失败后才会执行它后面的命令
  74. [es@bigdata-senior01 ~]$ [ -d test02.sh ] || echo "不是目录"
  75. 不是目录
  76. [es@bigdata-senior01 ~]$ [ $USER = 112 ] && echo "用户是es" || echo "用户不是es"
  77. 用户不是es
  78. [es@bigdata-senior01 ~]$ [ $USER = es ] && echo "用户是es" || echo "用户不是es"
  79. 用户是es
  80. 5.4 逻辑“非”(!),在 Linux系统中的运算符号是一个叹号(!),它表示把条件测试中的判断结果取相反值
  81. [es@bigdata-senior01 ~]$ [ ! $USER = es ] && echo "用户是es" || echo "用户不是es"
  82. 用户不是es
  83. 6、整数比较运算符,因为=,<,>这些符号和赋值以及重定向冲突,所以shell脚本中,整数比较使用英文简写来代替的
  84. -eq 是否等于
  85. -ne 是否不等于
  86. -gt 是否大于
  87. -lt 是否小于
  88. -le 是否等于或小于
  89. -ge 是否大于或等于
  90. 用例:
  91. 取空闲内存:
  92. [es@bigdata-senior01 ~]$ freeMem=$(free -m | grep Mem | awk '{print $4}')
  93. 或者
  94. [es@bigdata-senior01 ~]$ freeMem2=`free -m | grep Mem | awk '{print $4}'`
  95. [es@bigdata-senior01 ~]$ [ $freeMem -lt 1024 ] && echo "内存不足" || echo "内存正常"
  96. 内存正常
  97. [es@bigdata-senior01 ~]$ echo $freeMem
  98. 1552
  99. 7、字符串比较,只有等于、不等于、空等。
  100. = 比较字符串内容是否相同
  101. != 比较字符串内容是否不同
  102. -z 判断字符串内容是否为空
  103. [es@bigdata-senior01 ~]$ [ $LANG != "zh_CN.UTF-8" ] && echo "需要中文字符集" || echo "是中文字符集"
  104. 是中文字符集
  105. 8、流程控制,ifforwhilecase
  106. 8.1 if条件
  107. 语法:
  108. if 条件
  109. then 命令1
  110. else 命令2
  111. fi 结束if
  112. 语法2:
  113. if 条件
  114. then 命令1
  115. elif 条件2
  116.   then 命令2
  117. else
  118. 命令3
  119. fi 结束if
  120. 用例1:vi createHomeData.sh
  121. #!/bin/bash
  122. #在用户home目录下,如果不存在data目录就创建data目录,然后拷贝数据到该目录
  123. dir=${HOME}"/data" #字符串连接,将$HOME变量和字符串"/data"连接
  124. if [ ! -e $dir ]
  125. then
  126. echo "目录$dir不存在,创建..."
  127. mkdir -p $dir
  128. cp /opt/elasticsearch*/config/*.yml $dir
  129. else
  130. cp /opt/elasticsearch*/config/*.yml $dir
  131. fi
  132. ls -d $dir
  133. ls -l $dir
  134. 用例2:vi checkHost.sh
  135. #!/bin/bash
  136. #检查参数1所表示的主机是否在线
  137. if [ -z $1 ]
  138. then
  139. echo "请输入主机IP或名字"
  140. exit
  141. fi
  142. echo "正在连接..."
  143. ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
  144. if [ $? -eq 0 ]
  145. then
  146. echo "Host1在线"
  147. else
  148. echo "Host1不在线"
  149. fi
  150. [es@bigdata-senior01 ~]$ ./checkHost.sh www.google.com
  151. 正在连接...
  152. Host:www.google.com不在线
  153. [es@bigdata-senior01 ~]$ ./checkHost.sh 114.114.114.114
  154. 正在连接...
  155. Host:114.114.114.114在线
  156. [es@bigdata-senior01 ~]$ ./checkHost.sh
  157. 请输入主机IP或名字
  158. 检查端口是否开放
  159. #!/bin/bash
  160. if [ -z $1 ] ; then
  161. echo "请输入需要检测IP和端口文件"
  162. exit
  163. fi
  164. if [ ! -e $1 ] ; then
  165. echo "文件不存在,请重新确认"
  166. exit
  167. fi
  168. File=$1
  169. cat $File | while read host
  170. do
  171. nc -zvw5 $host &> /dev/null
  172. if [ $? -eq 0 ] ; then echo "host: $host 开放"
  173. else echo "host: $host 关闭"
  174. fi
  175. done
  176. 用例3: vi checkScores.sh
  177. #!/bin/bash
  178. #输入分数,判断分数等级
  179. read -p "输入分数0-100: " grade
  180. if [ $grade -ge 90 ] && [ $grade -le 100 ] ; then
  181. echo "grade A"
  182. elif [ $grade -ge 75 ] && [ $grade -lt 90 ] ; then
  183. echo "grade B"
  184. elif [ $grade -ge 60 ] && [ $grade -lt 75 ] ; then
  185. echo "grade C"
  186. else
  187. echo "grade D"
  188. fi
  189. 8.2 for循环
  190. 语法:
  191. for 被循环自动赋值的变量 in 取值列表
  192. do
  193. 执行各种命令
  194. done
  195. 用例1:vi createUsers.sh
  196. 先准备一个userlist.txt,里面加一下用户名称,如下:
  197. [es@bigdata-senior01 ~]$ cat userlist.txt
  198. tom
  199. amy
  200. xu.dm
  201. xu.dm.test
  202. bus
  203. dba
  204. 编辑createUsers.sh脚本
  205. #!/bin/bash
  206. # 用重定向到/dev/nul来屏蔽屏幕输出
  207. # 脚本后面需要一个文件名参数
  208. if [ -z $1 ] ; then
  209. echo "请在脚本后面输入用户文件列表"
  210. exit
  211. else
  212. if [ ! -e $1 ] ; then
  213. echo "输入的文件不存在,请检查后重新输入"
  214. exit
  215. fi
  216. fi
  217. read -p "请输入用户密码:" password
  218. users=$(cat $1) #注意等号两边不能有空格,shell语法坑死人
  219. echo $users
  220. for uname in $users
  221. do
  222. id $uname &> /dev/null
  223. if [ $? -eq 0 ] ; then
  224. echo "$uname,用户已经存在!"
  225. else
  226. useradd $uname &> /dev/null
  227. echo "$password" | passwd --stdin $uname &> /dev/null
  228. if [ $? -eq 0 ] ; then
  229. echo "$uname,创建成功"
  230. else
  231. echo "$uname,创建失败"
  232. fi
  233. fi
  234. done
  235. 执行脚本:
  236. [es@bigdata-senior01 ~]$ sudo ./createUsers.sh userlist.txt
  237. 请输入用户密码:123@abc.com
  238. tom amy xu.dm xu.dm.test bus dba
  239. tom,创建成功
  240. amy,创建成功
  241. xu.dm,用户已经存在!
  242. xu.dm.test,创建成功
  243. bus,创建成功
  244. dba,创建成功
  245. 用例2:vi checkHosts.sh,检查文件列表里的主机是否在线
  246. #!/bin/bash
  247. if [ -z $1 ] ; then
  248. echo "请输入主机列表文件"
  249. exit
  250. fi
  251. if [ ! -e $1 ] ; then
  252. echo "文件不存在,请检查后重新输入"
  253. exit
  254. fi
  255. hostlist=$(cat $1)
  256. for host in $hostlist ; do
  257. ping -c 3 -i 0.2 -W 3 $host &> /dev/null
  258. if [ $? -eq 0 ] ; then
  259. echo "hosthost 在线"
  260. else
  261. echo "hosthost 不在线或被屏蔽"
  262. fi
  263. done
  264. [es@bigdata-senior01 ~]$ ./checkHosts.sh hosts.txt
  265. host:192.168.1.2 在线
  266. host:www.baidu.com 在线
  267. host:www.google.com 不在线或被屏蔽
  268. host:www.cnblogs.cn 在线
  269. host:www.csdn.net 在线
  270. host:www.qq.com 在线
  271. 8.3 while语句
  272. 语法:
  273. while 条件测试语句
  274. do
  275. 执行命令
  276. done
  277. 用例1:vi outputNum.sh
  278. #!/bin/bash
  279. #倒序输出数字
  280. if [ -z $1 ] ; then
  281. echo "please input a integer number[1-100]"
  282. exit
  283. fi
  284. if [ $1 -gt 100 ] || [ $1 -lt 1 ] ; then
  285. echo "error num,need number between [1-100]"
  286. exit
  287. fi
  288. num=$1
  289. while [ $num -ge 1 ] ; do
  290. echo "numnum"
  291. #num-1有三种写法
  292. #let "num=$num - 1"
  293. #num=$(expr $num - 1)
  294. let num--
  295. done
  296. 用例2:
  297. #!/bin/bash
  298. #测试随机数,需要输入测试次数
  299. if [ -z $1 ] ; then
  300. echo "please input test count number"
  301. exit
  302. fi
  303. count=$1
  304. num=1
  305. while [ $count -gt 0 ]
  306. do
  307. echo "第$num次,随机数:$RANDOM"
  308. let num++
  309. let count--
  310. done
  311. 8.4、case语句
  312. case 变量值 in
  313. 模式1)
  314. 命令1;;
  315. 模式2)
  316. 命令2;;
  317. *)
  318. 其他命令;;
  319. esac #就是case反过来和if语句类似,我已经不想吐槽这是什么逻辑
  320. 模式字符串中可以使用通配符
  321. 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令列表。
  322. 各模式字符串应是唯一的,不应重复出现,并且要合理安排它们的出现顺序。
  323. 用例1:修改上面的testRandom.sh
  324. #!/bin/bash
  325. #测试随机数,需要输入测试次数
  326. if [ -z $1 ] ; then
  327. echo "please input test count number"
  328. exit
  329. fi
  330. case $1 in
  331. *[a-z]* | *[A-Z]*)
  332. echo "输入的是字母,需要输入数字"
  333. exit;;
  334. *[0-9]*)
  335. echo "输入的是数字,符合要求";;
  336. *)
  337. echo "输入非法字符无法继续"
  338. exit;;
  339. esac
  340. count=$1
  341. num=1
  342. while [ $count -gt 0 ]
  343. do
  344. echo "第$num次,随机数:$RANDOM"
  345. let num++
  346. let count--
  347. done
  348. 9、breakcontinue
  349. break:用于立即终止当前循环的执行,break命令可以使用户从循环体中退出来。
  350. 语法:break[n] ,其中,n表示要跳出几层循环,默认值为1
  351. continue:跳过循环体中在其之后的语句,会返回到本循环层的开头,进行下一次循环。
  352. –语法:continue[n],其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环,默认值为1,循环层数是由内向外编号。
  353. 假设我们定义了一个变量为:
  354. file=/dir1/dir2/dir3/my.file.txt
  355. 可以用${ }分别替换得到不同的值:
  356. ${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
  357. ${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
  358. ${file#*.}:删掉第一个 . 及其左边的字符串:file.txt
  359. ${file##*.}:删掉最后一个 . 及其左边的字符串:txt
  360. ${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3,#取父件夹路径
  361. ${file%%/*}:删掉第一个 / 及其右边的字符串:(空值)
  362. ${file%.*}:删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
  363. ${file%%.*}:删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
  364. 记忆的方法为:
  365. # 是 去掉左边(键盘上#在 $ 的左边)
  366. %是去掉右边(键盘上% 在$ 的右边)
  367. 单一符号是最小匹配;两个符号是最大匹配
  368. ${file:0:5}:提取最左边的 5 个字节:/dir1
  369. ${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
  370. 也可以对变量值里的字符串作替换:
  371. ${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt
  372. ${file//dir/path}:将全部dir 替换为 path:/path1/path2/path3/my.file.txt
  373. 利用 ${ } 还可针对不同的变数状态赋值(沒设定、空值、非空值):
  374. ${file-my.file.txt} :假如 $file 沒有设定,則使用 my.file.txt 作传回值。(空值及非空值時不作处理)
  375. ${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作处理)
  376. ${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作处理)
  377. ${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作处理)
  378. ${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。(空值及非空值時不作处理)
  379. ${file:=my.file.txt} :若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為my.file.txt 。 (非空值時不作处理)
  380. ${file?my.file.txt} :若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
  381. ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值時不作处理)
  382. ${#var} 可计算出变量值的长度:
  383. ${#file} 可得到 27 ,因为/dir1/dir2/dir3/my.file.txt 是27个字节
  384. &#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;Shell 数组
  385. bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
  386. 类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
  387. 定义数组
  388. 在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
  389. 数组名=(值1 值2 ... 值n)
  390. 例如:
  391. array_name=(value0 value1 value2 value3)
  392. 或者
  393. array_name=(
  394. value0
  395. value1
  396. value2
  397. value3
  398. )
  399. 还可以单独定义数组的各个分量:
  400. array_name[0]=value0
  401. array_name[1]=value1
  402. array_name[n]=valuen
  403. 可以不使用连续的下标,而且下标的范围没有限制。
  404. 读取数组
  405. 读取数组元素值的一般格式是:
  406. ${数组名[下标]}
  407. 例如:
  408. valuen=${array_name[n]}
  409. 使用 @ 符号可以获取数组中的所有元素,例如:
  410. echo ${array_name[@]}
  411. 获取数组的长度
  412. 获取数组长度的方法与获取字符串长度的方法相同,例如:
  413. # 取得数组元素的个数
  414. length=${#array_name[@]}
  415. # 或者
  416. length=${#array_name
  417. }
  418. # 取得数组单个元素的长度
  419. lengthn=${#array_name[n]}
  420. Shell 注释
  421. # 开头的行就是注释,会被解释器忽略。
  422. 通过每一行加一个 # 号设置多行注释,像这样:
  423. #--------------------------------------------
  424. # 这是一个注释
  425. # author:菜鸟教程
  426. # site:www.runoob.com
  427. # slogan:学的不仅是技术,更是梦想!
  428. #--------------------------------------------
  429. ##### 用户配置区 开始 #####
  430. #
  431. #
  432. # 这里可以添加脚本描述信息
  433. #
  434. #
  435. ##### 用户配置区 结束 #####
  436. 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
  437. 每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
  438. 多行注释
  439. 多行注释还可以使用以下格式:
  440. :<<EOF
  441. 注释内容...
  442. 注释内容...
  443. 注释内容...
  444. EOF
  445. EOF 也可以使用其他符号:
  446. :<<'
  447. 注释内容...
  448. 注释内容...
  449. 注释内容...
  450. '
  451. :<<!
  452. 注释内容...
  453. 注释内容...
  454. 注释内容...
  455. !
  456. &#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;函数,函数必须写在调用的前面
  457. 实例
  458. #!/bin/bash
  459. # author:菜鸟教程
  460. # url:www.runoob.com
  461. funWithReturn(){
  462. echo "这个函数会对输入的两个数字进行相加运算..."
  463. echo "输入第一个数字: "
  464. read aNum
  465. echo "输入第二个数字: "
  466. read anotherNum
  467. echo "两个数字分别为 $aNum$anotherNum !"
  468. return $(($aNum+$anotherNum))
  469. }
  470. funWithReturn
  471. echo "输入的两个数字之和为 $? !"
  472. 输出类似下面:
  473. 这个函数会对输入的两个数字进行相加运算...
  474. 输入第一个数字:
  475. 1
  476. 输入第二个数字:
  477. 2
  478. 两个数字分别为 1 和 2 !
  479. 输入的两个数字之和为 3 !
  480. 函数返回值在调用该函数后通过 $? 来获得。
  481. 注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
  482. 函数参数
  483. 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
  484. 带参数的函数示例:
  485. 实例
  486. #!/bin/bash
  487. # author:菜鸟教程
  488. # url:www.runoob.com
  489. funWithParam(){
  490. echo "第一个参数为 $1 !"
  491. echo "第二个参数为 $2 !"
  492. echo "第十个参数为 $10 !"
  493. echo "第十个参数为 ${10} !"
  494. echo "第十一个参数为 ${11} !"
  495. echo "参数总数有 $# 个!"
  496. echo "作为一个字符串输出所有参数 $* !"
  497. }
  498. funWithParam 1 2 3 4 5 6 7 8 9 34 73
  499. 输出结果:
  500. 第一个参数为 1 !
  501. 第二个参数为 2 !
  502. 第十个参数为 10 !
  503. 第十个参数为 34 !
  504. 第十一个参数为 73 !
  505. 参数总数有 11 个!
  506. 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
  507. 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
  508. 另外,还有几个特殊字符用来处理参数:
  509. 参数处理 说明
  510. $# 传递到脚本或函数的参数个数
  511. $* 以一个单字符串显示所有向脚本传递的参数
  512. $$ 脚本运行的当前进程ID号
  513. $! 后台运行的最后一个进程的ID号
  514. $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
  515. $- 显示Shell使用的当前选项,与set命令功能相同。
  516. $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
  517. &#65532;
  518. Shell 输入/输出重定向
  519. 大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回&#8203;&#8203;到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
  520. 重定向命令列表如下:
  521. 命令说明
  522. command > file将输出重定向到 file。
  523. command < file将输入重定向到 file。
  524. command >> file将输出以追加的方式重定向到 file。
  525. n > file将文件描述符为 n 的文件重定向到 file。
  526. n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
  527. n >& m将输出文件 m 和 n 合并。
  528. n <& m将输入文件 m 和 n 合并。
  529. << tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。
  530. 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  531. 输出重定向
  532. 重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:
  533. command1 > file1
  534. 上面这个命令执行command1然后将输出的内容存入file1。
  535. 注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
  536. 实例
  537. 执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
  538. $ who > users
  539. 执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
  540. 你可以使用 cat 命令查看文件内容:
  541. $ cat users
  542. _mbsetupuser console Oct 31 17:35
  543. tianqixin console Oct 31 17:35
  544. tianqixin ttys000 Dec 1 11:33
  545. 输出重定向会覆盖文件内容,请看下面的例子:
  546. $ echo "菜鸟教程:www.runoob.com" > users
  547. $ cat users
  548. 菜鸟教程:www.runoob.com
  549. $
  550. 如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
  551. $ echo "菜鸟教程:www.runoob.com" >> users
  552. $ cat users
  553. 菜鸟教程:www.runoob.com
  554. 菜鸟教程:www.runoob.com
  555. $
  556. 输入重定向
  557. 和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:
  558. command1 < file1
  559. 这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
  560. 注意:输出重定向是大于号(>),输入重定向是小于号(<)。
  561. 实例
  562. 接着以上实例,我们需要统计 users 文件的行数,执行以下命令:
  563. $ wc -l users
  564. 2 users
  565. 也可以将输入重定向到 users 文件:
  566. $ wc -l < users
  567. 2
  568. 注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
  569. command1 < infile > outfile
  570. 同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
  571. 重定向深入讲解
  572. 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
  573. 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  574. 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  575. 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
  576. 默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
  577. 如果希望 stderr 重定向到 file,可以这样写:
  578. $ command 2>file
  579. 如果希望 stderr 追加到 file 文件末尾,可以这样写:
  580. $ command 2>>file
  581. 2 表示标准错误文件(stderr)。
  582. 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
  583. $ command > file 2>&1
  584. 或者
  585. $ command >> file 2>&1
  586. 如果希望对 stdin 和 stdout 都重定向,可以这样写:
  587. $ command < file1 >file2
  588. command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
  589. Here Document
  590. Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
  591. 它的基本的形式如下:
  592. command << delimiter
  593. document
  594. delimiter
  595. 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command
  596. 注意:
  597. 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
  598. 开始的delimiter前后的空格会被忽略掉。
  599. 实例
  600. 在命令行中通过 wc -l 命令计算 Here Document 的行数:
  601. $ wc -l << EOF
  602. 欢迎来到
  603. 菜鸟教程
  604. www.runoob.com
  605. EOF
  606. 3 # 输出结果为 3 行
  607. $
  608. 我们也可以将 Here Document 用在脚本中,例如:
  609. #!/bin/bash
  610. # author:菜鸟教程
  611. # url:www.runoob.com
  612. cat << EOF
  613. 欢迎来到
  614. 菜鸟教程
  615. www.runoob.com
  616. EOF
  617. 执行以上脚本,输出结果:
  618. 欢迎来到
  619. 菜鸟教程
  620. www.runoob.com
  621. /dev/null 文件
  622. 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
  623. $ command > /dev/null
  624. /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
  625. 如果希望屏蔽 stdout 和 stderr,可以这样写:
  626. $ command > /dev/null 2>&1
  627. 注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  628. 这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
  629. Shell 文件包含
  630. 和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
  631. Shell 文件包含的语法格式如下:
  632. . filename # 注意点号(.)和文件名中间有一空格
  633. source filename
  634. 实例
  635. 创建两个 shell 脚本文件。
  636. test1.sh 代码如下:
  637. #!/bin/bash
  638. # author:菜鸟教程
  639. # url:www.runoob.com
  640. url="http://www.runoob.com"
  641. test2.sh 代码如下:
  642. #!/bin/bash
  643. # author:菜鸟教程
  644. # url:www.runoob.com
  645. #使用 . 号来引用test1.sh 文件
  646. . ./test1.sh
  647. # 或者使用以下包含文件代码
  648. # source ./test1.sh
  649. echo "菜鸟教程官网地址:$url"
  650. 接下来,我们为 test2.sh 添加可执行权限并执行:
  651. $ chmod +x test2.sh
  652. $ ./test2.sh
  653. 菜鸟教程官网地址:http://www.runoob.com
  654. 注:被包含的文件 test1.sh 不需要可执行权限。
  655. 默认 bash 里定义的变量是全局的。
  656. $ a=10; function b() { a=2; }; b; echo $a
  657. 执行结果为2
  658. 即:函数b里对a进行修改后,a的值就发生改变。
  659. 如果不想b对a的操作不影响全局的值,可以将b中的a设为局部变量。
  660. 如下所示:
  661. $ a=10; function b() { local a=2; }; b; echo $a
  662. 执行结果为10
  663. 还有一点需要注意。
  664. 在pipe之后的处理不会改变原来的值,因为新建一个进程。
  665. $ a=1; ec1、先查看脚本解释器
  666. [es@bigdata-senior01 ~]$ echo $SHELL
  667. /bin/bash
  668. 2、编写最简单的脚本
  669. vi test.sh
  670. #第一行的脚本声明(#!)用来告诉系统使用哪种 Shell 解释器来执行该脚本
  671. #!/bin/bash
  672. #查看当前目录,按文件大小列出文件
  673. pwd
  674. ls -lSh
  675. Shell 脚本文件的名称可以任意,但为了避免被误以为是普通文件,将.sh 后缀加上,以表示是一个脚本文件。
  676. 3、执行脚本的方式
  677. 第一种:用解释器执行,sh/bash或都可以,用subshell方式开打
  678. [es@bigdata-senior01 ~]$ bash test.sh
  679. 第二种:通过./方式直接执行,用subshell方式开打,需要有“执行权限”,通过chmod来添加
  680. [es@bigdata-senior01 ~]$ ./test.sh
  681. 第三种:用source来执行,在当前shell内去读取,变量会被设置到当前shell环境中
  682. [es@bigdata-senior01 ~]$ source test.sh
  683. source可以省略为"."
  684. [es@bigdata-senior01 ~]$ . test.sh #注意.后面有空格
  685. 注意:第二种方式如果出现权限不足的情况,那么注意看看文件有没有执行权限
  686. [es@bigdata-senior01 ~]$ ./test.sh
  687. -bash: ./test.sh: 权限不够
  688. 因为test.sh没有执行权限,我们查看一下
  689. [es@bigdata-senior01 ~]$ ll test.sh
  690. -rw------- 1 es es 167 1月 8 15:25 test.sh
  691. 没有x,没有执行权限,所以加一下就可以
  692. [es@bigdata-senior01 ~]$ chmod 700 test.sh
  693. 如果要给所有人权限
  694. [es@bigdata-senior01 ~]$ chmod 777 test.sh
  695. 4、脚本参数,例如:./test02.sh one two three four five
  696. $0 表示当前 Shell 脚本程序的名称
  697. $# 表示总共有几个参数
  698. $* 表示所有位置的参数值
  699. $? 表示shell返回值
  700. $1$2$3……则分别对应着第 N 个位置的参数值
  701. 用例
  702. [es@bigdata-senior01 ~]$ cat test02.sh
  703. #!/bin/bash
  704. echo "脚本名称:$0"
  705. echo "脚本共有参数$#个"
  706. echo "脚本参数:$*"
  707. echo "脚本第一个参数:$1"
  708. echo "脚本第三个参数:$3"
  709. [es@bigdata-senior01 ~]$ bash test02.sh one two three four five
  710. 脚本名称:test02.sh
  711. 脚本共有参数5个
  712. 脚本参数:one two three four five
  713. 脚本第一个参数:one
  714. 脚本第三个参数:three
  715. 5、条件测试,Shell 脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回其他随机数值。
  716. [ 条件表达式 ] #注意表达式两端各有一个空格
  717. 5.1 文件测试:
  718. -d 测试文件是否为目录类型
  719. -e 测试文件是否存在
  720. -f 判断是否为一般文件
  721. -r 测试当前用户是否有权限读取
  722. -w 测试当前用户是否有权限写入
  723. -x 测试当前用户是否有权限执行
  724. 用例:
  725. 判断test02.sh是否是目录,结果非0,为不符合
  726. 这个判断原则其实与程序(列入java程序)退出代码一致,退出代码为0表示正常退出,退出代码>0就是异常退出
  727. [es@bigdata-senior01 ~]$ [ -d test02.sh ]
  728. [es@bigdata-senior01 ~]$ echo $?
  729. 1
  730. 判断test02.sh是否是文件,结果为0,符合
  731. [es@bigdata-senior01 ~]$ [ -f test02.sh ]
  732. [es@bigdata-senior01 ~]$ echo $?
  733. 0
  734. 5.2 逻辑与(&&),在 Shell终端中逻辑“与”的运算符号是&&,它表示当前面的命令执行成功后才会执行它后面的命令
  735. [es@bigdata-senior01 ~]$ [ -f test02.sh ] && echo "ok"
  736. ok
  737. 5.3 逻辑或(||),表示当前面的命令执行失败后才会执行它后面的命令
  738. [es@bigdata-senior01 ~]$ [ -d test02.sh ] || echo "不是目录"
  739. 不是目录
  740. [es@bigdata-senior01 ~]$ [ $USER = 112 ] && echo "用户是es" || echo "用户不是es"
  741. 用户不是es
  742. [es@bigdata-senior01 ~]$ [ $USER = es ] && echo "用户是es" || echo "用户不是es"
  743. 用户是es
  744. 5.4 逻辑“非”(!),在 Linux系统中的运算符号是一个叹号(!),它表示把条件测试中的判断结果取相反值
  745. [es@bigdata-senior01 ~]$ [ ! $USER = es ] && echo "用户是es" || echo "用户不是es"
  746. 用户不是es
  747. 6、整数比较运算符,因为=,<,>这些符号和赋值以及重定向冲突,所以shell脚本中,整数比较使用英文简写来代替的
  748. -eq 是否等于
  749. -ne 是否不等于
  750. -gt 是否大于
  751. -lt 是否小于
  752. -le 是否等于或小于
  753. -ge 是否大于或等于
  754. 用例:
  755. 取空闲内存:
  756. [es@bigdata-senior01 ~]$ freeMem=$(free -m | grep Mem | awk '{print $4}')
  757. 或者
  758. [es@bigdata-senior01 ~]$ freeMem2=`free -m | grep Mem | awk '{print $4}'`
  759. [es@bigdata-senior01 ~]$ [ $freeMem -lt 1024 ] && echo "内存不足" || echo "内存正常"
  760. 内存正常
  761. [es@bigdata-senior01 ~]$ echo $freeMem
  762. 1552
  763. 7、字符串比较,只有等于、不等于、空等。
  764. = 比较字符串内容是否相同
  765. != 比较字符串内容是否不同
  766. -z 判断字符串内容是否为空
  767. [es@bigdata-senior01 ~]$ [ $LANG != "zh_CN.UTF-8" ] && echo "需要中文字符集" || echo "是中文字符集"
  768. 是中文字符集
  769. 8、流程控制,ifforwhilecase
  770. 8.1 if条件
  771. 语法:
  772. if 条件
  773. then 命令1
  774. else 命令2
  775. fi 结束if
  776. 语法2:
  777. if 条件
  778. then 命令1
  779. elif 条件2
  780.   then 命令2
  781. else
  782. 命令3
  783. fi 结束if
  784. 用例1:vi createHomeData.sh
  785. #!/bin/bash
  786. #在用户home目录下,如果不存在data目录就创建data目录,然后拷贝数据到该目录
  787. dir=${HOME}"/data" #字符串连接,将$HOME变量和字符串"/data"连接
  788. if [ ! -e $dir ]
  789. then
  790. echo "目录$dir不存在,创建..."
  791. mkdir -p $dir
  792. cp /opt/elasticsearch*/config/*.yml $dir
  793. else
  794. cp /opt/elasticsearch*/config/*.yml $dir
  795. fi
  796. ls -d $dir
  797. ls -l $dir
  798. 用例2:vi checkHost.sh
  799. #!/bin/bash
  800. #检查参数1所表示的主机是否在线
  801. if [ -z $1 ]
  802. then
  803. echo "请输入主机IP或名字"
  804. exit
  805. fi
  806. echo "正在连接..."
  807. ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
  808. if [ $? -eq 0 ]
  809. then
  810. echo "Host1在线"
  811. else
  812. echo "Host1不在线"
  813. fi
  814. [es@bigdata-senior01 ~]$ ./checkHost.sh www.google.com
  815. 正在连接...
  816. Host:www.google.com不在线
  817. [es@bigdata-senior01 ~]$ ./checkHost.sh 114.114.114.114
  818. 正在连接...
  819. Host:114.114.114.114在线
  820. [es@bigdata-senior01 ~]$ ./checkHost.sh
  821. 请输入主机IP或名字
  822. 检查端口是否开放
  823. #!/bin/bash
  824. if [ -z $1 ] ; then
  825. echo "请输入需要检测IP和端口文件"
  826. exit
  827. fi
  828. if [ ! -e $1 ] ; then
  829. echo "文件不存在,请重新确认"
  830. exit
  831. fi
  832. File=$1
  833. cat $File | while read host
  834. do
  835. nc -zvw5 $host &> /dev/null
  836. if [ $? -eq 0 ] ; then echo "host: $host 开放"
  837. else echo "host: $host 关闭"
  838. fi
  839. done
  840. 用例3: vi checkScores.sh
  841. #!/bin/bash
  842. #输入分数,判断分数等级
  843. read -p "输入分数0-100: " grade
  844. if [ $grade -ge 90 ] && [ $grade -le 100 ] ; then
  845. echo "grade A"
  846. elif [ $grade -ge 75 ] && [ $grade -lt 90 ] ; then
  847. echo "grade B"
  848. elif [ $grade -ge 60 ] && [ $grade -lt 75 ] ; then
  849. echo "grade C"
  850. else
  851. echo "grade D"
  852. fi
  853. 8.2 for循环
  854. 语法:
  855. for 被循环自动赋值的变量 in 取值列表
  856. do
  857. 执行各种命令
  858. done
  859. 用例1:vi createUsers.sh
  860. 先准备一个userlist.txt,里面加一下用户名称,如下:
  861. [es@bigdata-senior01 ~]$ cat userlist.txt
  862. tom
  863. amy
  864. xu.dm
  865. xu.dm.test
  866. bus
  867. dba
  868. 编辑createUsers.sh脚本
  869. #!/bin/bash
  870. # 用重定向到/dev/nul来屏蔽屏幕输出
  871. # 脚本后面需要一个文件名参数
  872. if [ -z $1 ] ; then
  873. echo "请在脚本后面输入用户文件列表"
  874. exit
  875. else
  876. if [ ! -e $1 ] ; then
  877. echo "输入的文件不存在,请检查后重新输入"
  878. exit
  879. fi
  880. fi
  881. read -p "请输入用户密码:" password
  882. users=$(cat $1) #注意等号两边不能有空格,shell语法坑死人
  883. echo $users
  884. for uname in $users
  885. do
  886. id $uname &> /dev/null
  887. if [ $? -eq 0 ] ; then
  888. echo "$uname,用户已经存在!"
  889. else
  890. useradd $uname &> /dev/null
  891. echo "$password" | passwd --stdin $uname &> /dev/null
  892. if [ $? -eq 0 ] ; then
  893. echo "$uname,创建成功"
  894. else
  895. echo "$uname,创建失败"
  896. fi
  897. fi
  898. done
  899. 执行脚本:
  900. [es@bigdata-senior01 ~]$ sudo ./createUsers.sh userlist.txt
  901. 请输入用户密码:123@abc.com
  902. tom amy xu.dm xu.dm.test bus dba
  903. tom,创建成功
  904. amy,创建成功
  905. xu.dm,用户已经存在!
  906. xu.dm.test,创建成功
  907. bus,创建成功
  908. dba,创建成功
  909. 用例2:vi checkHosts.sh,检查文件列表里的主机是否在线
  910. #!/bin/bash
  911. if [ -z $1 ] ; then
  912. echo "请输入主机列表文件"
  913. exit
  914. fi
  915. if [ ! -e $1 ] ; then
  916. echo "文件不存在,请检查后重新输入"
  917. exit
  918. fi
  919. hostlist=$(cat $1)
  920. for host in $hostlist ; do
  921. ping -c 3 -i 0.2 -W 3 $host &> /dev/null
  922. if [ $? -eq 0 ] ; then
  923. echo "hosthost 在线"
  924. else
  925. echo "hosthost 不在线或被屏蔽"
  926. fi
  927. done
  928. [es@bigdata-senior01 ~]$ ./checkHosts.sh hosts.txt
  929. host:192.168.1.2 在线
  930. host:www.baidu.com 在线
  931. host:www.google.com 不在线或被屏蔽
  932. host:www.cnblogs.cn 在线
  933. host:www.csdn.net 在线
  934. host:www.qq.com 在线
  935. 8.3 while语句
  936. 语法:
  937. while 条件测试语句
  938. do
  939. 执行命令
  940. done
  941. 用例1:vi outputNum.sh
  942. #!/bin/bash
  943. #倒序输出数字
  944. if [ -z $1 ] ; then
  945. echo "please input a integer number[1-100]"
  946. exit
  947. fi
  948. if [ $1 -gt 100 ] || [ $1 -lt 1 ] ; then
  949. echo "error num,need number between [1-100]"
  950. exit
  951. fi
  952. num=$1
  953. while [ $num -ge 1 ] ; do
  954. echo "numnum"
  955. #num-1有三种写法
  956. #let "num=$num - 1"
  957. #num=$(expr $num - 1)
  958. let num--
  959. done
  960. 用例2:
  961. #!/bin/bash
  962. #测试随机数,需要输入测试次数
  963. if [ -z $1 ] ; then
  964. echo "please input test count number"
  965. exit
  966. fi
  967. count=$1
  968. num=1
  969. while [ $count -gt 0 ]
  970. do
  971. echo "第$num次,随机数:$RANDOM"
  972. let num++
  973. let count--
  974. done
  975. 8.4、case语句
  976. case 变量值 in
  977. 模式1)
  978. 命令1;;
  979. 模式2)
  980. 命令2;;
  981. *)
  982. 其他命令;;
  983. esac #就是case反过来和if语句类似,我已经不想吐槽这是什么逻辑
  984. 模式字符串中可以使用通配符
  985. 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令列表。
  986. 各模式字符串应是唯一的,不应重复出现,并且要合理安排它们的出现顺序。
  987. 用例1:修改上面的testRandom.sh
  988. #!/bin/bash
  989. #测试随机数,需要输入测试次数
  990. if [ -z $1 ] ; then
  991. echo "please input test count number"
  992. exit
  993. fi
  994. case $1 in
  995. *[a-z]* | *[A-Z]*)
  996. echo "输入的是字母,需要输入数字"
  997. exit;;
  998. *[0-9]*)
  999. echo "输入的是数字,符合要求";;
  1000. *)
  1001. echo "输入非法字符无法继续"
  1002. exit;;
  1003. esac
  1004. count=$1
  1005. num=1
  1006. while [ $count -gt 0 ]
  1007. do
  1008. echo "第$num次,随机数:$RANDOM"
  1009. let num++
  1010. let count--
  1011. done
  1012. 9、breakcontinue
  1013. break:用于立即终止当前循环的执行,break命令可以使用户从循环体中退出来。
  1014. 语法:break[n] ,其中,n表示要跳出几层循环,默认值为1
  1015. continue:跳过循环体中在其之后的语句,会返回到本循环层的开头,进行下一次循环。
  1016. –语法:continue[n],其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环,默认值为1,循环层数是由内向外编号。
  1017. 假设我们定义了一个变量为:
  1018. file=/dir1/dir2/dir3/my.file.txt
  1019. 可以用${ }分别替换得到不同的值:
  1020. ${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
  1021. ${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
  1022. ${file#*.}:删掉第一个 . 及其左边的字符串:file.txt
  1023. ${file##*.}:删掉最后一个 . 及其左边的字符串:txt
  1024. ${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3,#取父件夹路径
  1025. ${file%%/*}:删掉第一个 / 及其右边的字符串:(空值)
  1026. ${file%.*}:删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
  1027. ${file%%.*}:删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
  1028. 记忆的方法为:
  1029. # 是 去掉左边(键盘上#在 $ 的左边)
  1030. %是去掉右边(键盘上% 在$ 的右边)
  1031. 单一符号是最小匹配;两个符号是最大匹配
  1032. ${file:0:5}:提取最左边的 5 个字节:/dir1
  1033. ${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
  1034. 也可以对变量值里的字符串作替换:
  1035. ${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt
  1036. ${file//dir/path}:将全部dir 替换为 path:/path1/path2/path3/my.file.txt
  1037. 利用 ${ } 还可针对不同的变数状态赋值(沒设定、空值、非空值):
  1038. ${file-my.file.txt} :假如 $file 沒有设定,則使用 my.file.txt 作传回值。(空值及非空值時不作处理)
  1039. ${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作处理)
  1040. ${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作处理)
  1041. ${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作处理)
  1042. ${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。(空值及非空值時不作处理)
  1043. ${file:=my.file.txt} :若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為my.file.txt 。 (非空值時不作处理)
  1044. ${file?my.file.txt} :若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
  1045. ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值時不作处理)
  1046. ${#var} 可计算出变量值的长度:
  1047. ${#file} 可得到 27 ,因为/dir1/dir2/dir3/my.file.txt 是27个字节
  1048. &#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;Shell 数组
  1049. bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
  1050. 类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
  1051. 定义数组
  1052. 在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
  1053. 数组名=(值1 值2 ... 值n)
  1054. 例如:
  1055. array_name=(value0 value1 value2 value3)
  1056. 或者
  1057. array_name=(
  1058. value0
  1059. value1
  1060. value2
  1061. value3
  1062. )
  1063. 还可以单独定义数组的各个分量:
  1064. array_name[0]=value0
  1065. array_name[1]=value1
  1066. array_name[n]=valuen
  1067. 可以不使用连续的下标,而且下标的范围没有限制。
  1068. 读取数组
  1069. 读取数组元素值的一般格式是:
  1070. ${数组名[下标]}
  1071. 例如:
  1072. valuen=${array_name[n]}
  1073. 使用 @ 符号可以获取数组中的所有元素,例如:
  1074. echo ${array_name[@]}
  1075. 获取数组的长度
  1076. 获取数组长度的方法与获取字符串长度的方法相同,例如:
  1077. # 取得数组元素的个数
  1078. length=${#array_name[@]}
  1079. # 或者
  1080. length=${#array_name
  1081. }
  1082. # 取得数组单个元素的长度
  1083. lengthn=${#array_name[n]}
  1084. Shell 注释
  1085. # 开头的行就是注释,会被解释器忽略。
  1086. 通过每一行加一个 # 号设置多行注释,像这样:
  1087. #--------------------------------------------
  1088. # 这是一个注释
  1089. # author:菜鸟教程
  1090. # site:www.runoob.com
  1091. # slogan:学的不仅是技术,更是梦想!
  1092. #--------------------------------------------
  1093. ##### 用户配置区 开始 #####
  1094. #
  1095. #
  1096. # 这里可以添加脚本描述信息
  1097. #
  1098. #
  1099. ##### 用户配置区 结束 #####
  1100. 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
  1101. 每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
  1102. 多行注释
  1103. 多行注释还可以使用以下格式:
  1104. :<<EOF
  1105. 注释内容...
  1106. 注释内容...
  1107. 注释内容...
  1108. EOF
  1109. EOF 也可以使用其他符号:
  1110. :<<'
  1111. 注释内容...
  1112. 注释内容...
  1113. 注释内容...
  1114. '
  1115. :<<!
  1116. 注释内容...
  1117. 注释内容...
  1118. 注释内容...
  1119. !
  1120. &#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;&#10036;&#65039;函数,函数必须写在调用的前面
  1121. 实例
  1122. #!/bin/bash
  1123. # author:菜鸟教程
  1124. # url:www.runoob.com
  1125. funWithReturn(){
  1126. echo "这个函数会对输入的两个数字进行相加运算..."
  1127. echo "输入第一个数字: "
  1128. read aNum
  1129. echo "输入第二个数字: "
  1130. read anotherNum
  1131. echo "两个数字分别为 $aNum$anotherNum !"
  1132. return $(($aNum+$anotherNum))
  1133. }
  1134. funWithReturn
  1135. echo "输入的两个数字之和为 $? !"
  1136. 输出类似下面:
  1137. 这个函数会对输入的两个数字进行相加运算...
  1138. 输入第一个数字:
  1139. 1
  1140. 输入第二个数字:
  1141. 2
  1142. 两个数字分别为 1 和 2 !
  1143. 输入的两个数字之和为 3 !
  1144. 函数返回值在调用该函数后通过 $? 来获得。
  1145. 注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
  1146. 函数参数
  1147. 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
  1148. 带参数的函数示例:
  1149. 实例
  1150. #!/bin/bash
  1151. # author:菜鸟教程
  1152. # url:www.runoob.com
  1153. funWithParam(){
  1154. echo "第一个参数为 $1 !"
  1155. echo "第二个参数为 $2 !"
  1156. echo "第十个参数为 $10 !"
  1157. echo "第十个参数为 ${10} !"
  1158. echo "第十一个参数为 ${11} !"
  1159. echo "参数总数有 $# 个!"
  1160. echo "作为一个字符串输出所有参数 $* !"
  1161. }
  1162. funWithParam 1 2 3 4 5 6 7 8 9 34 73
  1163. 输出结果:
  1164. 第一个参数为 1 !
  1165. 第二个参数为 2 !
  1166. 第十个参数为 10 !
  1167. 第十个参数为 34 !
  1168. 第十一个参数为 73 !
  1169. 参数总数有 11 个!
  1170. 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
  1171. 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
  1172. 另外,还有几个特殊字符用来处理参数:
  1173. 参数处理 说明
  1174. $# 传递到脚本或函数的参数个数
  1175. $* 以一个单字符串显示所有向脚本传递的参数
  1176. $$ 脚本运行的当前进程ID号
  1177. $! 后台运行的最后一个进程的ID号
  1178. $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
  1179. $- 显示Shell使用的当前选项,与set命令功能相同。
  1180. $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
  1181. &#65532;
  1182. Shell 输入/输出重定向
  1183. 大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回&#8203;&#8203;到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
  1184. 重定向命令列表如下:
  1185. 命令说明
  1186. command > file将输出重定向到 file。
  1187. command < file将输入重定向到 file。
  1188. command >> file将输出以追加的方式重定向到 file。
  1189. n > file将文件描述符为 n 的文件重定向到 file。
  1190. n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
  1191. n >& m将输出文件 m 和 n 合并。
  1192. n <& m将输入文件 m 和 n 合并。
  1193. << tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。
  1194. 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  1195. 输出重定向
  1196. 重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:
  1197. command1 > file1
  1198. 上面这个命令执行command1然后将输出的内容存入file1。
  1199. 注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
  1200. 实例
  1201. 执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
  1202. $ who > users
  1203. 执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
  1204. 你可以使用 cat 命令查看文件内容:
  1205. $ cat users
  1206. _mbsetupuser console Oct 31 17:35
  1207. tianqixin console Oct 31 17:35
  1208. tianqixin ttys000 Dec 1 11:33
  1209. 输出重定向会覆盖文件内容,请看下面的例子:
  1210. $ echo "菜鸟教程:www.runoob.com" > users
  1211. $ cat users
  1212. 菜鸟教程:www.runoob.com
  1213. $
  1214. 如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
  1215. $ echo "菜鸟教程:www.runoob.com" >> users
  1216. $ cat users
  1217. 菜鸟教程:www.runoob.com
  1218. 菜鸟教程:www.runoob.com
  1219. $
  1220. 输入重定向
  1221. 和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:
  1222. command1 < file1
  1223. 这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
  1224. 注意:输出重定向是大于号(>),输入重定向是小于号(<)。
  1225. 实例
  1226. 接着以上实例,我们需要统计 users 文件的行数,执行以下命令:
  1227. $ wc -l users
  1228. 2 users
  1229. 也可以将输入重定向到 users 文件:
  1230. $ wc -l < users
  1231. 2
  1232. 注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
  1233. command1 < infile > outfile
  1234. 同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
  1235. 重定向深入讲解
  1236. 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
  1237. 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  1238. 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  1239. 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
  1240. 默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
  1241. 如果希望 stderr 重定向到 file,可以这样写:
  1242. $ command 2>file
  1243. 如果希望 stderr 追加到 file 文件末尾,可以这样写:
  1244. $ command 2>>file
  1245. 2 表示标准错误文件(stderr)。
  1246. 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
  1247. $ command > file 2>&1
  1248. 或者
  1249. $ command >> file 2>&1
  1250. 如果希望对 stdin 和 stdout 都重定向,可以这样写:
  1251. $ command < file1 >file2
  1252. command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
  1253. Here Document
  1254. Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
  1255. 它的基本的形式如下:
  1256. command << delimiter
  1257. document
  1258. delimiter
  1259. 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command
  1260. 注意:
  1261. 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
  1262. 开始的delimiter前后的空格会被忽略掉。
  1263. 实例
  1264. 在命令行中通过 wc -l 命令计算 Here Document 的行数:
  1265. $ wc -l << EOF
  1266. 欢迎来到
  1267. 菜鸟教程
  1268. www.runoob.com
  1269. EOF
  1270. 3 # 输出结果为 3 行
  1271. $
  1272. 我们也可以将 Here Document 用在脚本中,例如:
  1273. #!/bin/bash
  1274. # author:菜鸟教程
  1275. # url:www.runoob.com
  1276. cat << EOF
  1277. 欢迎来到
  1278. 菜鸟教程
  1279. www.runoob.com
  1280. EOF
  1281. 执行以上脚本,输出结果:
  1282. 欢迎来到
  1283. 菜鸟教程
  1284. www.runoob.com
  1285. /dev/null 文件
  1286. 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
  1287. $ command > /dev/null
  1288. /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
  1289. 如果希望屏蔽 stdout 和 stderr,可以这样写:
  1290. $ command > /dev/null 2>&1
  1291. 注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  1292. 这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
  1293. Shell 文件包含
  1294. 和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
  1295. Shell 文件包含的语法格式如下:
  1296. . filename # 注意点号(.)和文件名中间有一空格
  1297. source filename
  1298. 实例
  1299. 创建两个 shell 脚本文件。
  1300. test1.sh 代码如下:
  1301. #!/bin/bash
  1302. # author:菜鸟教程
  1303. # url:www.runoob.com
  1304. url="http://www.runoob.com"
  1305. test2.sh 代码如下:
  1306. #!/bin/bash
  1307. # author:菜鸟教程
  1308. # url:www.runoob.com
  1309. #使用 . 号来引用test1.sh 文件
  1310. . ./test1.sh
  1311. # 或者使用以下包含文件代码
  1312. # source ./test1.sh
  1313. echo "菜鸟教程官网地址:$url"
  1314. 接下来,我们为 test2.sh 添加可执行权限并执行:
  1315. $ chmod +x test2.sh
  1316. $ ./test2.sh
  1317. 菜鸟教程官网地址:http://www.runoob.com
  1318. 注:被包含的文件 test1.sh 不需要可执行权限。
  1319. 默认 bash 里定义的变量是全局的。
  1320. $ a=10; function b() { a=2; }; b; echo $a
  1321. 执行结果为2
  1322. 即:函数b里对a进行修改后,a的值就发生改变。
  1323. 如果不想b对a的操作不影响全局的值,可以将b中的a设为局部变量。
  1324. 如下所示:
  1325. $ a=10; function b() { local a=2; }; b; echo $a
  1326. 执行结果为10
  1327. 还有一点需要注意。
  1328. 在pipe之后的处理不会改变原来的值,因为新建一个进程。
  1329. $ a=1; echo $a; echo "hello, world" | while read line ; do a=2; echo $a; done; echo $a
  1330. 执行结果为:
  1331. 1
  1332. 2
  1333. 1
  1334. 虽然在 pipe 后a改成2,但是最后输出的值仍是最初的1
  1335. ho $a; echo "hello, world" | while read line ; do a=2; echo $a; done; echo $a
  1336. 执行结果为:
  1337. 1
  1338. 2
  1339. 1
  1340. 虽然在 pipe 后a改成2,但是最后输出的值仍是最初的1

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/154417?site
推荐阅读
相关标签
  

闽ICP备14008679号