本文来自《鸟哥的Linux私房菜》学习以及知识点整理,仅供学习使用
一、查看系统合法的shell
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
如上就是当前系统支持的所有可用的shell版本。Linux默认使用的是bash,sh也被bash替换了。
二、bash的优点
历史命令
有没有发现在命令行通过上下方向键就可以快速找到之前输入的命令?这是因为bash会记录所有的历史命令,当前登录操作的命令会放在内存中,在你注销后,会保存到你的home目录中的 .bash_history中。输入history命令就可以查看历史输入的命令了
文件补全
按Tab就可以轻松补全你的命令或文件名
别名
[root@localhost ~]# alias la='ls -a'
[root@localhost ~]# la
. anaconda-ks.cfg .bash_logout .bashrc .tcshrc testtxt
.. .bash_history .bash_profile .cshrc testdir .viminfo
通过alias命令就可以给某个命令设置别名使用
通过unalias xx
就可以移除xx别名。
当前这样设置只是在当前会话,换一个shell会话就不起作用了,想要起作用就要配置在当前用户下:
在里面配置下就可以了
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
另外还有任务管理、前后台控制等,后面会讲,以及非常重要的shell脚本。
三、查看命令类型type
语法:
type [-tpa] name
-t: 按下面显示出name的类型:
file: 外部命令
alias: 为别名命令
builtin: 内置命令
-p: name为外部命令时才会显示完整文件名
-a: 把PATH变量定义的路径中所有包含name的命令都显示出来
四、命令执行、删除快捷键
-
有没有命令特别长,想换行发现回车吧唧就执行的时候呢?
试试
\enter
?反斜杠可以将回车转译,这样就可以进行换行继续写命令 -
快速删除命令
ctrl +u
: 删除光标前面的命令ctrl + k
: 删除光标后面的命令ctrl + a
:光标移动到命令最前面ctrl + e
:光标移动到最后面
五、变量
快速输入定义和输出变量
[root@localhost ~]# name=xiaoyunshi
[root@localhost ~]# echo name
name
[root@localhost ~]# echo $name
xiaoyunshi
[root@localhost ~]# echo ${name}
xiaoyunshi
name=xiaoyunshi
这样就定义了一个name变量,值为xiaoyunshi。
可以发现,直接echo name
是把name当做字符输出。
而输入变量需要通过==$变量
或${变量}
==的方式输出。
5.1变量规则
-
使用
=
连接变量与内容name=xiaoyunshi
-
变量两边不留空格,有空格的内容需用单引号引起来==【单引号内的内容为普通字符】==
[root@localhost ~]# name = x -bash: name: 未找到命令 [root@localhost ~]# name= x -bash: x: 未找到命令 [root@localhost ~]# name=x y -bash: y: 未找到命令 [root@localhost ~]# name='x y' [root@localhost ~]# echo $name x y
-
双引号也可以引用字符串,但是双引号内会保留特殊字符(如$)
[root@localhost ~]# echo "$name" x y # 双引号输出了name这个变量 [root@localhost ~]# echo '$name' $name # 单引号输出了原字符 [root@localhost ~]# echo "\$name" $name # 转义一下就可以输出原字符了
因此遇到 【$、空格、\、回车、单引号】等特殊字符,需要使用 \ 进行转义
-
反单引号【``】或$()提供语句赋值
[root@localhost ~]# ls `cd ~` anaconda-ks.cfg testdir testtxt [root@localhost a]# for file in $(ls ./); do echo ${file}; done; anaconda-ks.cfg awk_script cal_new.txt cal.patch cal.txt dir_context last.txt regex_txt regular_express.txt
即,会先执行反单引号或括号内的命令,将结果作为输入,执行外部命令
-
增加变量内容使用$变量名
或${变量名}[root@localhost ~]# name=xiaoyunshi [root@localhost ~]# name=${name}:xiaoyunshi_111 [root@localhost ~]# echo $name xiaoyunshi:xiaoyunshi_111 [root@localhost ~]# name=$name:_222 [root@localhost ~]# echo $name xiaoyunshi:xiaoyunshi_111:_222
${name}冒号后面的就是给name变量加的内容
-
使用export让变量成为环境变量
如果想在子程序拿到变量,则必须让其成为环境变量
[root@localhost ~]# name=xiaoyunshi [root@localhost ~]# echo $name xiaoyunshi # 开启一个子程序 [root@localhost ~]# bash # 输出name,发现没有 [root@localhost ~]# echo $name # 退出子程序 [root@localhost ~]# exit exit # 让name成为环境变量 [root@localhost ~]# export name [root@localhost ~]# bash # 重新进入子程序可以输出该变量了 [root@localhost ~]# echo $name xiaoyunshi
通常把系统变量设置为大写字符,自定义变量为小写字符
-
删除变量
unset xxx
$后的括号
$(cmd)
小括号内为子命令,相当于 两个单引号``,小括号内可有多个命令:`${cmd1,cmd2},子命令是在新的子bash中执行
$((expression))
最里面括号用于执行数学计算,如:
a=1
b=2
c=$(($a+$b))
echo $c
${cmd1,cmd2…}
与单个小括号一样,只不过这里的子命令是在当前bash执行,不是新的子bash中执行
这样在命令行设置的变量也只能当前会话用,否则应该在~/bashrc内配置好命令:
............
name="wangmaolin"
............
这样就可以真正的定义到你的机器里了
5.2 环境变量功能
env
这里主要介绍环境变量的查看方法与主要的环境变量的功能。
查看环境变量可以通过env
命令或export
命令,但是export还有其他功能,如将自定义变量导出为环境变量。
[root@localhost ~]# env
XDG_SESSION_ID=47
HOSTNAME=localhost.localdomain # 主机名
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash # 当前环境使用的shell是哪个程序
HISTSIZE=1000 # 历史命令最大数量
SSH_CLIENT=172.16.117.1 65160 22
SELINUX_USE_CURRENT_RANGE=
SSH_TTY=/dev/pts/1
USER=root # 当前用户
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 执行文件查找的路径
MAIL=/var/spool/mail/root # 邮箱文件
PWD=/root
LANG=zh_CN.UTF-8 # 当前环境的语系
HOME=/root # 当前用户的根目录
SSH_CONNECTION=172.16.117.1 65160 172.16.117.5 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env
另外还有RANDOM变量,可以输出0~32767之间的数值:
[wangmaolin@localhost ~]$ echo $RANDOM
11454
set
set可以查看环境变量+自定义变量,以及一些和bash操作解密奥有关的变量。
[root@localhost ~]# set
BASH=/usr/bin/bash
BASH_VERSINFO=([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.2.46(2)-release' # bash版本
COLUMNS=238 # 当前终端环境使用的栏位长度
HISTFILE=/root/.bash_history # history文件
HISTFILESIZE=1000 # 上面文件的最大命令数量
HISTSIZE=1000 # 内存中历史命令的最大数量
HOME=/root
HOSTNAME=localhost.localdomain
HOSTTYPE=x86_64 # 64位操作系统
IFS=$' \t\n' # 这个就是当前系统的默认分隔符
LINES=57 # 当前终端下的最大行数
MACHTYPE=x86_64-redhat-linux-gnu # 机器类型
OSTYPE=linux-gnu # 操作系统类型
PS1='[\u@\h \W]\$ '
PS2='> '
PS4='+ '
#这是自定义变量
name=xiaoyunshi
-
PS1
该变量是对提示字符的设置,即
[root@localhost ~]#
命令行前的这个提示符,其参数如下:-
\d
【星期 月 日】 如:【Mon Feb 3】
-
\H
完整主机名,即上面的HOSTNAME变量值
-
\h
简短主机名,即完整主机名的第一个小数点前面的值
-
\t | \T | \A | @
都是时间格式:
\t: 24小时制 【HH:MM:SS】 \T: 12 小时制 【HH:MM:SS】 \A: 24小时制 【HH:MM】 \@: 12小时格式 【am/pm】
-
\u
当前用户账号名
-
\v
bash版本信息
-
\w
完整工作目录名【从根目录开始】
-
\W
当前工作目录名【basename】
-
\#
当前终端执行的命令数
-
/$
root用户提示 # ,其他用户提示 $
-
-
$
[root@localhost local]# echo $$ 7158
该变量输出当前shell的进程号PID
-
?
输出上个命令的结果。结果正确返回0,不正确返回对应的错误码
[root@localhost local]# echoe bash: echoe: 未找到命令 # 因为上个命令出错,因此返回错误码 [root@localhost local]# echo $? 127 [root@localhost local]# echo a a # 上个命令正确,输出0 [root@localhost local]# echo $? 0
-
export
将某个变量转为环境变量
前面输入bash命令后就会进入到子进程,当进入到子进程,相当于开辟了一个新的内存区域,会自动将父进程的环境变量所在内存区域导入到自己的环境变量区块中,因此如果不是环境变量,无法在子进程调用父进程的变量,而想调用就必须通过
export variable
将其转为环境变量。
5.3 read读取键盘输入变量
该命令允许读取键盘输入的变量
# 参数
-p : 接提示文件
-t : 接等待秒数
示例:
[root@localhost local]# read -p 快在20s内输入变量名 -t 20 testread
快在20s内输入变量名90
[root@localhost local]# echo $testread
90
5.4 变量类型与变量声明
declare
声明变量类型
declare [-下面的参数] 变量名
-a : 声明为数组类型
-i : ...整数类型
-x : 同export,声明为环境变量
-r : 声明为只读变量 或 使用readonly命令
示例:
5.4.1数组
# 声明并赋值
arr(val1 val2 val3 ... valN)
# 或直接给指定索引赋值
arr[0]=val1
arr[1]=val2
#输出单个元素
echo ${arr[0]}
# 输出所有元素
echo ${arr[@]}
#获取数组长度
len=${#arr[@或*]}
示例:
[root@localhost ~]# arr=(1 2 3 4)
[root@localhost ~]# echo ${arr[2]}
3
[root@localhost ~]# echo ${arr[@]}
1 2 3 4
也可以先用declare -a
声明一个数组
# 数组示例
[root@localhost local]# declare -a arr
# 给指定位置赋值
[root@localhost local]# arr[0]=1
[root@localhost local]# arr[1]=2
[root@localhost local]# echo ${arr[1]}
2
[root@localhost local]# arr[2]=2
# 输出
[root@localhost local]# echo ${arr[1]},${arr[2]},${arr[0]}
2,2,1
5.4.2 整数:
# 不声明为整数默认为字符串,无法执行计算
[root@localhost local]# sum=1+2
[root@localhost local]# echo $sum
1+2
# 声明为整数
[root@localhost local]# declare -i sum
[root@localhost local]# sum=1+2
[root@localhost local]# echo $sum
3 # 返回计算结果
5.4.3只读:
[root@localhost local]# declare -r read
[root@localhost local]# read=1
bash: read: 只读变量 # 无法赋值
[root@localhost ~]# readonly name
[root@localhost ~]# name=1
-bash: name: 只读变量
当然,一般要赋值好变量,然后再进行只读声明,或声明的同时赋值变量
5.4.4 字符串
[root@localhost ~]# str="abc"
字符串长度
[root@localhost ~]# echo ${#str}
3
5.5 ulimit
该命令和文件系统和程序的限制有关。
bash默认会限制系统资源的相关数值,如可建立的最大文件容量等。
语法为:
ulimit [-下面参数] [配额]
-H : 严格设置,不能超过当前配置
-S : 软设置,超过该值仅进行警告
-a : 查看当前所有限制额度
-c : 单个内核文件最大容量【程序出错时系统可能会将其在内存中的信息写入文件,该文件就是内核文件】
-f : 当前shell可以建议的最大容量 单位为Kbytes
-d : 程序可食用的最大段内存容量
-l : 可用于锁定的内存量
-t : 可使用的最大的CPU时间 【秒】
-u : 单个使用者可使用的最大进程数
查看所有参数额度:
[root@localhost ~]# ulimit -a
core file size (blocks, -c) 0 # 0就是无限制
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7183
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024 # 可同时开启的文件数
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7183
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
5.6 变量的删除与替换
5.6.1 变量的删除
语法:
variable=${variable#通配符+关键字}
整个语法由三部分组成,首先是要删除的变量名variable,没什么说的
接着是 # 、##、%、%%四个选项:
最后是通配符匹配关键字。
示例如下:
[root@localhost ~]# path=${PATH}
[root@localhost ~]# echo $path
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
#删除以/开始,以bin:结尾的所有文字,中间的*为通配符,替换0到多个字符【正则知识】
[root@localhost ~]# echo ${path#/*bin:}
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 这样就把第一个符合要求的/usr/local/sbin:删除掉了,当前这里我是输出删除后的结果,直接让path=这个结果就相当于删除了
# 删除所有上面规则匹配到的文字
[root@localhost ~]# echo ${path##/*bin:}
/root/bin # 这样就只剩最后一个了
% 就是反向操作,就不演示了、
5.6.2 替换
替换语法为:
${variable/old/new}
将变量variable中old替换为new【只匹配第一个进行替换】
${variable//old/new}
【匹配所有进行替换】
5.6.3 替换时添加测试
即,当变量存在时不替换,变量不存在时替换为给定的值。
语法:
variable=${variable-newValue}
:如果变量variable存在【包括空串】,则等于原值,否则赋值为newValue
variable=${variable:-newValue}
:这里同上,但是如果是空串视为不存在,将变量赋为新值
当然除了-
符号,还有 +
、=
、?
等,功能差不多或相反,可以自己了解下。
[root@localhost ~]# testv=1
[root@localhost ~]# testv=${testv-2}
[root@localhost ~]# echo $testv
1 # testv有值,因此不替换
[root@localhost ~]# unset testv
[root@localhost ~]# testv=${testv-2}
[root@localhost ~]# echo $testv
2 # testv不存在,因此赋新值2
# testv 赋值为空串
[root@localhost ~]# testv=
[root@localhost ~]# echo $testv
# 再次执行,替换失败
[root@localhost ~]# testv=${testv-2}
[root@localhost ~]# echo $testv
# 使用:- 替换成功
[root@localhost ~]# testv=${testv:-2}
[root@localhost ~]# echo $testv
2
六、Base shell的操作环境
6.1 路径与命令查找顺序
当执行一个命令时,其按照以下顺序执行:
- 以相对或绝对路径执行命令
- 通过别名alias找到该命令来执行
- 由内置(buildin)的bash命令执行
- 通过**$PATH**变量的顺序查找到第一个命令来执行
如,执行ls时有颜色,而执行/bin/ls
时没有颜色,这是因为ls会执行其别名ls --color=auto
命令。
[root@iZbp1fki8n8k5f1j3acuhzZ ~]# ls
my my1 test test.txt # 带颜色的
[root@iZbp1fki8n8k5f1j3acuhzZ ~]# /bin/ls
my my1 test test.txt
可通过type -a
查看命令执行顺序:
[root@iZbp1fki8n8k5f1j3acuhzZ ~]# type -a ls
ls 是 `ls --color=auto' 的别名
ls 是 /usr/bin/ls
6.2 bash的环境配置文件
一般登录情况下,只会读取以下两个配置文件:
-
/etc/profile
系统配置文件(最好不要修改)
-
~/.bash_profile | ~/.bash_login | ~/.profile
当前用户的设置,一般我们安装的一些软件的环境变量都在这里配置,如JDK、NODE、MAVEN等
6.2.1 /etc/profile
设置了系统整体的配置,所有用户登录了都会读取。
同时还会去调用以下配置文件:
-
/etc/profile.d/*.sh
/etc/profile.d目录下的以sh为后缀的文件都会被调用。centos7中在该目录下规范了ll、ls、vi、which等命令别名。用于为所有用户建立共享命令别名等。
-
/etc/locale.conf
由
/etc/profile.d/lang.sh
调用 -
/usr/share/bash-completion/completions/*
Tab的相关功能就是从该目录里找到对应的命令进行处理。
6.2.2 ~/.bash_profile
bash读取完/etc/profile并调用完其他配置后,最后就会调用用户的个人配置文件,同时包括以下三个:
- ~/.bash_profile
- ~/.bash_login
- ~/.profile
这三个文件按照顺序,只会读取一个,上一个不存在才会读取下一个。
bash_profile的默认内容如下:
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
这里会看有没有~/.bashrc文件,有的话还会读取该文件。
另外,PATH变量在/etc/profile中设置了,这里就用了前面讲到的添加变量的方式,给PATH变量添加额外的值。
如,我们要添加MAVEN变量:
export MAVEN_HOME=/Users/xxx/software/maven/apache-maven-3.6.1
export PATH=$PATH:$MAVEN_HOME/bin
就可以这样导出MAVEN_HOME变量到环境变量并作为额外的执行文件目录,这样就可以直接执行该目录下的命令,无需知道位置。
source
该命令用于读取配置文件的命令,一般重新编辑了配置文件,想要生效,就要使用该命令进行刷新读取。
6.2.3 ~/.bashrc
该配置文件除了刚刚说的会由/.bash_profile在登录情况下读取外,在非登录状态下,仅会读取/.bashrc文件。
该文件默认配置了rm/cp/mv的别名,以及读取/etc/bashrc
文件,/etc/bashrc
会定义以下内容:
- 根据UID设置umask值
- 根据uID设置提示符
- 调用/etc/profile.d/*.sh的设置
6.2.4 其他配置文件
-
~/.bash_logout
记录了【注销bash后,系统会做的操作】,我装的这个版本好像啥都没做,不过可以把一些重要的容易忘的操作写到这里,这样在注销后就可以自动帮你处理。
6.3 特殊符号
下面列举比较重要的特殊符号:
符号 | 意义 |
---|---|
# | 注释用 |
\ | 转义 |
| | 管道 |
; | 用于多个命令分隔 |
& | 任务管理;将命令变成后台任务 |
>、>> | 数据流重定向: 输出定向【替换】与【累加】 |
<、<< | 输入定向 |
() | 用于执行子shell命令 |
{} | 命令区块组合 |
‘’ | 不可变量替换 |
“” | 可以进行变量替换 |
`` | ` 之间的命令会先执行,将结果作为外部命令的输入 |
七、数据流重定向
数据流重定向就是把标准输出与标准错误输出传输到另一个地方。
7.1 标准输出与标准错误输出 > 、 >>、2>、2>>
-
标准输出:> 或 >>
一般输入命令,反馈到屏幕上的信息就是标准输出。代码:1,使用 > 或 >> 【与 1> 或 1>> 相同,没有数字默认为1】
-
标准错误输出: 2> 或 2>>
输入命令,如执行错误,反馈到出来的错误信息就是标准错误输出。 代码:2,使用 2> 或 2>> 【没有空格】
>就是将标准输出重定向到指定的文件或设备,且每次都会替换指定文件或设备的内容。
>>将标准输出重定向到指定的文件或设备,但是是以累加的方式写到指定文件或设备
例1:将标准输出重定向到指定文件【>】
# 把ls当前目录的结果输出到out文件
[root@localhost testdir]# ls > out
[root@localhost testdir]# cat out
out
txt
# 将ls上级目录并输出到out文件
[root@localhost testdir]# ls ../ > out
[root@localhost testdir]# cat out
anaconda-ks.cfg
testdir
以上,可以看到>符号每次都会替换指定文件内容。
例2:将标准输出重定向到指定文件【>>覆盖的方式】
[root@localhost testdir]# ls >>out
[root@localhost testdir]# cat out
anaconda-ks.cfg
testdir
out
txt
以上,ls当前文件,输出结果重定向到out文件,最终内容是累加了例1的结果。
例3:标准错误输出重定向到指定文件【2> 或 2>>】
# 当前目录没有haha因此会将错误输出到error文件【如果没有错误,则不会写入任何信息到error,但会创建error文件】
[root@localhost testdir]# ls haha 2> error
[root@localhost testdir]# cat error
ls: 无法访问haha: 没有那个文件或目录
# 追加的方式写入标准错误输出
[root@localhost testdir]# ls lala 2>> error
[root@localhost testdir]# cat error
ls: 无法访问haha: 没有那个文件或目录
ls: 无法访问lala: 没有那个文件或目录
例4:将标准输出和标准错误谁出分别重定向到指定的不同文件
# ls 当前目录和baga目录,并将成功的数据输出到success文件,错误的信息输出到error文件
[root@localhost testdir]# ls ./ baga > success 2> error
[root@localhost testdir]# cat error
ls: 无法访问baga: 没有那个文件或目录
[root@localhost testdir]# cat success
./:
error
out
success
txt
可以看到成功和错误信息到了不同文件。
例5:正确和错误输出到输出到同一个文件
[root@localhost testdir]# ls ./ baga > merge 2>&1
[root@localhost testdir]# cat merge
ls: 无法访问baga: 没有那个文件或目录
./:
error
merge
out
success
txt
如果要重定向到同一个文件则只能采用以下两种方式:
> file 2>&1
&> file
如果依然采用> file 2> file
的方式,虽然也能写入到文件file,但是成功和错误的信息会混在一起。
/dev/null
/dev/null可以看作垃圾桶黑洞设备
如已知某个错误信息,但我们不想看到它想直接丢弃,就可以直接将错误信息重定向到该文件
[root@localhost testdir]# ls ./ baga 2>/dev/null
./:
error input merge out srfile success test test2 txt
正确的信息输出到屏幕上,但是错误信息【baga目录不存在】直接重定向到/dev/null
7.2 标准输入 < 、<<
< 和 <<
与 >> 相反, < 作用是将原本需要由键盘的输入换成由文件内容作为输入。
以cat为例:
cat后不跟任何参数,则会将键盘输入再次输出到屏幕中:
[root@localhost testdir]# cat
1 # 键盘输入
1 # 将键盘输入再输出到屏幕 下面一样
2
2
3
3
^C
因此,可以通过 cat > test
将键盘输入输出到【屏幕的内容】写到test文件【通过ctrl+d 或 ctrl+c停止输入】:
[root@localhost testdir]# cat > test
1
2
3
4
5
^C
[root@localhost testdir]# cat test
1
2
3
4
5
而使用标准输入的方式,我们可以把某个文件的内容写入到test文件,不需要通过键盘输入了:
[root@localhost testdir]# echo abc > srfile
[root@localhost testdir]# cat srfile
abc
# 通过标准输入的方式,将srfile内容写入到test中
[root@localhost testdir]# cat > test < srfile
[root@localhost testdir]# cat test
abc
<< 符号的使用方法如下:
command << 终止符
右侧终止符,表示输入该符号后command命令停止,而不用ctrl+c来终止。
例:以感叹号终止键盘输入
[root@localhost testdir]# cat > test2 <<\!
> a
> b
> c
> !
[root@localhost testdir]# cat test2
a
b
c
7.3 命令执行依据
7.3.1 ;
如果要顺序执行两个命令,则可以使用英文分号 ; 间隔开。
[root@localhost testdir]# ls a ; ls /
ls: 无法访问a: 没有那个文件或目录
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
两个命令执行成功与否互不影响。
7.3.2 &&
command1 && command2
command1返回成功,则继续执行command2,否则不执行command2
返回成功与否通过$?
决定,$?
可以获取上个命令的执行结果,1为错误,0为成功。
[root@localhost testdir]# mkdir haha
[root@localhost testdir]# ls ./haha && cd haha
由于存在haha目录,因此可以cd,且不报错
7.3.3 ||
command1 || command2
command1执行成功,则不执行command2,否则执行command2
例:如果xixi目录不存在,则创建xixi目录并进入该目录:
[root@localhost haha]# ls ./xixi || mkdir xixi && cd xixi
ls: 无法访问./xixi: 没有那个文件或目录
[root@localhost xixi]#
shell默认从左到右顺序执行,执行流程:
-
ls ./xixi
由于不存在xixi目录,因此第一个命令执行错误,所以继续执行第二个命令
-
mkdir xixi
第二个命令执行成功,前两个命令执行的最终结果为 false || true = true,因此继续执行第三个命令
-
cd xixi
进入xixi目录
八、管道(pipe)命令 |
管道命令的符号为 : |
场景:将上一个命令的结果【标准输出信息(标准错误输出信息无法处理)】作为下一个命令的输入进行进一步处理。
即我们需要通过多个命令对产生的数据进行处理后才能得到结果,就可以使用管道命令。
[root@localhost testdir]# cat input
1
2
3
[root@localhost testdir]# cat input | tail -1
3
-
管道命令仅处理标准输出
-
管道命令必须能够接收来自前一个命令的数据作为标准输入才能继续处理
如cp等命令就可以作为管道命令去接收上一个命令的输出
8.1 选取命令cut、grep
8.1.1 cut
cut主要针对一行数据进行分解,常用于分析一些以特定字符划分的数据。
在处理多空格相连的文本时比较困难,需要用后面的awk命令
语法:
-
cut -d ‘分隔符’ -f 'index’
-f: 把按照 ‘分隔符’ 拆分出来的数据,取出第几段
[root@localhost ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 按:分割,取出第一个 [root@localhost ~]# echo $PATH | cut -d ':' -f 1 /usr/local/sbin # 同上,取出第1和第3个 [root@localhost ~]# echo $PATH | cut -d ':' -f 1,3 /usr/local/sbin:/usr/sbin
-
cut -c 区间
取出指定区间【都是闭区间】的字符
# 取出从第2个字符开始的字符 [root@localhost ~]# echo $PATH | cut -c 2- usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 取出第2到4个间的字符 [root@localhost ~]# echo $PATH | cut -c 2-4 usr # 取出到第4个和第4个字符前的字符 [root@localhost ~]# echo $PATH | cut -c -4 /usr
按空格分割,取出第一个【如果-f 2,无法取到第二列值,因为中间间隔的空格数不确定,因此cut没法很好的处理这种带多个空格的文本】
[root@localhost ~]# last
reboot system boot 3.10.0-1127.el7. Wed Jul 21 20:58 - 10:44 (13:45)
root tty1 Tue Jul 13 23:10 - 23:10 (00:00)
root tty1 Tue Jul 13 23:10 - 23:10 (00:00)
reboot system boot 3.10.0-1127.el7. Tue Jul 13 23:09 - 23:10 (00:00)
root tty1 Tue Jul 13 21:47 - 23:03 (01:15)
reboot system boot 3.10.0-1127.el7. Tue Jul 13 21:47 - 23:10 (01:22)
reboot system boot 3.10.0-1127.el7. Tue Jul 13 21:32 - 21:47 (00:15)
wtmp begins Tue Jul 13 21:32:12 2021
[root@localhost ~]# last | cut -d ' ' -f 1
root
reboot
root
root
root
8.1.2 grep
grep主要用于分析一行的数据,将包含想要的数据的那一行【整行,而cut是取想要的那一部分】取出来。
语法:grep [-acinv] [--color=auto] '目标字符' filename
,当然,也支持正则查找
-
-c
匹配目标字符的个数
-
i
忽略大小写
-
-n
输出行号
-
-v
反向选择【不包含目标字符的行】
-
-A
after,后面跟数字N,表示将匹配的那一行的后N行也列出来【如我们要查看异常日志,通常要定位某个关键字错误,同时也要看一下上下文信息,就可使用-A以及下面的-B参数】
-
-B
与-A相反,表示把匹配的那行的前N行也列出来
# 包含root的行取出来
[root@localhost ~]# last | grep root
root 【这里符合条件的文本root是高亮显示的】 pts/0 gateway Sat Aug 21 18:10 still logged in
root tty1 Sat Aug 21 18:08 - 18:10 (00:02)
# 取出不包含root的行信息
[root@localhost ~]# last | grep -v root
reboot system boot 3.10.0-1127.el7. Sat Aug 21 18:10 - 21:53 (03:42)
reboot system boot 3.10.0-1127.el7. Wed Jul 28 21:22 - 21:53 (24+00:31)
# 匹配目标字符的个数
[root@localhost ~]# last | grep -c root
24
8.2 排序命令
8.2.1 sort
默认按文本排序
参数:
-f : 忽略大小写
-b : 忽略最前面的空格
-M : 按月份排序 JAN DEC等
-n : 使用纯数字排序
-r : 反向排序
-u : unip 相同的数据仅展示一行
-t : 分割符号,默认按TAB
-k : 以哪个区间进行排序
对账号进行排序:
[root@localhost ~]# cat /etc/passwd|sort
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
对每行,按 : 分割,然后按第3列进行排序:
[root@localhost ~]# cat /etc/passwd|sort -t : -k 3
root:x:0:0:root:/root:/bin/bash
wangmaolin:x:1000:1000:wangmaolin:/home/wangmaolin:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
但是目前第3列的数字并不是按照数字顺序排序的,而是按照文本进行排序,进行可以使用-n按数字排序:
[root@localhost ~]# cat /etc/passwd|sort -t : -k 3 -n
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
如上,已按数字大小进行排序。
如果想对排序结果进行去重,则使用 -u:
[root@localhost ~]# last | cut -d ' ' -f 1 | sort -u
reboot
root
wtmp
8.2.2 uniq
语法:
-c : 对结果进行计数
-i: 忽略大小写
或者也可以使用 uniq
命令进一步去重:
[root@localhost ~]# last | cut -d ' ' -f 1 | sort | uniq
reboot
root
wtmp
去重后,并统计出现的次数:
[root@localhost ~]# last | cut -d ' ' -f 1 | sort | uniq -c
1
8 reboot
24 root
1 wtmp
[root@localhost ~]# last | cut -d ' ' -f 1 | sort | uniq -ci
1
8 reboot
24 root
1 wtmp
8.2.3 wc
直接统计文本的行数、字数和字符数
参数:
-l : 显示行数
-w : 字数
-c : 字节数
[root@localhost ~]# cat test
a
i啊安 就哈哈
cb a
[root@localhost ~]# wc test
3 5 25 test
[root@localhost ~]# ll test
-rw-r--r--. 1 root root 25 8月 21 22:33 test
[root@localhost ~]# wc test -w
5 (字数)
[root@localhost ~]# wc test -c
25 (字节数)
[root@localhost ~]# wc test -l
3 (行数)
8.3双向重定向tee
前面讲到的数据重定向,只能把数据重定向到文本,但是无法在中间捕获到数据。而tee可以额外的支持将数据打印到屏幕上进行标准输出。
# 将当前目录文件列表重定向到dir_context(拼错了)中,并打印到屏幕,打印到屏幕的内容会通过管道符交给grep命令处理,把包含test的行输出到屏幕
[root@localhost ~]# ll | tee dir_context | grep test
-rw-r--r--. 1 root root 25 8月 21 22:33 test
drwxr-xr--. 3 root test 52 8月 15 23:54 testdir
# 重定向后的文件内容
[root@localhost ~]# cat dir_context
总用量 12
-rw-------. 1 root root 1417 7月 13 21:28 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 8月 21 23:08 dir_context
-rw-r--r--. 1 root root 2502 8月 21 22:38 last.txt
-rw-r--r--. 1 root root 25 8月 21 22:33 test
drwxr-xr--. 3 root test 52 8月 15 23:54 testdir
8.4 字符转换命令
8.4.1 tr
从标准输入中替换、删减或删除字符,并将结果写到标准输出。
tr [选项] val1 val2
主要选项:
-d: 删除匹配val1的内容,不作替换
-s: 如果匹配val1 的字符在输入序列中存在连续的重复,在替换时会被统一缩为一个字符的长度,如果指定了val2,则替换为val2
现有文本如下:
[root@localhost ~]# cat test
a
b
c
d
e
f
q
gg
g
-
将文本替换为大写【支持正则匹配】
[root@localhost ~]# cat test | tr [a-z] [A-Z] A B C D E F Q GG G
-
删除 g
[root@localhost ~]# cat test | tr -d g a b c d e f q
-
将重复的文本 g 缩短为1个字符
[root@localhost ~]# cat test | tr -s g a b c d e f q g g
-
将重复的文本g替换为s
[root@localhost ~]# cat test | tr -s g s a b c d e f q s s
8.4.2 col
将TAB键替换为空格
[root@localhost ~]# cat test
a p
b
c
d
[root@localhost ~]# cat -A test
a^Ip$
b$
c$
d$
有以上文本,第一行a和p中间是一个空格键,经过替换后,如下,已经没有TAB(^I)了
[root@localhost ~]# cat test | col -x | cat -A
a p$
b$
c$
d$
8.4.3 paste
直接将两个内容以TAB键粘在一起
-d : 指定分隔符粘贴,默认为TAB
[root@localhost ~]# paste -d : test dir_context
a p:总用量 12
b:-rw-------. 1 root root 1417 7月 13 21:28 anaconda-ks.cfg
c:-rw-r--r--. 1 root root 0 8月 21 23:08 dir_context
d:-rw-r--r--. 1 root root 2502 8月 21 22:38 last.txt
e:-rw-r--r--. 1 root root 25 8月 21 22:33 test
f:drwxr-xr--. 3 root test 52 8月 15 23:54 testdir
gg:
g:
如果粘贴内容来自标准输入,则需要使用 - :
# 将test内容(这里是cat命令作为标准输入)与dir_context粘贴在一起在输出
[root@localhost ~]# cat test | paste - dir_context
a p 总用量 12
b -rw-------. 1 root root 1417 7月 13 21:28 anaconda-ks.cfg
c -rw-r--r--. 1 root root 0 8月 21 23:08 dir_context
d -rw-r--r--. 1 root root 2502 8月 21 22:38 last.txt
e -rw-r--r--. 1 root root 25 8月 21 22:33 test
f drwxr-xr--. 3 root test 52 8月 15 23:54 testdir
gg
g
8.4.4 expand
将TAB键转换为指定数量的空格:
[root@localhost ~]# cat -A test
a^Ip$
# 将TAB转为3个空白字符
[root@localhost ~]# cat test | expand -t 3 | cat -A
a p$
8.5 split
该命令可以将文件按照大小或行数进行拆分。
语法:split filename [选项] prefix
prefix: 拆分后的文件前缀
选项:
-b : 按文件大小,单位:b、k、m等
-l : 按行数
如下,有一个2.5K的文件:
[root@localhost ~]# ll -lh last*
-rw-r--r--. 1 root root 2.5K 8月 21 22:38 last.txt
1.按照1K的规格进行拆分,拆分后文件前缀为result:
[root@localhost ~]# split last.txt -b 1k result
# 以下三个result*文件为拆分后的文件
[root@localhost ~]# ll result*
-rw-r--r--. 1 root root 1024 8月 22 10:02 resultaa
-rw-r--r--. 1 root root 1024 8月 22 10:02 resultab
-rw-r--r--. 1 root root 454 8月 22 10:02 resultac
2.按照10行的规格进行拆分,拆分后文件前缀为lresult:
[root@localhost ~]# split -l 10 last.txt lresult
[root@localhost ~]# ll lresult*
-rw-r--r--. 1 root root 770 8月 22 10:06 lresultaa
-rw-r--r--. 1 root root 770 8月 22 10:06 lresultab
-rw-r--r--. 1 root root 770 8月 22 10:06 lresultac
-rw-r--r--. 1 root root 192 8月 22 10:06 lresultad
那如果要拆分的内容来自标准输入呢,同样使用 -
代替就好了:
# 将当前目录文件列表每5行拆分至lala*文件
[root@localhost ~]# ll | split -l 5 - lala
[root@localhost ~]# ll lala*
-rw-r--r--. 1 root root 236 8月 22 10:08 lalaaa
-rw-r--r--. 1 root root 268 8月 22 10:08 lalaab
-rw-r--r--. 1 root root 159 8月 22 10:08 lalaac
[root@localhost ~]# cat lalaa