前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >啥?还不知道shuf,那你一定写了很多废代码!Bash程序员,说你呢

啥?还不知道shuf,那你一定写了很多废代码!Bash程序员,说你呢

作者头像
程序员小助手
发布2020-04-27 13:17:05
6890
发布2020-04-27 13:17:05
举报
文章被收录于专栏:程序员小助手程序员小助手

引言

作为大多数熟练的 bash 程序员,都有可能没有听说过 shuf 这个指令。

不管你用或者不用,它都静静的躺在那里,从Linux发行版开始,它已经内置在指令集里了。

敢于冒险的人找到了 shuf,从此也改变了自己对他的看法。

shuf 是什么

在本文中我们尝试深入的了解 shuf 这个指令。

shuf是一个类似sort的命令行实用程序,包含在Coreutils中。您可能已经猜到,它用于伪随机给定的输入,就像您洗牌一样。你猜对了!

字如其人,它的名称也跟它的功能一样一目了然。

找到了这个指令,那就用起来吧,像其他大多数指令一样,在终端命令行里用 --help 打印出它的可选项。

shuf在哪些领域比较有用呢?有三个方面。

  1. 文件 file。
  2. 列表 list。
  3. 区间 range。

每种方式都有其优点,提前预习下 shuf 指令的用法,可以尽量避免使用中出现的错误。

操作文件

文件操作是下坡最常用的方式。选项中包含 -e 或者 -i,默认为文件操作。也就是说,命令行告诉该指令要输入的是一个文件。

文件来源可以是标准输入,或者是手动指定的文件路径。

参数列表的最后一个参数,也就可能是路径名或文件名。如果省略参数,则视为从标准输入读取。

下面是一些示例,明确指定文件来源。

标准输入隐式作为文件

这样,我们就从shuf命令的参数中省略了file。根据约定,您可以选择 - 来代替文件,以指示将该文件作为标准输入。

代码语言:javascript
复制
seq 3 | shuf

输出内容为,

代码语言:javascript
复制
1 3 2

标准输入显式作为文件

在命令行执行以下指令,

代码语言:javascript
复制
seq 3 | shuf -

输出内容如下,

代码语言:javascript
复制
3 1 2

我们可以看到上述两种方式。所达到的效果是一样的。

明确指定文件名

通过这种方式,我们在shuf命令的参数中指定一个文件名作为文件。下面是几个使用文件的文件shuf示例。

从终端输入

执行以下指令,

代码语言:javascript
复制
shuf /dev/fd/1

/dev/fd/1 其实就是类UNIX系统中的标准输入。命令行 Enter 之后。会停留在输入窗口。如上图所示,输入任意字符串。最后按 Ctrl + D 结束输入。shuf 将输入的文本随机打断并输出。

打乱文件的行

输入以下指令,

代码语言:javascript
复制
seq 3 > tmp.txt
shuf tmp.txt
rm -f tmp.txt

输出内容如下,

代码语言:javascript
复制
2 1 3

列表

在这种方式中,我们操作一个文件或通过管道输入到shuf命令中。通过这种方式,我们允许使用 -e 选项将输入行指定为shuf命令的参数,从而强制 shuf 作为列表 shuf 进行操作。

用法如下,

代码语言:javascript
复制
Usage: shuf -e [OPTION]... [ARG]...

在终端输入一些命令,

代码语言:javascript
复制
shuf -e 1 2 3

输出内容如下,

代码语言:javascript
复制
1 3 2

上述指令作用等同于

代码语言:javascript
复制
seq 3 | shuf -

使用变量作为参数

在终端输入以下指令,

代码语言:javascript
复制
var="1 2 3";
shuf -e ${var}

这个没有这个例子很简单,就是使用了 bash 变量进行操作。变量中存储了一个列表。

当然了,生成列表也可以用 bash 内置的方式。

代码语言:javascript
复制
shuf -e {1..3}

输出内容如下,

代码语言:javascript
复制
1 2 3

bash 的一些其他玩法,

代码语言:javascript
复制
shuf -e $( seq 3 )

本质上与命令符、管道、重定向,或文件内容读取原理是一致的。

区间

最后一种方法与前面介绍的方法不同。它不需要在命令行中指定文件或参数,而是需要一个整数范围。-i 选项强制 shuf 作为 range shuf 操作。

区间 shuf 生成一个按随机顺序排列的整数范围。

用法如下,

代码语言:javascript
复制
Usage: shuf -i LO-HI [OPTION]...

先举一个例子,

代码语言:javascript
复制
shuf -i 1-3

输出内容如下,

代码语言:javascript
复制
2 3 1

一些高级选项

下面列出的这些高级选项,在 bash 脚本编程中可能会很有用。

限制输出行数

运行以下指令,

代码语言:javascript
复制
shuf -i 1-3 -n 1

输出内容如下,

代码语言:javascript
复制
3

我们使用参数 -n 指定输出的行数。本例中 -n 等于 1,那么仅输出一行。

指定输出文件

像其他一些Linux中的指令一样,-o 用于指定输出文件名。举例说明,

代码语言:javascript
复制
shuf -i 1-3 -n 1 -o filter.txt
cat filter.txt
rm -f filter.txt

输出内容如下,

代码语言:javascript
复制
1

流操作

为了创建连续的输出行流,我们使用-r选项,如下所示。

代码语言:javascript
复制
shuf -e {0,1} -r | xargs -i echo -n "{}"

输出内容如下,

代码语言:javascript
复制
000101101010101101010110000101111010001010111001110…

使用\0代替\n用作分隔符

使用\0作为分隔行,仅需要使用 -z 选项。举例如下:

代码语言:javascript
复制
seq 3 | tr '\n' '\0' | shuf -z

输出内容如下:

代码语言:javascript
复制
213

如果没有shuffle,bash随机函数长什么样?

下面使用 gawk 实现类似 shuf 的功能,我们看看区别在哪里。

闲言少叙,直接上脚本。

代码语言:javascript
复制
gawk-shuf() {
  gawk -v random=${RANDOM} '
  function randInt() {
    return int(rand()*1000)
  }
  function case_numeric_compare(i1, v1, i2, v2, l, r) {
    l = int(v1)
    r = int(v2)
    if(l<r) return -1
    else if(l==r) return 0
    else return 1
  }
BEGIN {
  count=1
  srand(random)
}
{
  rank[count]=randInt()
  line[count]=$(0)
  count++
}
END {
asorti(rank,order,"case_numeric_compare")
  for(i=0;i&lt;count;i++) {
    print line[order[i]]
  }
}
' -
}if [ ${#} -eq 0 ]then
  trueelse
  exit 1 # wrong argsfi
gawk-shuf

功能是一样的,但是效率很低,维护起来很麻烦,对不对?

写在最后

大神们都已经准备好工具了,我们只用把它拿出来使用,不要重复造轮子了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员小助手 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • shuf 是什么
  • 操作文件
  • 列表
  • 区间
  • 一些高级选项
  • 如果没有shuffle,bash随机函数长什么样?
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档