对Shell不感兴趣?来,我们在shell里面看一部电影,《星球大战》
谁说的shell没有图形化。
在linux bash终端中输入以下命令即可
# sudo apt-get install telnet
# telnet towel.blinkenlights.nl
shell编程对代码的实现很自由,但对空格有严格的规定。
在Linux中,千万不要使用这条命令,千万不要!千万千万千万千万不要!!!
$ rm -rf /*
熟话说:
rm -rf 准备好跑路了吗?
date 看看几点适合跑路。
cal 看看几号老板不上班。
shutdown 数据库被删了赶紧跑,就说这是策划的锅
Ax 介绍
Unix式的操作系统仍是有史以来最佳设计之一,这种架构最重要的一个特性就是命令行界面或Shell。Bash(Bourne Again Shell)是目前大多数Linux默认的Shell环境。
shell不同于其它语言,它的本质就算一些文件,包含一系列的命令。
熟悉Shell前的基本认识
$ 为普通用户
# 表示root
建议:最好不要用Root运行命令,因为能力越大,责任就越大,比如删除一个东西,root不会有如何阻碍,如果你操作失误了,你可能会把电脑的文件删除干净,导致系统崩溃。
可以使用sudo命令赋予普通用户的root权限,这样就相当于加了一层安全防护。
shell文本文件的格式,在第一行加入一个Bash解释器命令的路径:
#! /bin/bash
运行shell文本文件的方法有两种,一是直接使用bash命令运行,二是赋予权限运行。
# bash命令
$ bash /home/path/script.sh
# 赋予权限
$ chmod a+x script.sh
$ ./script.sh
命令定界符
命令或命令序列使用分号或换行符分隔
$ cmd1 ; cmd2
# 相当于 这是注释
$ cmd1
$ cmd2
Bx 终端打印
echo
命令
$ echo "test"
test
shell比较灵活,可以单引号,双引号,也可以不需要。
printf
命令
主要用于格式化,printf不会自动添加换行符。
$ printf "hello world"
转义
有时候,我们使用的字符属于命令,我们需要转义。
使用echo -e 来转义
$ echo -e "1\t2\t3\!"
颜色
$ echo -e "\e[1;31m This is red text \e[0m"
Cx 变量与环境变量
变量是各种语言最基本的部分。本质把数据存放与一个内存中,某段内存就称为变量。
那么,何为环境变量,它与变量有什么关系呢?
环境变量到底是不是变量呢?
就一下问题,我们稍后继续探讨。
休息一下,马上回来
/// 娱乐内容 ↓↓↓
老师要求我们写99乘法表,怎么办?
别急,一行解决
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
好,回到我们的变量。
有一些特殊的变量会被shell环境和操作系统环境用来存储一些特别的值,这类变量就算环境变量。
来看看查看进程的环境变量
$ cat /proc/PID/environ
a 变量赋值
var=value
var="this a value"
var是变量名,value是值。如果值有空格,则需要使用引号。
shell的值都以字符串形式存储,不需要声明变量类型。
b 打印变量
在变量名的前面加上$就可以打印
echo $var
echo ${var}
输出PATH变量
$ echo $PATH
export命令是用于设置环境变量。
c 增加环境变量
$ PATH="$PATH:/home/user/bin"
$ export PATH
# or
$ export PATH="$PATH:/home/user/bin"
一些常见的环境变量有:HOME\PWD\USER\UID\SHELL..
d 应用
1.获取字符串长度
length=${#var}
$ var=12345673\$
echo ${#var}
2.识别当前所在shell
$ echo $SHELL
# or
$ echo $0
3.检查是否为超级用户
根据ROOT用户的UID为0,我们可以自己为
if [ $UID -ne 0 ]; then
echo Non root user. Please run as root.
else
echo Root user
fi
4.修改Bash提示字符串
查看PS1的变量
设置
\u
用户名\h
主机名\w
工作目录
Dx 使用函数添加环境变量
例如
export PATH=/opt/myapp/bin:$PATH
export LD_LIBRARY_PATH=/opt/myapp/lib;$LD_LIBRARY_PATH
可以使用函数实现
prepend() { [ -d $2 ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ;}
prepend PATH /opt/myapp/bin
prepend LD_LIBRARY_PATH /opt/myapp/lib
Ex 使用shell进行数学运算
a let命令
let命令可以进行基本的算术操作。
root@Enomothem:~# num1=4
root@Enomothem:~# num2=2
root@Enomothem:~# let count=num1+num2
root@Enomothem:~# echo $count
6
自增、自减,以及简写与C语言类似:
$ let no1++
$ let no1--
$ let no1+=6
$ let no1-=6
b 操作符[]、(())
root@Enomothem:~# num1=4
root@Enomothem:~# num2=2
root@Enomothem:~# result=$[ num1 + $num2 ] # 里面的变量$符号可要可不要
root@Enomothem:~# echo $result
6
root@Enomothem:~# conclusion=$(( num1 + 51 ))
root@Enomothem:~# echo $conclusion55
c expr方法
root@Enomothem:~# result=` expr 3 + 4`
root@Enomothem:~# echo result
result
root@Enomothem:~# echo $result
7
注意,对空格很敏感。
以上三种方法都不支持浮点数,所以下面我们将介绍一个数学工具。
d bc数学运算高级工具
1.基本运算
使用定界符,通过stdin传递给bc
root@Enomothem:~# echo "4 * 0.35" | bc
1.40
root@Enomothem:~# no=43;
root@Enomothem:~# result=`echo "$no * 1.5" | bc`
root@Enomothem:~# echo $result
64.5
2.设置小数精度
使用参数scale=2,将小数点个数设置为2.
root@Enomothem:~# echo "scale=2;8/2" | bc
4.00
3.进制转换
bc可以进行进制转换 ibase意思就是inputbase,输入的进制是什么,obase为输出的进制,output,输入的进制默认为十进制,所以不需要写,第二个需要注明是二进制。
root@Enomothem:~# no=100
root@Enomothem:~# echo "obase=2;$no" | bc
1100100
root@Enomothem:~# no=1100100
root@Enomothem:~# echo "obase=10;ibase=2;$no" | bc
100
4.平方根和平方
root@Enomothem:~# echo "sqrt(100)" | bc
10
root@Enomothem:~# echo "10^10" | bc
10000000000
Fx 文件描述符与重定向
a 重定向
- 0 ——
stdin
标志输入 - 1 ——
stdout
标准输出 - 2 ——
stderr
标准错误
看到这里,我终于弄明白了C语言里那个stdio是什么意思,原来就是标准输入输出的合体啊。
顿悟。
>和>>并不相同,>会先把内容清空,再写入。
root@Enomothem:/opt/shell/cdx# echo "This is a sample text 1" > temp.txt
root@Enomothem:/opt/shell/cdx# echo "This is a sample text 2" >> temp.txt # 这是追加内容
root@Enomothem:/opt/shell/cdx# cat temp.txt
This is a sample text 1
This is a sample text 2
那么,错误的信息呢,就是我们平时把命令打错了的时候时显示的,像这样
root@Enomothem:/opt/shell/cdx# efawhoiefhawioe
efawhoiefhawioe: command not found
这就是标准错误,返回值为2.
我们可以使用重定向,将stderr(标准错误)重定向到一个文本里面。
root@Enomothem:/opt/shell/cdx# enomothem 2>> temp.txt
root@Enomothem:/opt/shell/cdx# cat temp.txt
This is a sample text 1
This is a sample text 2
enomothem: command not found
重定向追加到了temp文本文件中,最后一行就是标准错误的输出。
那么1是什么呢,其实我们一遍默认就是1>,为标准输出,加和不加都一样。
1.使标准输出和标准错误分开输出
root # enomothem 2>stderr.txt 1>stdout.txt
2.使标准错误转为标准输出
root # enomothem &> output.txt
3.不要标准错误
root # cat enomothem 2>/dev/null
/dev/null是一个特殊是设备文件,接收的任何数据都会被丢弃,凡是到这里的数据都一去不复返,被称为黑洞。
文本 > 文件
终于领悟了,为什么Linux里面要把文件称为文本文件,文件就是内含文本的文件。
所以,我们将文本重定向到了文件中,但是这样的话,我们就没法将文本传递给下一个命令了,这时候,我们就要用到下面这个命令。
从文件中重定向到命令
$ cmd < file
b tee命令
tee命令用于读取标准输入stdin
的数据,并将其内容输出成文件。
我们使用tee命令将数据重定向到文件,还提供一份副本用于后续命令的stdin。
root@Enomothem:/opt/shell/cdx# cat a* | tee out.txt | cat -n
# root@Enomothem:/opt/shell/cdx# cat a* | tee -a out.txt | cat -n # 如果加 -a 则为追加到文件否则会覆盖。
1 a1
2 a1
3 a1
root@Enomothem:/opt/shell/cdx# cat out.txt
a1
a1
a1
分析:实现,运行cat命令,查找a开头的文本文件,然后将数据利用管道符输出到tee命令变为输入数据,一份输入到了out.txt,复制一份通过管道符变为cat命令的参数。
1.使stdin作为命令的参数,代替文件名加一个 - 就行了
也可以使用/dev/stdin代替stdin
root@Enomothem:/opt/shell/cdx# echo who is enomothem | tee /dev/stdout
who is enomothem
who is enomothem
root@Enomothem:/opt/shell/cdx# echo who is enomothem | tee /dev/stderr
who is enomothem
who is enomothem
root@Enomothem:/opt/shell/cdx# echo who is enomothem | tee -
who is enomothem
不知道为什么,一使用/dev/stdin
就疯狂输出,停不下来。
Conclusion:
从stdin读取输入的命令能以多种方式接收数据,可以用cat和管道来指定我们自己的文件描述符
文件描述符 | 描述 |
---|---|
< | 从文件中读取stdin |
> | 截断模式的文件写入 |
>> | 追加模式的文件写入 |
下面,我们DIY文件描述符:
a exec命令的使用
创建并使用一个文件读取
Enomothem:/opt/shell/cdx$ echo this is a test line > input.txt
Enomothem:/opt/shell/cdx$ exec 3<input.txt
Enomothem:/opt/shell/cdx$ cat <&3
this is a test line
我们会发现,只能使用一次。
创建并使用一个截断模式的写入
root@Enomothem:/opt/shell/cdx# exec 4>output.txt
root@Enomothem:/opt/shell/cdx# echo newline >&4
root@Enomothem:/opt/shell/cdx# cat output.txt
newline
创建并使用一个追加模式的写入
root@Enomothem:/opt/shell/cdx# exec 5>>input.txt
root@Enomothem:/opt/shell/cdx# echo appended line >&5
root@Enomothem:/opt/shell/cdx# cat input.txt
this is a test line
appended line
Gx 数组和关联数组
与其它语言一样,shell也是支持数组的,Bash从4.0版本引入了关联数组。
a 数组
# 定义数组
array_var=(1 2 3 4 5 6)
array_var[0]="2"
# 输出数组
echo ${array_var[0]}
echo ${array_var[*]} # 也可以用@
# 输出个数
echo ${#array_var[*]}
b 关联数组
# 定义关联数组
declare -A arrays
# 赋值
arrays=([apple]='1000yuan' [orange]='150yuan')
echo Apple is ${arrays[apple]}
# 列出全部
echo ${!arrays[*]}
Hx 使用别名
a alias命令
alias 别名=‘命令 [format]’
这只是暂时性的,关闭终端后即失效。
b 使命令永久化
将命令放入~/.bashrc
$ echo 'alias cmd="command seq"' >> ~/.bashrc
c 删除别名unalias
$ unalias bieming=
d 给rm改名
alias rm='cp $@ ~/backup && rm $@'
删除原始文件并在backup中保留副本
注意:新的别名会取代别的相同的别名。
安全思考:如何防止别名的滥用呢,我们可以在命令前面加一个\,用于转义。
可以看到,别名无法通过转义的命令执行。
Ix 获取终端信息
a 获取终端行数和列数
tput cols
tput lines
b 打印当前终端名
tput longname
但是没有换行,可以加一个echo 来换行
c 将光标移动到坐标(xx,xxx)处
这个实在是太有趣了
tput cup xxx xxx
d 设置终端背景颜色
tputsetb n # n在0-7之间
e 设置文本样式为粗体
tput bold
f 设置下划线的起止
tput smul
tput rmul
g 删除从当前光标位置到行尾的所有内容
tputed
这个命令我shell中没有
h 输入密码不显示密码
#! /bin/bash
echo -e "Enter password:"
stty -echo
read password
stty echo
echo echo Password read.
-echo 的意思就是禁止将内容发送到终端。
在这里我是输入了密码的,无显示。
Jx 获取、设置日期和延时
纪元时被定义为世界标准时间1970年1月1日0时0分起至当前的总秒数,不包括闰秒。
为什么要延时,用于延时运行程序,监视。
日期内容 | 格式 |
---|---|
星期 | %a or %A |
月 | %b or %B |
日 | %d |
固定格式日期 | %D |
年 | %Y or %y |
小时 | %I or %H |
秒 | %S |
纳秒 | %N |
分钟 | %M |
Unix纪元时 | %s |
a 读取时间
date
b 打印纪元时
date +%s # 打印纪元时
date --date "Sat 23 Jan 2021 10:59:57 PM CST" +%s # 转为纪元时
c 用格式组合打印
d 设置日期和时间
date -s "格式化的日期字符串"
例如
date -s “21 June 1999 11:01:22”
e 检查一组命令所需的时间
#! /bin/bash
start=$(date +%s)
ls;
pwd;
end=$(date +$s)
difference=$(( end - start))
echo Time taken to execute commands is $difference seconds.
f 在脚本中生成延时
利用sleep命令
#!/bin/bash
echo -n Count:
tput sc
count=0;
while true;
do
if [ $count -lt 4 ];
then
let count++;
sleep 1;
tput rc
tput ed
echo -n $count;
else exit 0;
fi
done
Kx 调试脚本
a 调试功能 -x
sh -x
bash -x
跟踪调试功能,可以打印出执行每一条命令的状态。
b 使用set脚本进行部分调试
#! /bin/bash
for i in {1 6};
do
set -x
echo $i
set +x
down
echo "Script executed"
只对set -x set +x 内的代码进行调试。
Lx 函数和参数
a 定义函数
function fname()
{
statements;
}
or
fname()
{
statements;
}
b 调用函数
使用函数的名字就行。
$ fname;
c 传参
$ fname arg1 arg2;
Mx 将命令序列的输出读入变量
stdin用于输入,stdout用于输出
组合多个命令,这些命令称为filter,使用pipe连接每个filter,pipe(|)
$ cmd1 | cmd2 | cmd3
命令cmd1的输出传递给命令2,命令2的输出传递给命令3将被打印或输出到某个文件。
例如
$ ls | cat -n > out.txt
ls命令输出后传递给cat命令加上行号,然后将输出重定向到文件中。
a 读入变量
1.子shell
什么是子shell,例如
output=$(ls | cat -n)
echo $output
2.反引用
什么是反引用,或叫反标记,这种方法也可以存储命令的输出
output=`ls | cat -n`
echo $output
反引用位于键盘上的~
键。
b 利用子shell生成一个独立的进程
pwd;
(cd /bin; ls);
pwd;
这个括号里的子命令并不会改变主shell的目录,只是存在于子shell中。
c 通过引用子shell的方式保留空格和换行符
可以将输出的东西用引号引起来。
比如我们一个文件中有三个数字,我们同时使用引号和不适用引号来对比以下。
out=$(cat txt)
echo $out
1 2 3
out="$(cat txt)"
echo $out
1
2
3
但我的shell没有成功,可能是因为版本问题。
Nx 不使用回车键来读取n个字符
a read命令
输入n个字符存入变量
b 无回显的方法 -s
c 提示内容 -p
read -p "Enter input:" var
d 限定时间 -t
在规定时间内读取输入,看看我2秒能输入多少个字符,嘿嘿
e 定界符 -d
我们可以自定义自己的定界符
比如,我们使用星*作为定界符,这样就不需要按回车啦
Ox 运行命令直至执行成功
熟话说得好,超过三次以上的重复工作的手工事情,就要考虑自动化了。
比如,我们荣耀水晶,一两次哪有那么容易,所以,我们如果写一个shell脚本,一直重复我们抽奖的动作,再附加一个购买次数的操作,我们只需充钱,就能自动化等待你的大奖被抽到了。
当然,这只是举个例子。
其实,在生活中,无处不在的重复性工作,我们可以实现机械自动化,程序自动化,这是科技带给我们最好的便利。
定义一个重复工作的函数
repeat(){
while true
do
$@ && return
done
}
or
repeat() { while :; do $@ && return; done }
例如,我们要在网上下载一个文件,不过这个文件要等待一会儿才能下,我们改造repeat函数,增加延时
repeat() { while :; do $@ && return; sleep 30; done }
Px 字段分隔符和迭代器
a 分隔符
IFS是目前shell的默认定界字符串,为空白字符(换行符、空格、制表符)
如果我们需要使用逗号作为分隔符,我们就需要使用IFS=“,”
b 迭代器
1.for循环
for var in list;
do
commands;
done
2.while循环
用true作为循环条件的话,可以无限循环。
for i in {a..z}; do actions; done;
# 或者与C语言类似
for((i=0;i<10;i++))
{
commands;
}
3.until循环
一直循环,直到为真。
x=0;
until [ $s -eq 9 ];
do
let x++; echo $x;
done
Qx 比较与测试
&& 逻辑与运算符
|| 逻辑或运算符
[condition] && action; # 如果condition成立,则执行action。
[condition] || action; # 如果condition为假,则执行action。
a if条件
if condition;
then
commands;
fi
b else if 和else
if condition;
then
commands;
else if condition; then
commands;
else
commands;
fi
c 算术比较
-eq:等于
-ne:不等于
-gt: 大于
-lt:小于
-ge:大于或等于
-le:小于或等于
用中括号[]将条件封闭起来
[ $var -eq 0 ] # 当var等于0,返回真
[ $var -ne 0 ] # 当var不等于0,返回真
条件可以结合!!
比如
[ $var1 -ne 0 -a $var2 -gt 2 ] # 使用逻辑与 -a
[ $var1 -ne 0 -o $var2 -gt 2 ] # 使用逻辑或 -o
参数 | 内容 |
---|---|
-f | 如果给定的变量包含正常的文件路径或文件名,则返回真 |
-x | 如果给定的变量包含的文件可执行,则返回真 |
-d | 如果给定的变量包含的是目录,则返回真 |
-e | 如果给定的变量包含的文件存在,则返回真 |
-c | 如果给定的变量包含的是一个字符设备的路径,则返回真 |
-b | 如果给定的变量包含的是一个块设备文件的路径,则返回真 |
-w | 如果给定的变量包含的文件可写,则返回真 |
-r | 如果给定的变量包含的文件可读,则返回真 |
-L | 如果给定的变量包含的是一个符号链接,则返回真 |
例如,我们写一个判断/etc/passwd路径是否存在的脚本
d 字符串比较
数字之间的关系再复杂也没有人与人之间的关系复杂。
关系表达式
关系运算符
等于、不等于 == 、!=
大于、小于 >、<
大于等于、大于等于 >= 、<=
如此一般简单,多好。
也就是说关系只有两种可能
真的
假的
也就是非0即1
人之间的关系看似真实,有时却虚假,有时看似平淡,实则复杂。
——引于学C语言关系运算符时悟出的C精神笔记
使用字符串比较时,我们最好使用双中括号,避免错误。
[[ $str1 = $str2 ]] # 相等时,返回真, 在等于前面加个!就是不等于了
[[ $str1 == $str2 ]] # 这是另一种写法
比较字符串的大小,实际上是比较字符串的字母序列。
使用逻辑运算符&&、||很容易的就可以将条件结合起来
也可以使用test命令执行条件检测,可以避免很多括号。
使用test命令
Xx 补充内容
a 在shell中查看天气预报
curl http://wttr.in
b scp命令-远程对拷文件
secure copy 远程拷贝文件的意思
1.本地拷贝到远端
1、目录拷贝:
scp -r ./ 用户名@ip:path
2、文件拷贝
scp ./ 用户名@ip:path
2.下载远端文件
scp -r name@ip:path ./
-r 是目录的意思哦
如果不写用户名,则会提示输入用户名和密码。
ESx 关注ES
关注永恒之锋公众微信号,加入星球和我们一起探讨吧
微信公众号
星球
网站
// 战队暂未建设网站