Getting started shell learning


对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 ]] # 这是另一种写法
  1. 比较字符串的大小,实际上是比较字符串的字母序列。

  2. 使用逻辑运算符&&、||很容易的就可以将条件结合起来

  3. 也可以使用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

关注永恒之锋公众微信号,加入星球和我们一起探讨吧

微信公众号

永恒之锋微信公众号

星球

永恒之锋星球

网站

// 战队暂未建设网站


文章作者: Enomothem
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Enomothem !
  目录