Linux数据处理与重定向


一、linux中的常用符号

* 代表任意字符串
代表任意字符
/ 代表根目录或作为路径间隔符使用
\ 转义字符。
\ 续行符。可以使用续行符将一个命令行分写在多行上
$ 变量值置换,如:$PATH表示环境变量PATH的值 env显示所有系统环境变量 export修改系统环境变量
' 在’…'中间的字符都会被当做普通字符处理
"" 在’’…’'中间的字符会被当做文字处理并允许变量值置换
` 命令替换,置换`…`中命令的执行结果
< 输入重定向字符
> 输出重定向字符
| 管道字符
& 后台执行字符。在一个命令之后加上字符“&”,该命令就会以后台方式执行
; 按照顺序执行的多个命令
() 在子Shell中执行命令
{} 在当前Shell中执行命令
! 执行命令历史记录中的命令
~ 代表登录用户的宿主目录(自家目录)

二、历史记录

linux系统在shell中保留了用户键入的每一个命令的历史记录,并且提供了很多种方法让用户通过历史记录找到曾经使用过的命令,并且调用这个历史记录的命令。

[root@zuolaoshi ~]# history
    1  ifconfig
    2  ip ad sh
    3  free -h
    4  df -h 
语法 替换
!! 前一个命令
!n 命令号n
!-n 倒数第n个命令
!cmd 最后用来启动cmd的命令

与历史记录相关的文件和变量

[root@zuolaoshi ~]# echo $HISTFILE
/root/.bash_history
#用户的历史记录保存的位置
[root@zuolaoshi ~]# echo $HISTFILESIZE
1000
#启动时从历史记录中读取的记录条数
[root@zuolaoshi ~]# echo $HISTSIZE
1000
#退出时被写入历史记录的最大条数

历史记录技巧

esc+./alt+. 调用上一条命令的最后一部分内容
ctrl+r  在历史记录中搜索给出关键字的命令

三、标准输入、标准输出、标准错误

在linux系统中,大多数时候我们从键盘读取输入,在终端显示输出,而我们在键盘中输入的内容,多数都是执行命令,这些命令属于终端程序,除了终端程序还有图形程序和屏幕程序(如vim),不管是哪一种程序都会涉及到输入,输出,错误,多数情况下,我们在键盘输入信息,在显示器查看信息(正确的信息和错误的信息),这些输入的信息我们称之为标准输入(可以用0表示),输出的信息我们称之为标准输出(可以用1表示),而错误的信息(可以用2表示),我们称之为标准错误。在日常使用中我们除了可以使用键盘输入信息,从显示器读取信息之外,还可以指定程序从键盘以外的地方读取需要输入的内容,也可以让程序将信息输出到显示器以外的地方。

重定向输入和输出

#重定向输出
[root@zuolaoshi ~]# mkdir a
[root@zuolaoshi ~]# cd a
[root@zuolaoshi a]# mkdir aa ab ac
[root@zuolaoshi a]# cd aa
[root@zuolaoshi aa]# touch bb bc bd
[root@zuolaoshi ~]# ls a/ > test
[root@zuolaoshi ~]# cat test
aa
ab
ac
[root@zuolaoshi ~]# ls a/aa/ >> test
[root@zuolaoshi ~]# cat test
aa
ab
ac
bb
bc
bd

>覆盖
>>追加
#重定向输入
[root@zuolaoshi ~]# wc -l test
6 hello
[root@zuolaoshi ~]# wc -l < test 
6
注意第一个例子会输出文件名第二个不会因为它仅仅知道从标准输入读取内容

我们知道标准输入可以用0来表示,标准输出可以用1来表示,标准错误可以用2来表示,而有些时候这些输出的信息中即包含了正确的信息,也包含了错误的信息,如:

[root@zuolaoshi ~]# ls /etc/rc.d/
init.d  rc0.d  rc1.d  rc2.d  rc3.d  rc4.d  rc5.d  rc6.d  rc.local
[root@zuolaoshi ~]# head -1 /etc/rc.d/*
==> /etc/rc.d/init.d <==
head: 读取'/etc/rc.d/init.d' 时出错: 是一个目录

==> /etc/rc.d/rc0.d <==
head: 读取'/etc/rc.d/rc0.d' 时出错: 是一个目录

==> /etc/rc.d/rc1.d <==
head: 读取'/etc/rc.d/rc1.d' 时出错: 是一个目录

==> /etc/rc.d/rc2.d <==
head: 读取'/etc/rc.d/rc2.d' 时出错: 是一个目录

==> /etc/rc.d/rc3.d <==
head: 读取'/etc/rc.d/rc3.d' 时出错: 是一个目录

==> /etc/rc.d/rc4.d <==
head: 读取'/etc/rc.d/rc4.d' 时出错: 是一个目录

==> /etc/rc.d/rc5.d <==
head: 读取'/etc/rc.d/rc5.d' 时出错: 是一个目录

==> /etc/rc.d/rc6.d <==
head: 读取'/etc/rc.d/rc6.d' 时出错: 是一个目录

==> /etc/rc.d/rc.local <==
#!/bin/bash

我们通过ls命令查看/etc/rc.d这个目录的时候,我们发现,这个目录中即有目录,也有文件,而当我们使用“head -1”命令去查看文件的第一行内容的时候, 很显然目录是无法查看第一行的,这时就会有报错,当我想把这些信息都写入到一个指定的文件中而不想看到这些内容我该如何去做?

[root@zuolaoshi ~]# head -1 /etc/rc.d/* > test
head: 读取'/etc/rc.d/init.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc0.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc1.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc2.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc3.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc4.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc5.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc6.d' 时出错: 是一个目录
[root@zuolaoshi ~]# cat test
==> /etc/rc.d/init.d <==
==> /etc/rc.d/rc0.d <==
==> /etc/rc.d/rc1.d <==
==> /etc/rc.d/rc2.d <==
==> /etc/rc.d/rc3.d <==
==> /etc/rc.d/rc4.d <==
==> /etc/rc.d/rc5.d <==
==> /etc/rc.d/rc6.d <==
==> /etc/rc.d/rc.local <==
#!/bin/bash

我们可以利用之前的>将输出的信息重定向到一个指定的文件,但是仍然会收到错误提示,这是为什么呢?因为在linux当中正确的输出和错误的输出实际上是两种数据流,默认情况下这两种数据流都会在显示器上打印出来,而我们使用的>相当于1>,也就是将正确的信息写入到了test文件中,错误的信息依旧会看到。利用前面的提到0,1,2这三个数字,我们可以这样做

[root@zuolaoshi ~]# head -1 /etc/rc.d/* > test 2> test.err
[root@zuolaoshi ~]# cat test
==> /etc/rc.d/init.d <==
==> /etc/rc.d/rc0.d <==
==> /etc/rc.d/rc1.d <==
==> /etc/rc.d/rc2.d <==
==> /etc/rc.d/rc3.d <==
==> /etc/rc.d/rc4.d <==
==> /etc/rc.d/rc5.d <==
==> /etc/rc.d/rc6.d <==
==> /etc/rc.d/rc.local <==
#!/bin/bash
[root@zuolaoshi ~]# cat test.err 
head: 读取'/etc/rc.d/init.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc0.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc1.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc2.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc3.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc4.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc5.d' 时出错: 是一个目录
head: 读取'/etc/rc.d/rc6.d' 时出错: 是一个目录

但是这依然是两个文件,能不能将这些信息都写入到一个文件中呢?

[root@zuolaoshi ~]# head -1 /etc/rc.d/* > test.both 2>&1
[root@zuolaoshi ~]# cat test.both 
==> /etc/rc.d/init.d <==
head: 读取'/etc/rc.d/init.d' 时出错: 是一个目录
==> /etc/rc.d/rc0.d <==
head: 读取'/etc/rc.d/rc0.d' 时出错: 是一个目录
==> /etc/rc.d/rc1.d <==
head: 读取'/etc/rc.d/rc1.d' 时出错: 是一个目录
==> /etc/rc.d/rc2.d <==
head: 读取'/etc/rc.d/rc2.d' 时出错: 是一个目录
==> /etc/rc.d/rc3.d <==
head: 读取'/etc/rc.d/rc3.d' 时出错: 是一个目录
==> /etc/rc.d/rc4.d <==
head: 读取'/etc/rc.d/rc4.d' 时出错: 是一个目录
==> /etc/rc.d/rc5.d <==
head: 读取'/etc/rc.d/rc5.d' 时出错: 是一个目录
==> /etc/rc.d/rc6.d <==
head: 读取'/etc/rc.d/rc6.d' 时出错: 是一个目录
==> /etc/rc.d/rc.local <==
#!/bin/bash

或者

[root@zuolaoshi ~]# head -1 /etc/rc.d/* >& test.both1
[root@zuolaoshi ~]# cat test.both1 
==> /etc/rc.d/init.d <==
head: 读取'/etc/rc.d/init.d' 时出错: 是一个目录
==> /etc/rc.d/rc0.d <==
head: 读取'/etc/rc.d/rc0.d' 时出错: 是一个目录
==> /etc/rc.d/rc1.d <==
head: 读取'/etc/rc.d/rc1.d' 时出错: 是一个目录
==> /etc/rc.d/rc2.d <==
head: 读取'/etc/rc.d/rc2.d' 时出错: 是一个目录
==> /etc/rc.d/rc3.d <==
head: 读取'/etc/rc.d/rc3.d' 时出错: 是一个目录
==> /etc/rc.d/rc4.d <==
head: 读取'/etc/rc.d/rc4.d' 时出错: 是一个目录
==> /etc/rc.d/rc5.d <==
head: 读取'/etc/rc.d/rc5.d' 时出错: 是一个目录
==> /etc/rc.d/rc6.d <==
head: 读取'/etc/rc.d/rc6.d' 时出错: 是一个目录
==> /etc/rc.d/rc.local <==
#!/bin/bash

这两种使用方式都是告诉shell将错误信息写入到正确信息所写入的文件中。
如果这些错误信息是我们早就知道的,并且还不想看到的呢?
[root@zuolaoshi ~]# head -1 /etc/rc.d/* 2> /dev/null 
==> /etc/rc.d/init.d <==
==> /etc/rc.d/rc0.d <==
==> /etc/rc.d/rc1.d <==
==> /etc/rc.d/rc2.d <==
==> /etc/rc.d/rc3.d <==
==> /etc/rc.d/rc4.d <==
==> /etc/rc.d/rc5.d <==
==> /etc/rc.d/rc6.d <==
==> /etc/rc.d/rc.local <==
#!/bin/bash

/dev/null:表示的是一个黑洞,通常用于丢弃不需要的数据输出

综上所述针对于输入输出重定向与合并的用法有

语法 作用
cmd < file 从file重定向标准输入
cmd > file 把标准输出重定向到file中,如果file存在的话,覆盖(损坏)它
cmd>>file 把标准输出重定向到file中,如果file存在,附加给它
cmd 2>file 把标准错误重定向到file,如果file 存在,覆盖(损坏)它
cmd 2>> file 把标准错误重定向到file中,如果file 存在,附加给他
cmd>file 2>&1 合并标准输出和标准错误,并且重定向到file中(可移植的语法)
cmd >& file 合并标准输出和标准错误,并且重定向到file中(方便的语法)

四、管道

在前面,我们已经看到,进程的输出可以被重定向到终端显示器以外的地方,或者可以让进程从终端键盘以外的地方读取输入。一种最常用,最有力的重定向形式是把这二者结合起来,在这种形式下,一个命令的输出(标准输出)被直接“用管道输送”到另一个命令的输入(标准输入)中,从而构成了 Linux(和Unix)所谓的管道(pipe)。当两个命令用管道连接起来时,第一个进程的标准输出流被直接连接到第二个进程的标准输入序列。为了用bash创建管道,用一个垂直的小节线 | 把这两个命令连接起来。

image20200323144226709.png

[root@zuolaoshi ~]# pwd
/root
[root@zuolaoshi ~]# ls | grep ana
anaconda-ks.cfg
注意从管道读数据是一次性操作数据一旦被读它就从管道中被抛弃释放空间以便写更多的数据它只能处理经由前面一个指令传出的正确输出信息对错误信息信息没有直接处理能力然后传递给下一个命令作为标准的输入

五、数据处理常用工具

以下是关于 Linux 常用数据处理命令(cut、sort、uniq、xargs、wc)的详细教程,包含功能说明、常用选项及实战示例:

一、cut:按列提取文本数据

功能:从文本文件或标准输入中按列(字段)提取指定内容,支持按分隔符或字符位置切割。 语法

cut [选项]... 文件名

常用选项:

  • -d,:指定分隔符(默认制表符 \t)。
  • -f N[,M,N-M]:提取第 N、M 列或 N 到 M 列(如 2-5 表示第 2 到 5 列)。
  • -s:忽略没有分隔符的行。
  • -c N[,M,N-M]:按字符位置提取(如 3-8 表示第 3 到 8 个字符)。

示例:

1.按分隔符提取字段(以 /etc/passwd 为例,冒号 : 分隔):

cut -d ':' -f 1,3 /etc/passwd  # 提取第 1 列(用户名)和第 3 列(UID)

2.提取固定字符长度内容

echo "abc123def456" | cut -c 4-6  # 输出 "123"

3.过滤无分隔符的行

cut -d ':' -f 1 -s file.txt  # 仅处理包含分隔符的行

二、sort:排序文本内容

功能:对文本行进行排序,支持按字典序、数字、日期等规则排序。 语法

sort [选项]... 文件名

常用选项:

  • -n:按数字值排序(而非字符 ASCII 码)。
  • -r:逆序排序(默认正序)。
  • -k N[,M]:按第 N 列到第 M 列排序(若省略 M,则排序到行末)。
  • -t,:指定列分隔符(默认制表符)。
  • -u:去重(等价于排序后执行 uniq)。

示例:

1.按数字升序排序

echo -e "3\n10\n1" | sort -n  # 输出 1\n3\n10

2.按第二列降序排序(文件内容以空格分隔):

sort -t ' ' -k 2 -r data.txt

3.排序并去重

sort -u unsorted.txt  # 先排序,再去除重复行

三、uniq:处理重复行

功能:去除或统计相邻重复行(需先通过 sort 确保重复行相邻)。 语法

uniq [选项]... [输入文件] [输出文件]

常用选项:

  • -c:在每行前显示重复次数。
  • -d:仅显示重复行(至少出现 2 次)。
  • -u:仅显示唯一行(不重复的行)。
  • -i:忽略大小写差异。

示例:

1.统计重复行出现次数(需先排序):

sort access.log | uniq -c  # 输出每行内容及其出现次数

2.删除连续重复行(保留第一个):

uniq raw_data.txt  # 假设文件已排序,否则需先执行 sort

3.仅显示重复行(不统计次数)

sort data.txt | uniq -d

四、xargs:将标准输入转为命令行参数

功能:将管道中的输出转换为命令的参数,解决命令行参数过长的问题。 语法

命令 | xargs [选项]... 目标命令

常用选项:

  • -n N:每次传递 N 个参数给目标命令。
  • -0:配合 find -print0 处理包含空格的文件名(以 null 分隔)。
  • -I {}:替换占位符 {} 为输入内容(类似模板)。

示例:

1.批量删除多个文件(处理含空格的文件名):

find . -name "*.bak" -print0 | xargs -0 rm -f

2.为每个文件添加前缀

echo "file1.txt file2.txt" | xargs -n 1 mv {} prefix_{}

3.结合 echo 生成命令并执行

echo "1 2 3 4" | xargs -n 2 bash -c 'echo "Sum: $(( $1 + $2 ))"' _

# 输出:Sum: 3  Sum: 7

五、wc:统计文本信息

功能:统计文件的行数、单词数、字节数或字符数。 语法

wc [选项]... 文件名

常用选项:

  • -l:统计行数(lines)。
  • -w:统计单词数(words,以空格/制表符/换行分隔)。
  • -m:统计字符数(bytes,Linux 中通常与 -c 相同,但 -m 更标准)。
  • -c:统计字节数(bytes)。
  • -L:统计最长行的字符数。

示例:

1.统计文件行数

wc -l access.log  # 输出行数和文件名

2.统计目录下所有 .txt 文件的单词数

wc -w *.txt  # 显示每个文件的单词数及总和

3.获取最长行的长度

wc -L code.py  # 输出最长行的字符数

六、命令组合实战

场景:分析日志文件(假设日志格式:时间 状态码 URL

1.提取所有状态码并统计出现次数

cat access.log | cut -d ' ' -f 2 | sort | uniq -c

2.找出访问量前 5 的 URL

cat access.log | cut -d ' ' -f 3 | sort | uniq -c | sort -nr -k 1 | head -n 5

3.批量压缩当前目录下所有 txt 文件

ls *.txt | xargs -n 1 gzip

总结

  • 数据提取cut 按列/字符切割,grep 按模式匹配(配合使用更强大)。
  • 排序去重sort 排序后用 uniq 处理重复行(统计/删除)。
  • 参数传递xargs 解决命令行参数限制,支持复杂场景(如含空格的文件名)。
  • 统计分析wc 快速获取文本基本信息,结合管道实现数据清洗和统计。

通过组合这些命令,可以高效处理日志分析、数据清洗、批量操作等任务,是 Linux 文本处理的核心工具集。