Shell编程:从命令输出中选择随机行

时间:2015-04-30 04:49:06

标签: macos shell

我正在尝试创建一个简单的Shell脚本,其中包括从当前工作目录中选择一个随机目录,然后导航到该目录。

任何人都可以说明如何列出所有目录,并从此列表中随机选择一个目录吗?

我试图避免将所有目录列入文本文件,只是从该文件中选择一个随机行(这很简单)。

我最初的尝试包括使用ls -d */命令仅列出目录。该命令在输入终端时有效,但返回错误:

ls: */: No such file or directory

当我尝试将其实现到此脚本中时:

DIR_LIST=` ls -d */`

echo "$DIR_LIST"

4 个答案:

答案 0 :(得分:4)

else{ System.out.println("Wrong value entered. Please try again"); continue; }

find . -maxdepth 1 -type d ! -path . | shuf -n 1版本:

shuf

修改:根据评论改进代码。

答案 1 :(得分:2)

您看到的错误消息表示当前目录发生以使(非隐藏)子目录< / strong> - 脚本的工作目录可能与您期望的cd不同,与期望的目录不同。第一

然而,除此之外,它更好not to parse the output of ls;
直接使用pathname expansion (globbing) 更简单,更健壮 - 见下文。

注意:最初,这个答案仅包含一个基于bash 数组的解决方案,该解决方案已被替换为多行字符串解决方案,并给出了通用标题问题。

问题标记为osx,其中有两个含义:

  • 未预装GNU实用程序shuf
    • 但是,您可以使用gshuf通过Homebrew安装(brew install coreutils)。
  • BSD实用程序jot已预先安装;虽然远不等同于shuf,但 能够从一系列整数中选择一个随机数。

基于 jot解决方案:

dirs=$(printf '%s\n' */)  # collect subdir. names, each on its own line
randomLineNum=$(jot -r 1 1 $(wc -l <<<"$dirs")) # pick random line number
randomDir=$(sed -n "$randomLineNum{p;q;}" <<<"$dirs") # extract random dir. name
cd "$randomDir"    # use the randomly selected dir.
  • printf '%s\n' */打印所有目录名称(终止/);每个人都有自己的路线。
    • 请注意,在这样的简单情况下,没有有充分理由使用find; glob */足以匹配所有子目录。
  • jot -r 1 1 $(wc -l <<<"$dirs")会返回1和$dirswc -l <<<"$dirs")中行数之间随机选择的整数,即子目录数。
  • sed -n '<lineNumber>{p;q;}'是一个sed成语,只打印(p)具有指定数字的行,然后退出(q)处理该文件。

符合POSIX标准的解决方案

注意:如果您不能认为存在jotshufbash,则此功能非常方便。

dirs=$(printf '%s\n' */)
randomLineNum=$(awk -v count="$(printf '%s\n' "$dirs" | wc -l)" \
                  'BEGIN { srand(); print 1 + int(rand()* count) }')
randomDir=$(printf '%s\n' "$dirs" | sed -n "$randomLineNum{p;q;}")
cd "$randomDir"
  • printf '%s\n' "$dirs" | wc -l计算$dir
  • 中的行数
  • awk -v count=<lineCount> 'BEGIN { srand(); print 1 + int(rand()* count) }'使用awk打印1到1之间的随机数:
    • srand()为随机生成器播种,rand()返回随机浮动&gt; = 0和&lt; 1;通过乘以行数,转换为整数并加1,返回随机数&gt; = 1&lt; =行数。

为了完整起见,让我们看一下shuf解决方案:

使用 shuf

最简单但效率低下的解决方案:

printf '%s\n' */ | shuf -n 1
  • shuf -n 1随机播放所有输入行,然后只打印洗牌行的第一个

这是低效的,因为即使只需要 1 随机行, shuf也总是将所有输入行一次读入内存,并将所有洗牌,而不是随机挑选1个;但是,少数几行可能无关紧要。

稍微繁琐,但效率更高shuf 解决方案:

请注意,此解决方案类似于基于jot的解决方案。

dirs=$(printf '%s\n' */)
randomLineNum=$(shuf -n 1 -i 1-"$(wc -l <<<"$dirs")")
randomDir=$(sed -n "$randomLineNum{p;q;}" <<<"$dirs")
cd "$randomDir"
  • shuf -n 1 -i 1-"$(wc -l <<<"$dirs")"在<{1}}($dirs)中的行数和1之间的范围内随机播放整数,并仅打印 1 (第一个)洗牌数字的wc -l <<<"$dirs",有效地产生一个随机的行号。
  • 仅调整行数字而不是行本身的范围通常会更有效率,但请注意所有的随机排列范围内的整数仍然在内存中构建 - 与-n 1不同,后者只是在范围内选择单个整数。

答案 2 :(得分:1)

试试这个:

ls -d */ | shuf -n 1

shuf 从您的输出中选择随机目录。

您的脚本将如下所示:

DIR_LIST=`ls -d */`
echo "$DIR_LIST" | shuf -n 1

答案 3 :(得分:0)

您可以尝试

find . -maxdepth 1 -type d

而不是使用ls。我现在不在终点站,所以我无法测试。它可能是maxdepth 0。