shell脚本
前言
今天开始shell的学习,shell是核心程序Kernel之外的命令解析器,是一个程序,也是一种命令语言和程序设计语言。作为一种命令语言,可以交互式解析用户输入的命令。
比如输入ls命令后,shell会解析ls这个字符并向内核发出请求,内核执行这个命令之后把结果告诉shell,shell会把结果输出到屏幕。linux 默认的shell是bash。
shell脚本基本元素
获取shell 脚本名 echo $0
或者 echo bashname
获取文件夹路径 dirname $0
shell 变量
定义一个变量
your_name="runoob.com"
- =左右不能加空格
- 变量名不加$
- 不能有标点符号
- 不能使用保留关键字符(可直接输入help 查看保留关键字)
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
your_name="qinjx"
echo $your_name
echo ${your_name}
变量名外的花括号是可选的,主要是为了界定变量的边界
your_name="qinjx"
echo $your_nameis my friend
echo ${your_name}is my friend
他会认为your_nameis 是个变量(其值为空)
编程习惯是:对变量名都加上花括号
shell 内部变量
- PWD 当前的工作目录
- RANDOM 每次引用这个变量时都会生成一个均匀分布的0~32767 范围内的随机整数
- SCONDS 脚本已经运行的时间
- PPID 当前进程的父进程的进程ID
- $? 表示最近一次执行命令或shell 脚本的出口状态
shell 环境变量
主要环境变量如下:
- EDITOR
- HOME 用户主目录
- PATH 指定命令的检索路径
shell字符串
最好用双引号来引用字符串
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
拼接字符串
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3
输出结果为:
hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !
获取字符串长度
string="abcd"
echo ${#string} #输出 4
提取子字符串
以下实例从字符串第 2 个字符开始截取 4 个字符:
string="runoob is a great site"
echo ${string:1:4} # 输出 unoo
注意:第一个字符的索引值为 0。
查找子字符串
查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
string="runoob is a great site"
echo `expr index "$string" io` # 输出 4
注意: 以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。
Shell 数组
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
数组名=(值1 值2 ... 值n)
例如:
array_name=(value0 value1 value2 value3)
或者
array_name=(
value0
value1
value2
value3
)
还可以:
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
读取数组
读取数组元素值的一般格式是:
${数组名[下标]}
例如:
valuen=${array_name[n]}
使用 @ 符号可以获取数组中的所有元素,例如:
echo ${array_name[@]}
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
Shell 注释
单行注释
以 # 开头的行就是注释,会被解释器忽略。
多行注释
多行注释还可以使用以下格式:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
Shell 传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
实例
以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名(包含文件路径):
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
shell中的控制流结构
if
if语句语法如下:
if
then
命令1
else
命令2
fi
案例如下:
if [[ ${num} < 10 ]]
then
echo "输入数字小于10"
else
echo "输入数字大于10"
fi
if语句语法进阶用法:
if 条件1
then
命令1
elif 条件2
then
命令2
else
命令3
fi
案例如下:
num=$1
if [[ ${num} -lt 100 ]] && [[ ${num} -ge 90 ]]
then
echo "优"
elif [[ ${num} -lt 90 ]] && [[ ${num} -ge 80 ]]
then
echo "良"
else
echo "中"
fi
case:
case语句语法如下:
case 值 in
模式1)
命令1
;;
模式2)
命令2
;;
*)
命令3
;;
esac
案例如下:
#!/bin/bash
#case select
echo -n "Enter a num form 1 to 3:"
read ANS
case $ANS in
1)
echo "you select 1 ..."
;;
2)
echo "you select 2 ..."
;;
3)
echo "you select 3 ..."
;;
*)
echo "`basename $0` : this is not between 1 and 3"
exit;
;;
esac
for语法:
for 变量名 in 列表
do
命令
done
案例如下:
#!/bin/bash
for n in {100..200}
do
host=192.168.1.$n
ping -c2 $host &>/dev/null
if [ $? = 0 ];then
echo "$host is UP"
else
echo "$host is DONE"
fi
done
使用shell脚本遍历日期
date=$(date +%Y-%m-%d)
for (( i=0; i < 30; i++ ))
do
date=$(date -d "${date} -1 days" +%Y-%m-%d)
echo "curl 32.61.51.61/$date"
done
1. 按天倒序遍历日期 ( 正序修改一下日期增减函数即可 )
#!/bin/bash
#倒序按天遍历日期
#传入遍历的开始时间和结束时间
startdate="$1"
enddate="$2"
echo 'startdate: '$startdate
echo 'enddate: '$enddate
echo "-----------------------------------"
#序列1-300,表示遍历300次,因为有结束时间的限制,所以实际上不会循环300次
for i in `seq 1 300`; do
#当开始时间小于结束时间时,直接结束脚本
if [[ $startdate -lt $enddate ]]; then
break
fi
echo $startdate
#执行hiveSQL脚本,我是需要按日期执行hiveSQL,这里可以无视
hive -hiveconf input_date=$startdate -f mergeSmallFiles.sql # >> mergeSmallFiles.txt
#每次执行后,使开始日期减一天,如果要正序,将下面-1换成+1即可,当然开始时间和结束时间也要换一下
startdate=$(date -d "$startdate -1 day" +%Y%m%d)
done
#!/bin/bash
#文件名:test.sh
name="xiongzaiqiren"
echo $name
echo "BACKUP DATE:" $(date +"%Y-%m-%d %H:%M:%S")
DATE=`date '+%Y%m%d-%H%M%S'`
echo $DATE
LogNameDATE=`date '+%Y%m%d'`
echo " " >> log$LogNameDATE.log
echo "———————————————–" >> log$LogNameDATE.log
echo "BACKUP DATE:" $(date +"%Y-%m-%d %H:%M:%S") >> log$LogNameDATE.log
echo "———————————————– " >> log$LogNameDATE.log
当前日期格式输出:
#将当前时间和包含换行符的文本内容输出到文件
echo -e $(date) "\nHello World !" >> test.txt
#将当前时间(格式化)和包含换行符的文本内容输出到文件
echo -e `date '+%Y-%m-%d %H:%M:%S %A'` "\nHello World !" >> test.txt
#同上,简化版。
echo -e `date '+%F %T %A'` "\nHello World !" >> test.txt
#输出到以日期格式文件名
echo -e $(date) "\nHello World !" >> test`date +'%Y-%m-%d'`.txt
ORACLE_TABLE=(`cat /root/ORACLE_TABLE.txt | awk '{print $1}'`)
HDFS_PATH=(`cat /root/ORACLE_TABLE.txt | awk '{print tolower($1)}'`)
TABLE_COUNTS=(`cat /root/ORACLE_TABLE.txt | wc -l`)
for(( i=0; i < $TABLE_COUNTS; i++ ))
do
echo tablename: ${ORACLE_TABLE[$i]} ------ HDFS_PATH: ${HDFS_PATH[$i]}
done
获取数组的长度
${#my_array[*]}
shell远程执行:
ssh user@node1 "cd /home ; ls"
例: ssh root@node1 "sh /root/sqoop-hdfs.sh"
总结:
(1)$(cmd)与··(键盘上1左边的~)一样,都是命令替换,可以将执行结果提取出来
(2)[]使用的时候[ ]前后都必须有空格,且两个字符或数字之间的比较符左右也必须有空格。
(3) []是test的另一种形式,[]中间只能使用= 和 != 比较字符串,如果使用< 、>需要进行转义\。
[]中间如果比较数字需要用 -lt 等符号,不能使用\<比较数字,会当成字符串处理。
(4)[[]]可用于处理逻辑命令,也可以用于处理字符串是否相等,且使用<、>不用转义符.
(5)(())可用于比较数字,且不用转义,而且也可以用于数字计算,比较的时候也是用普通的>,<。(())计算的时候运算符与数字之间不能有空格,例如: sum=$(($sum+4))
(6)字符串比较 用[],与普通的<,>,=,!=符号,如果使用<,>需要转义;或者使用[[]]比较字符串也是用普通符号不用转义
数字比较用[]的时候用-lt,-gt等符号,不能使用\<(因为会当成字符串处理);或者用(())比较数字用普通符号不用转义
(7)可以将$理解为取变量的符号,$var 或者 ${} ,在不影响语义的情况下可以省去{},但是最好写上{}。例如:test=XXX.$testWWWW.这时候就必须加上{}变为${test}WWWW
shell脚本中的$0, $?, $!, $$, $*, $#, #@的
1. $$Shell本身的PID(ProcessID)
2. $!Shell最后运行的后台Process的PID
3. $?最后运行的命令的结束代码(返回值)
4. $-使用Set命令设定的Flag一览
5. $*
如果加" ",即"$*"则表示视所有该命令参数为一个单词,如果没有" ",那么该命令每一个参数
都视为独立的参数。比如传入的参数:1 2 3 4,$*:表示1 2 3 4;"$*":表示1,2,3,4
6. $@不管有" "没有" ",每个参数都是独立的,但是,如果传入的参数比如是:1 "2 3" 4,那就有点差异了,"$@": 表示1,2 3,4;而$@:表示1,2,3,4
7. $#添加到Shell的参数个数
8. $0Shell本身的文件名