shell基础命令

bash语言

常用命令

1.head

  • 默认取文件前10行内容

2.tail

  • 默认取文件末尾10行的内容
    例:取文件8-14行的内容
head -14 /etc/passwd | tail -7 

3.cut

  • 取出文本指定的列
  • 默认以空格或者tab键进行分割(不支持不规则的空格)
  • 选项
    • -d 指定分割符
    • -f 指定获取的列号
      获取第一列:
cut -f1 test.txt

获取1和2列:

cut -f1,2 test.txt
cut -f1-2 test.txt

获取第一列和第三列:

cut -f1,3 test.txt

为分割符获取第一列:

cut -d":" -f1 test.txt

4.sort

  • 对文本的内容进行排序
  • 默认以字符的ASCII码数值从小到大排序
  • 选项
    • -n 以数值大小排序
    • -r 倒序
    • -t 指定分割符,默认为空格
    • -knum 指定以某个字段来排序
  • 以数字大小排序
sort -n num.txt
  • 指定分割符为:且以第三列的数字大小排列:
sort -t":" -k3 -n /etc/passwd

指定分割符为:且以第三列的数字大小逆序(从大到小)排列:

sort -t":" -k3 -n -r /etc/passwd

5.uniq

  • 去重:必须有顺序后才去重
sort -n num.txt | uniq
  • 选项
    • -d 仅打印有重复的元素
    • -c 打印元素重复的个数

6.wc(word count)

  • 计算文本数量
  • 选项
    • wc -l 打印行数
wc -l /etc/passwd
  • wc -w 打印单词数
  • wc -c 打印字节数
  • wc -L 打印最长行的字节数

变量

变量定义

  • 方法一
    • 变量名=变量值
    • 变量值必须是一个整体,中间没有特殊字符,等号两侧不能有空格
  • 方法二
    • 变量名='变量值'
    • 看到的内容,就输出什么内容
  • 方法三
    • 变量名="变量值"
  • 方法四
  • 变量名=$(linux命令)
  • 变量名=$linux命令(反引号)
    以上四种方法默认为本地变量
a=1
b=emilymei
c="hello world"
echo 123 #向屏幕输出内容
echo $a   #1
echo $b    #emilymei
echo $c  #hello world
d='hello from china'
echo $d  #hello from china
date_now=$(date)
echo $date_now  # 打印当前时间
date_now2=`ls`
echo $date_now2. # 打印文件列表

单引号与双引号的区别:

a=123
echo 'this is $a'  #this is $a  ,单引号时,把 $a当成普通字符串打印出来
echo "this is $a'"   #this is 123 ,使用双引号时,引用变量
echo $a #123
echo @a_1  #(空),认为a_1是变量,shell中不存在的变量不会报错
echo ${a}_1  #123_1
echo $a _1  #123 _1

预定义变量(全局变量)

env:查看环境变量(只显示全局变量)

  • 定义全局变量
    • 方法一
变量=值
export 变量
  • 方法二(最常用)
export 变量=值

全局变量只能在当前打开的终端中生效,关闭后不再生效,需将环境变量放到~/.bashrc~/.bash_profile/etc/profile文件中去,才能永久生效,这三个文件都是打开终端就会执行的文件。其他文件中的都是临时全局变量。

echo $PWD #相当于pwd,返回的是当前的家目录
echo $USER  #返回当前用户名
echo $HOME  #返回当前用户的家目录,相当于echo ~
echo $PATH  #返回当前的环境变量
ls  #显示当前目录的文件
a=ls ;echo $a   #ls
a=`ls`;echo $a  #显示当前目录的文件,反引号(``)里的内容会被当做命令来执行
which python  #查找Python的安装地址,只能在当前环境变量里找
export PATH=$PATH:$PWD   #将当前目录加到环境变量里去
ll  #ls -l的简写
chmod +x 1.sh  #1.sh文件添加可执行权限

内置变量

符号 含义
$0 获取当前执行的shell脚本文件名,包括脚本路径
$n 获取当前执行的shell脚本的第n个参数值,n=1..9,如果n大于9就要用大括号括起来${10}
$# 获取当前shell命令行中参数的总个数
$? 获取执行上一个指令的返回值(0为成功,非0为失败)
$@ 获取当前执行的shell脚本的所有参数
$# 获取当前执行的shell脚本的参数总数

数组变量

a=1
b=2
c=3
array=(1 2 3 4 5)
echo $array  #1,错误
echo ${array[@]} #1 2 3 4 5
echo ${array[*]}  #1 2 3 4 5
echo ${#array[@]}   #5,代表这个数组里有5个元素
echo ${#array[*]}   #5,代表这个数组里有5个元素
echo ${array[2]}   #3,取数组第3个元素
echo ${array[-1]}   #3,取数组最后1个元素
array=(`ls`)  #数组的表现形式是(),这个是数组
echo ${array[@]}   #执行ls命令
array=`ls`  #这个是字符串,因为没有()
#@和*的区别,*:当加上双引号时,会当成字符串来处理,不加双引号,当初数组处理@:不管加不加双引号,依然当做数组处理,例:
array=(1 2 3);for line in "${array[@]}";do echo $line;done   
#1
#2   
#3
array=(1 2 3);for line in "${array[*}";do echo $line;done     #1 2 3,把array当成字符串处理
array=(1 2 3);for line in ${array[@]};do echo $line;done   
#1
#2
#3
array=(1 2 3);for line in ${array[*]};do echo $line;done
#1
#2
#3

特殊符号的引用

  • 双引号用于括起一段字符串,支持$var形式的变量替换
  • 单引号也表示其内容是字符串值,不支持转义
  • \反斜杠,某些情况下表示转义
  • $(ls)表示执行ls后的结果。与``类似。不过可以嵌套
  • `反引号。用法比较独特,代表命令的输出,非常有用
  • ((a+b))$((2+3))
  • (())是证书扩展。把里面的变量当做整数去处理
  • ({1..1010})等价于 seq 1 10 。表示1到10
echo "a\nbb"  #a\nbb,没有达到转义效果,错误
echo -e "a\nbb"    #-e,开启转义模式
#a
#bb
echo "a\bb"   #a\bb,\b=删除前一个字符
echo -e "a\bb"   #b
echo -e "a\abb"  #abb,\a=发出警告的声音
a=2;b=3
echo $((a+b))  #5,(())进行两个变量间的操作
a=5;b=7;c=2
echo $((a+b*c))  #19
echo $(($a+$b*$c))  #19
a=1;b=3
(($a>$b));echo $?   #1,非0的返回表示结果为false,0表示true
array=(`seq 1 15`)  #1到15的整数
echo ${array[@]}   #1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

变量类型

  • 字符串
  • 数字
  • 布尔
  • 数组 array=(a b c )
  • 函数 foo(){echo hello world}
a="hello world"   #引号引起来的都是字符串
b=1;c=2   #整型
a=true;echo $a   #true  ,布尔

数字型变量操作

  • 计算:i=1;echo $i ;echo $((i+1))
  • 更新: ((i=i+1));echo $i
  • 只能进行整数计算
  • 浮点数计算请使用awk 'BEGIN{print 1/3}'
  • 格式化显示可以使用 awk 'BEGIN{printf("%.2f\n",1/3)}'
  • printf 格式化
  • BEGIN :为了读取管道或者输入的数据
a=1
echo $((a+1))   #2
((a=a+1))   #自增
echo $a  #2
i=1;((i=i+1));echo $i   #2
a=1 
((a++));echo $a   #2
awk 'BEGIN{print 2/3}'   #0.666667
awk 'BEGIN{printf "%.2f\n",1/3}'   #0.33,格式化为保留两个小数点

字符串操作

  • # 表示掐头
  • % 表示去尾
  • / 表示替换
  • # 与## 、%与%% 、/与//的区别 :最短匹配模式与最长匹配模式
  • awk可以代替这些操作
s="hello world";echo $s   #hello world
echo ${s:6}    #world,从第6项开始,打印后面的字符
echo ${s:6:3}   #wor  ,只打印从第6项开始之后3个内容
echo ${#s}     #11,字符串的长度
echo ${s#hello}   #world,掐头,去掉hello内容,默认空格去掉
echo "${s#hello}"   # world,掐头,去掉hello内容,空格不去掉
echo ${s#*o}  #*表示任意字符, world
echo ${s##*o}  #rld,表示去掉最后一个o前的所有内容
echo ${s%world}   #hello ,去尾
echo ${s%%o}   #hell
echo ${s/world/emily}   #hello emily   ,world替换为emily

布尔变量

  • 命令执行返回值 $?
  • 任何命令执行都会有一个返回值
  • 0表示正确
  • 非0表示错误
a=true;echo $?   #0
b=false;echo $?   #0,$?返回的是前一条命令是否正确执行

算术判断

  • [ 2 -eq 2 ] 相等
  • [ 2 -ne 2 ] 不等
  • [ 3 -gt 1] 大于
  • [ 3 -ge 3 ] 大于等于
  • [ 3 -lt 4 ] 小于
  • [ 3 -le 3 ] 小于等于
  • (())也可以表示算术比较。((10>=8)).((10==10)).
[ 3 -eq 2 ];echo $?    #1
echo $?   #0
((3>2));echo $?   #0

字符串比较

  • [ string1 = string2 ]如果两字符串相同,则结果为真
  • [ string1 != string2 ]如果两字符串不相同,则结果为真
  • [ -n string1 ]如果字符串不是空,则结果为真
  • [ -z string1 ]如果字符串是空,则结果为真
  • [[ "xxxx" == x* ]] 在表达式中表示0个或多个字符
  • [[ "xxxx" == x?? ]] 在表达式中表示单个字符
  • 在引用变量的时候要记得加双引号[ -z "$a" ]否则当a未定义时会语法报错
aaa=wwef
bbb=fdgggdh
["@{aaa}" == "${bbb}"];echo $?

逻辑判断

[ 2 -ge 1 -a 3 -ge 4 ];echo $?  # 与
[ 2 -ge 1 -o 3 -ge 4 ];echo $?  #或
[ 2 -ge 1 && 3 -ge 4 ];echo $?  # 与
[ 2 -ge 1 || 3 -ge 4 ];echo $? # 或
[ ! 2 -ge 1 ];echo $? #非 
username="snow"
["${username}" == "snow"] && echo "成功" || echo "失败"  # 如果等于snow就输出成功
# ,否则输出失败
passwd=123
["${username}" == "snow"  -a "${passwd}" == 123 ]&& echo "成功" || echo "失败"  
# 如果username等于snow并且passwd等于123就输出成功,否则就输出失败

内置判断

  • -e file 如果文件存在,则结果为真
  • -d file 如果文件是一个子目录,则结果为真
  • -f file 如果文件是一个普通文件,则结果为真
  • -r file 如果文件可读,则结果为真
  • -s file 如果文件的长度不为0,则结果为真
  • -w file 如果文件可写,则结果为真
  • -x file 如果文件可执行,则结果为真
[-x test.sh];echo $?  # 文件是否可执行
  • [[]]是[]的扩展语法,在老的sh里并不支持,推荐用[]

shell脚本格式

1.格式要求

  • 在文件首行指定执行shell的程序及相关说明
#!/bin/bash
# Author: Mei
# Date: 2021-12-07
  • shell脚本文件后缀,建议命令为.sh
  • 脚本执行失败时,使用exit返回非零值,来退出程序
  • 默认缩紧4个空格
  • shell脚本的命名简单、有意义

2.注释

  • 单行注释
    • #
  • 多行注释
 :<

逻辑控制

  • 条件 if
  • 分支case
  • 循环 for while until select
  • && ||
  • break和continue
    if结构
  • if [ condition ] ;then ```;fi
  • if [ condition ] ;then ```;else ...;fi
  • if [ condition ] ;then ```;elif ...;fi
  • 简单的逻辑可以使用 && ||去替代
  • 条件可以用命令返回值代替
if [ -e test ];then echo exist;else echo not exist;fi    
if ls test;then echo exist;else echo not exist;fi
ls test && echo exist ||echo not exist
[ -e test ] $$ echo exist || echo not exist   #不是完全等价于if else
echo "1" && echo "2" || echo "3" && echo "4" || echo "5" && echo "6" && echo "7" && echo "8" ||echo "9"   #1\n2\n4\n7\n8
read -e -p "请输入路径(如:/etc/passwd):"
if [ -f ${REPLY} ]
then 
        echo "${REPLY}是常规文件"
elif [ -d ${REPLY} ]
then
        echo "${REPLY}是目录"
else
        echo "${REPLY}是其他文件"
fi

查询某个应用是否启动:(例)

if netsta -tiup | grep "3306" &> /dev/nell; then
        echo "已启动"
else 
        echo "未启动" 
fi

control+r:可搜索
less ~./bash_history:查看历史命令

for循环

  • for(( c1;c2;c3 ));
  • do
  • ...;
  • done
  • for ((i=0;i<10;i++));do echo $i;done
for ((i=0;i<10;i++));do echo $i ;done  #0\n1\n2\n3\n4\n5\n6\n7\n8\n9
array=(1 2 3 4 5)
for ((i=0;i<${array[@]};i++));do echo $i;done  #语法报错,变量不能和数组作比较
for ((i=0;i<${#array[@]};i++));do echo ${array[i]};done   #1\n2\n3\n4\n5

for遍历循环

  • 用于递归数组,还可以递归以空格隔开的字符串序列。或者某个命令的返回值

  • for f in $array[*];

  • do

  • ...

  • done

  • ss="aa bb cc dd";for x in x;done

  • for x in ls;do echo $x;done

  • ss=(aa bb cc "sss dd");for x in x;done

ss="aa bb cc dd";for x in $ss;do echo $x;done
for x in `ls`;do echo $x;done
ss=(aa bb cc "sss dd");for x in ${ss[@]};do echo $x;done
#若文件名中有空格,则不能使用`ls`,若使用:(文件名为2 3)
for x in `ls`;do echo $x;done    #2\n3
#此时用:
for x in *;do echo $x;done  #2 3
touch "2 3"  #新建文件2 3
rm "2 3"  #删除文件2 3 
rm -rf   #删除目录下的所有文件,慎用
dir=$(ls /)
index=1
for f in $dir
do
        echo "${index}.${f}"
        index=$((index+1))
done

批量创建用户:

for u in ${cat ~/users.txt}; do
        useradd "$u"
        echo "$u:123456" | chpasswd&>/dev/nell && echo "创建$u成功" || echo "创建$u失败"
        passwd -e "$u"
done

while循环

  • i=0;while [ $i -lt 3 ] ;do echo $i;((i=i+1));done
  • 一个有用的小技巧。一行行的读取文件内容:while read line;do echo $line;done < 2
  • 如果while数据源来自文件,done<文件名,重定向到while中去
while read hostname ip;do
        ping -c1 -W1 "$ip" &> /dev/null
        if [ $? -eq 0 ];then
                echo "主机【${hostname}】在线"
        else
                echo "主机【${hostname}】离线"
        fi
done < ~/servers.txt
i=0
while [ $i -lt 3 ] ;do echo $i;((i=i+1));done  #0\n1\n2
while read line;do echo $line;done < 2   #读取文件2中每一行内容到屏幕
read x  #(输入 )1111  ,相当于input,读取一个标准输入
 echo $x   #1111
read -p "enter:" x;echo 我刚刚输入了: $x   #enter:(输入)111    我刚刚输入了:111
echo $x   #111

case

function menu
{
             echo -e "\t\txxx管理系统"
             echo
             echo -e "\t\t1.备份"
             echo -e "\t\t2.显示"
             echo -e "\t\t3.重启"
             echo -e "\t\t4.还原"
             echo -e "\t\t0.退出"

}
function main
{

        while true;do
                clear
                menu
                read -s -e -n1
                echo 
                case "${REPLY}" in
                            "1") 
                                                echo "执行备份操作"   
                                                ;;
                            "2") 
                                                echo "执行显示操作"  
                                                ;;
                            "3") 
                                                echo "执行重启操作" 
                                                ;;
                            "4") 
                                                echo "执行还原操作" 
                                                ;;
                            "0") 
                                                echo "执行退出操作"
                                                 break 
                                                ;;
                             *) 
                                                echo "其他操作" 
                                                ;;
                  esac
                  read   
          done
}

退出控制

  • return 函数返回
  • exit 脚本退出
  • break 退出当前循环。默认为1
  • break 2 退出两层循环
  • continue 跳过当前循环,进入下一次循环
  • continue 2 跳到上层循环的下一次循环中
for f in *;do echo $f;if [ -d f ];then break;fi;done
for f in *;do echo $f;if [ -f f ];then echo $f is a file ;else continue;fi;done  #结束当前循环,并且进入下次循环

函数

1.格式

# 格式一:
函数名()
{
        命令1
        命令2
        ···
}
# 格式二:
function 函数名
{
        命令1
        命令2
        ···
}
# 方法一
show_info()
{
    echo -e "Hello,${USER},date:$(date)"
}
# 使用函数名即可调用
show_info

# 方法二
function show_info1
{
    echo -e "Hello,${USER},date:$(date)"
}
show_info1

2.参数

function check_host
{
    ip="$1"
    hostname = "$2"
    ping -c1 "&{ip}" &> /dev/null
    [ $? -eq 0 ] && echo -e "主机【&{hostname}】在线" || echo -e "主机【&{hostname}】离线"
}
# check_host 114.114.114.114 应用服务器1
check_host $1 $2 # 外部执行时传参

shell运行环境概念

  • bash是一个进程,bash下还可以重新启动一个shell,这个shell是sub shell,原shell会复制自身给他
  • 在sub shell中定义的变量,会随着sub shell的消亡而消失
  • ()在子shell中运行
  • {}在当前shell中运行
  • $$ 当前脚本执行的pid
  • & 后台执行
  • $! 运行在后台的最后一个作业的pid
echo $$   #查看当前shell的进程号
(a=1)   #()内的是子shell
echo $a   #返回为空,子shell运行完会消失
a=3
(a=1;echo $a );echo $a  #1\n3
{a=1;echo $a ;};echo $a  #1\n1
for ((i=0;i<10;i++));do echo $i ;sleep 2;done  #平均每2秒打印一次i
for ((i=0;i<10;i++));do echo $i ;done &   #平均每2秒打印一次i并且放在后台执行
jobs     # 查看执行任务的运行状态
Ctrl z  #把当前任务切换到后台执行
bg 1   #把后台的命令继续执行,1指命令标记
fg 1   #把1任务放到前台执行

shell环境变量

  • shell首先是一个工作环境,有很多变量可以供我们使用
  • set 可以获得当前的所有变量
  • env 可以获得可以传递给子进程的变量
  • export aa==bbbb 把私有变量导出
echo $PATH   #查看环境变量
export PATH:`pwd`    #把当前目录加到环境变量里
vim .bashrc  里面加上  export PATH=$PATH:`pwd`,保存
source ~/.bashrc  #环境变量添加成功
#vim模式下
:set nu   #显示行号
:set nonu   #不显示行号

esc模式下:

  • gg 跳到文件开头
  • G 跳到文件最后一行

按键盘v ,进入剪切复制模式:

  • d 选中
  • y 复制
  • p 粘贴

shell输入输出

  • read 用来读取输入,并赋值给变量
# -p:提示用户输入内容
read -p "请输入:"
# -n:指定输入字数
read -n1 -p "请输入Y/N:"
echo
echo $REPLY
# -s:加密输入内容
read -s -p "请输入密码:"
  • $REPLY:内置函数,往往只接受read的一个参数,如需多个,则需指定赋值给哪个参数
read -p "请输入主机名称:" hostname
read -p "请输入IP地址:" ip
function check_host
{
    # ip="$1"
    # hostname = "$2"
    ping -c1 -W2 "&{ip}" &> /dev/null
    [ $? -eq 0 ] && echo -e "主机【&{hostname}】在线" || echo -e "主机【&{hostname}】离线"
}
# check_host 114.114.114.114 应用服务器1
check_host
  • echo,print 可以简单输出变量
  • file 将输出重定向到另一个文件,等价于 tee

  • < file 输入重定向
  • | 表示管道,也就是前一个命令的输出传入下一个命令的输入
echo "hello world" > 1  #1文件里内容为hello world,覆盖原来的文件内容
echo "hello china" >>1  #1文件里内容为hello world  hello china,向原来文件追加内容
vim 222  #hello world \n hello china
read x < 222   #从222文件读取内容输入到x
echo $x  #222文件内容
ls > 1  #1文件内容为执行ls命令后返回的数据
ls ddd >1 2>&1    #标准输入(1)和标准错误(2)都重定向到1文件中

vim text1
#hello world
#email
#hhh
#little
vim text2
#hello world,this is a grep test.
#email
#hhh
#little
vim text2
#hello world
#email
#ffff
#little
grep -i "hello world" text1 text2 text3   
#text1:hello world
#text2:hello world,this is a grep test.
#text1:hello world
-i  :不区分大小写
-o  :只显示需要的字符串
grep -o "hello world" text1 text2 text3   
#text1:hello world
#text2:hello world
#text1:hello world
grep -io "hello world" text1 text2 text3   
#text1:hello world
#text2:hello world
#text1:hello world
echo abcd | grep -o c   #c

正则表达式

  • . 表示任意一个字符
  • * 表示前面的字符有0个或多个
  • .* 表示任意一个有0个或多个的字符
curl www.baidu.com
vim baidu.keyword     #\mp3\nmp4\nrest-assured\nandroid\nios
while read k;do echo $k;done < baidu.keyword    #\mp3\nmp4\nrest-assured\nandroid\nios
while read k; do $k ;curl http://www.baidu.com/s?wd=$k; done < baidu.keyword  #搜索相应的内容后返回的网页代码输出到baidu.keyword
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o  "结果约[0-9,]*"   #显示结果约和数量
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o  "结果约[0-9,]*" | grep -o "[0-9,]*"   #只显示数字
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o  "结果约[0-9]*"  | while read x;do printf $x;done;echo;done 

你可能感兴趣的:(shell基础命令)