《Linux私房菜》——二、BASH以及相关命令(变量、配置文件、数据流定向、pipe、sort、选取命令等)

本文来自《鸟哥的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 ~]#命令行前的这个提示符,其参数如下:

    1. \d

      【星期 月 日】 如:【Mon Feb 3】

    2. \H

      完整主机名,即上面的HOSTNAME变量值

    3. \h

      简短主机名,即完整主机名的第一个小数点前面的值

    4. \t | \T | \A | @

      都是时间格式:

      \t: 24小时制 【HH:MM:SS】
      \T: 12 小时制 【HH:MM:SS】
      \A: 24小时制   【HH:MM】
      \@: 12小时格式 【am/pm】
      
    5. \u

      当前用户账号名

    6. \v

      bash版本信息

    7. \w

      完整工作目录名【从根目录开始】

    8. \W

      当前工作目录名【basename】

    9. \#

      当前终端执行的命令数

    10. /$

      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 路径与命令查找顺序

当执行一个命令时,其按照以下顺序执行:

  1. 以相对或绝对路径执行命令
  2. 通过别名alias找到该命令来执行
  3. 由内置(buildin)的bash命令执行
  4. 通过**$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并调用完其他配置后,最后就会调用用户的个人配置文件,同时包括以下三个:

  1. ~/.bash_profile
  2. ~/.bash_login
  3. ~/.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 821 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 821 22:33 test
drwxr-xr--. 3 root test   52 815 23:54 testdir

# 重定向后的文件内容
[root@localhost ~]# cat dir_context
总用量 12
-rw-------. 1 root root 1417 713 21:28 anaconda-ks.cfg
-rw-r--r--. 1 root root    0 821 23:08 dir_context
-rw-r--r--. 1 root root 2502 821 22:38 last.txt
-rw-r--r--. 1 root root   25 821 22:33 test
drwxr-xr--. 3 root test   52 815 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
  1. 将文本替换为大写【支持正则匹配】

    [root@localhost ~]# cat test | tr [a-z] [A-Z]
    A
    B
    C
    D
    E
    F
    Q
    GG
    G
    
  2. 删除 g

    [root@localhost ~]# cat test | tr -d g
    a
    b
    c
    d
    e
    f
    q
    
    
    
    
  3. 将重复的文本 g 缩短为1个字符

    [root@localhost ~]# cat test | tr -s g
    a
    b
    c
    d
    e
    f
    q
    g
    g
    
  4. 将重复的文本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 713 21:28 anaconda-ks.cfg
c:-rw-r--r--. 1 root root    0 821 23:08 dir_context
d:-rw-r--r--. 1 root root 2502 821 22:38 last.txt
e:-rw-r--r--. 1 root root   25 821 22:33 test
f:drwxr-xr--. 3 root test   52 815 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 713 21:28 anaconda-ks.cfg
c	-rw-r--r--. 1 root root    0 821 23:08 dir_context
d	-rw-r--r--. 1 root root 2502 821 22:38 last.txt
e	-rw-r--r--. 1 root root   25 821 22:33 test
f	drwxr-xr--. 3 root test   52 815 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 821 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 822 10:02 resultaa
-rw-r--r--. 1 root root 1024 822 10:02 resultab
-rw-r--r--. 1 root root  454 822 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 822 10:06 lresultaa
-rw-r--r--. 1 root root 770 822 10:06 lresultab
-rw-r--r--. 1 root root 770 822 10:06 lresultac
-rw-r--r--. 1 root root 192 822 10:06 lresultad

那如果要拆分的内容来自标准输入呢,同样使用 - 代替就好了:

# 将当前目录文件列表每5行拆分至lala*文件
[root@localhost ~]# ll | split -l 5 - lala
[root@localhost ~]# ll lala*
-rw-r--r--. 1 root root 236 822 10:08 lalaaa
-rw-r--r--. 1 root root 268 822 10:08 lalaab
-rw-r--r--. 1 root root 159 822 10:08 lalaac
[root@localhost ~]# cat lalaa
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值