Linux Shell脚本攻略-玩转xargs

玩转xargs

Unix命令可以从标准输入(stdin)或命令行参数中接受数据。利用管道可以将一个命令的标准输出传入到另一个命令的标准输入

可以用别的方法来调用只能接受命令行参数的命令,最简单的方法就是使用反引号执行命令,然后将其输出作为命令行参数

$ gcc `find '*.c'`

这种方法在很多情况下都管用,但是如果要处理的文件过多,则会出现Argument list too long。xrag命令可以解决这个问题
xargs命令从stdin处读取一系列参数,然后使用这些参数来执行指定命令。能将单行或多行输入文本转换成其它格式,例如单行变多行或多行变单行

1. 预备知识

xargs命令应该紧跟在管道操作符之后。它使用标准输入作为主要的数据源,将从stdin中读取的数据作为指定命令的参数并执行该命令

ls *.c | xargs grep main # 在一组C语言源代码文件中搜索字符串main

xargs命令重新格式化stdin接收到的数据,再将其作为参数提供给指定命令。xargs默认会执行echo命令。和find命令的-exec选项相比,两者在很多方面都很相似

# 1. 将多行输入转换成单行输出
$ cat example.txt
1	2	3	4	5	6
7	8	9	10
11	12

$ cat example.txt| xargs
1	2	3	4	5	6	7	8	9	10	11	12
# 2. 将单行输入转换成多行输出
## -n选项可以限制每次调用命令时用到的参数个数
$ cat example.txt| xargs -n 3
1	2	3
4	5	6
7	8	9
10	11	12

2.工作原理

xargs命令接受来自stdin的输入,将数据解析成单个元素,然后调用指定命令并将这些元素作为该命令的参数。xargs默认使用空白字符分隔输入并执行/bin/echo
可以定义一个用来分隔参数的分隔符。-d选项可以为输入数据指定自定义的分隔符

$ echo "splitXsplit2Xsplit3Xsplit4"| xargs -d X
split1	split2	split3	split4

# 结合-n选项,可以将输入分割成多行,每行包含两个单词
$ echo "splitXsplit2Xsplit3Xsplit4"| xargs -d X -n 2
split1	split2	
split3	split4

xargs命令可以和find命令很好地结合在一起,find地输出可以通过管道传给xargs,然后再由xargs执行-exec选项所无法处理的复杂操作。如果文件系统的有些文件名中包含空格,find命令的-print0选项可以使用0(NULL)来分隔查找到的元素,然后再用xargs对应的-0选项进行解析

# 在Samba挂载的文件系统中搜索.docx文件,这些文件名中通常会包含大写字母和空格。
# 其中使用了grep找出内容中不包含image的文件
$ find /smbMount -iname '*.docx' -print0 | xargs -0 grep -L image
# 1. 读取stdin,为命令传入格式化参数
#!/bin/bash
# cecho.sh

echo $*'#'

## 当参数被传递给文件cecho.sh后,它会打印这些参数并以#字符结尾

$ ./cecho.sh arg1 arg2
arg1 arg2 #

# 1. 有一个包含着参数列表的文件(每行一个参数)要提供给某个命令(比如cecho.sh)
# 需要以不同的形式来应用这些参数。在一种形式中,每次调用提供一个参数
$ ./cecho.sh arg1
$ ./cecho.sh arg2
$ ./cecho.sh arg3

# 2. 每次调用提供一到两个参数
$ ./cecho.sh arg1 arg2
$ ./cecho.sh arg3

# 3. 在单词调用中提供所有参数
$ ./cecho.sh arg1 arg2 arg3


# args.txt
$ cat args.txt
arg1
arg2
arg3

$ cat args.txt | xargs -n 1 ./cecho.sh
arg1 #
arg2 #
arg3 #


$ cat args.txt | xargs -n 2 ./cecho.sh
arg1 arg2 #
arg3 #

$ cat args.txt | xargs ./cecho.sh
arg1 arg2 arg3 #

# 在上面的例子中,由xrags添加的参数都被放置在指定命令的尾部,但有时可能需要在命令末尾有一个固定的参数,并希望xargs能够替换居中的参数
## ./cecho.sh -p arg1 -l
## 在命令执行过程中,arg1是唯一的可变内容,其余部分都保持不变
## xargs有一个-I,可以用于指定替换字符串,这个字符串会在xargs解析输入时被参数替换掉
$ cat args.txt | xargs -I {} ./cecho.sh -p {} -l
-p arg1 -l #
-p arg2 -l #
-p arg3 -l #

-I {}指定了替换字符串,为该命令提供的各个参数会通过stdin读取并依次替换字符串{}

使用-I的时候,命令以循环的方式运,如果有3个参数,那么命令就会连同{}一同被执行3次。{}会在每次执行中被替换为相应的参数

  1. 结合find使用xargs
# 下面这样做很危险,有可能会误删文件,无法预测find命令输出的分隔符究竟是什么'\n'还是' '。
# 如果文件名中包含空格符,xargs会将其误认为是分隔符
# 如 bashrc text.txt会被视为bashrc和text.txt
$ find . -type f -name "*.txt" -print | xargs rm -f

使用find命令的-print0选项可以生成以空字符(’\0’)作为分隔符的输出,然后将其作为xargs命令的输入

$ find . -type f -name "*.txt" -print0 | xargs -0 rm -f
  1. 统计源代码目录中所有C程序文件的行数
    行数(Lines of Code, LOC)
$ find source_code_dir_path -type f -name "*.c" -print0 | xargs -o wc -l
  1. 结合stdin,巧妙运用while语句和子shell
    xargs会将参数放置在指定命令的尾部,因此无法为多组命令提供参数,可以通过创建子shell来处理这种复杂情况。子shell利用while循环读取参数并执行命令
$ cat file.txt | (while read arg; do cat $arg; done)
# 等同于cat files.txt | xargs -I {} cat {}

在while循环中,可以将cat $arg替换成任意数量的命令,这样就可以对同一个参数执行多个命令。也可以不借助管道将输出传递给其它命令。这种利用()创建子shell的技巧可以应用于多种问题场景。子shell操作符内部的多条命令在执行时就像一个整体

$ cmd0 | (cmd1;cmd2;cmd3) | cmd4

shell的-c选项可以调用子shell来执行命令行脚本,可以于xargs结合解决多次替换的问题,下列命令找出了所有C文件并显示出每个文件的名字,文件名前会加上一个换行符(-e选项运行进行转义替换)

$ find . -name '*.c' | xargs -I ^ sh -c "echo -ne '\n ^: '; grep main ^"

用tr进行替换

tr可以对来自标准输入的内容进行字符替换、字符删除以及重复字符压缩
tr是translate的简写,因为它可以将一组字符转换成另一组字符

tr只能通过标准输入接收输入,无法通过命令行参数接收

$ tr [options] set1 set2

来自标准输入的输入字符会按照位置从set1映射到set2(set1中的第一个字符映射到set2中的第一个字符,以此类推),然后将输出写入到stdout
set1和set2是字符类或字符组,如果两个字符组的长度不相等,那么set2会不断复制其最后一个字符,直到长度与set1相同;如果set2的长度大于set1,那么在set2中超出set1长度那部分字符则被全部忽略

$ echo "HELLO WHO IS THIS" | tr 'A-Z' 'a-z'
hello who is this

将字符从一个集合映射到另一个集合

$ echo 12345 | tr '0-9' '9876543210'
87654 # 已加密

$ echo 87654 | tr '9876543210' '0-9'
12345 # 已解密

tr命令可用于加密,ROT13是一个著名的加密算法,此算法中,字符会被移动13个位置,因此文本加密和解密都使用同一个函数

tr还可以把制表符转换成单个空格

$ tr '\t' ' ' < file.txt
  1. 用tr删除字符,-d,指定要删除的字符集合
$ echo "Hello 123 world 456" | tr -d '0-9'
Hello world
# 将stdin中的数字删除并打印删除后的结果
  1. 字符集补集
# tr -c [set1] [set2]
# 如果只给出set1,那么tr将删除所有不在set1中的字符
# 同时给出set1和set2的话,tr会将不再set1中的字符转换成set2中的字符
# 如果给出了-c选项,则需要同时给出set1和set2
# 如果同时给出了-c和-d选项,则只能给出set1,其它所有字符都会被删除

$ echo hello 1 char 2 next 4 | tr -d -c '0-9 \n'
124

$ echo hello 1 char 2 next 4 | tr -c '0-9' ' '
	1	2	4
  1. 用tr压缩字符
    tr可以删除字符串中重复出现的字符
# tr -s '[需要压缩的一组字符]'
$ cat multi_blanks.txt | tre - s '\n'
line 1
line 2
line 3
line 4

# tr可以将文件中的数字列表进行相加
$ cat sum.txt
1
2
3
4
5

$ cat sum.txt | echo $[$(tr '\n' '+') 0 ]
15

$ cat text.txt
first 1
second 2
third 3

$ cat test.txt | tr -d [a-z] | echo "total: $[$(tr ' ' '+')]"
total: 6
  1. 字符类

alnum 字母和数字
alpha 字母
cntrl 控制(非打印)字符
digit 数字
graph 图形字符
lower 小写字母
print 可打印字符
punct 标点符号
space 空白字符
upper 大写字母
xdigit 十六进制字符

tr '[:lower:]' '[:upper:]'

校验与核实

通过比对下载文件和原始文件的校验和,能够合适接收到的文件是否正确
如果源位置上的校验和与目的地上接收文件的校验和相等,就意味着我们接收到的文件没有问题

Unix和Linux支持多种校验和程序,但是常用的是MD5和SHA-1

$ md5sum filename
52a1aedd6a17fdad7818d0bf799a890c
# md5sum是一个长度为32个字符的十六进制串
$ md5sum file1 file2 file3
[checksum1] file1
[checksum1] file2
[checksum1] file3

$ md5sum -c file_sum.md5
# 检验校验和是否匹配

$ md5sum -c *.md5
# 用所有的md5信息来检查所有文件

# SHA-1给出一个长度为40个字符的十六进制串
$ sha1sum filename

对目录进行校验

校验和是从文件中计算得来的,对目录进行校验和意味着需要对目录中的所有文件以递归的方式进行计算
md5deep或sha1deep命令可以遍历目录树,计算其中所有文件的校验和

$ md5deep -rl directory_path > directory.md5
# -r 使用递归遍历
# -l 使用相对路径,默认路径,md5deep会输出文件的绝对路径
$ find directory_path -type f -print0 | xargs -0 md5sum >> directory.md5
$ md5sum -c directory.md5
$ md5sum file
52a1aedd6a17fdad7818d0bf799a890c  tmp
$ sha1sum file
20ce7b1832443054086668f4a9423bc344171a75  tmp

md5和sha1都是单向散列算法,均无法逆推出原始数据,两者通常用于为特定数据生成唯一的密钥
这类散列算法多用于存储密码,密码只存储其对应散列值。如果需要认证某个用户,则读取该用户提供的密码并转换为散列值,然后与之前存储的散列值进行比对,如果相同,用户则通过认证并授权访问

尽管应用广泛,md5sum和sha-1已不再安全,推荐使用bcrypt或sha512sum这类工具进行加密

shadow-like散列(加盐散列)

在linux中,用户密码是以散列值形式存储在文件/etc/shadow中
shadow密码通常是加盐密码(salted password),所谓的“盐”就是一个额外的字符串,起混淆的作用,使加密更加难以破解
盐是由一些随机位组成的,它们作为密钥生成函数的输入之一,产生密码的加盐散列

将salt_string替换为随机字符串并将PASSWORD替换成想要使用的密码

加密工具与散列

加密技术主要用于防止数据遭受未经授权的访问。和上面讲的校验和算法不同,加密算法可以无损地重构原始数据,可用的加密算法有很多,下面将讨论Linux/Unix中最常用的那些

crypt

crypt命令通常并没有安装在Linux系统中。它是一个简单的加密工具,相对而言不是那么安全,该命令从stdin接受输入,要求用户创建口令,然后将加密数据输出到stdout

$ crypt <input_file >output-file
Enter passphrase:
$ crypt PASSPHRASE <input_file >encrypted_file
$ crypt PASSPHARSE -d <encrypted_file >output_file
# 解密文件 

gpg(GNU privacy guard)

是一种应用广泛的工具,它使用加密技术来保护文件,以确保数据在送达目的地之前无法被读取

gpg签名同样广泛用于E-mail通信中的邮件“签名”,以证明发送方的真实性

$ gpg -c filename
# 加密文件
$ gpg filename.gpg
# 解密文件
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值