shell 脚本可以找到与相同正则表达式匹配的所有连续行组并将它们打乱吗?

shell 脚本可以找到与相同正则表达式匹配的所有连续行组并将它们打乱吗?

我正在用 Markdown 语言为我的学生编写测验。其中一项测验可能如下所示:

% QUESTION
Who played drums for The Beatles?

(X) Ringo
( ) John
( ) Paul
( ) George

% QUESTION
What is the first line of MOBY DICK?

(X) Call me Ishmael.
( ) foo
( ) bar
( ) spam
( ) eggs

我想随机化所有这些多项选择选项。所以,我想我需要一个 shell 脚本:

  1. 查找以 (X) 或 ( ) 开头的所有连续行块。
  2. 打乱这些行块中的每一个。

这可能吗?我知道这一点,shuf并且sort -R会随机化任何文本的行,但我不确定如何隔离这些选项块。

答案1

使用 AWK:

BEGIN {
    srand()
    answers[1] = ""
    delete answers[1]
}

function outputanswers(answers, len, i) {
    len = length(answers)
    while (length(answers) > 0) {
        i = int(rand() * len + 1)
        if (answers[i]) {
            print answers[i]
        }
        delete answers[i]
    }
}

/^$/ {
    outputanswers(answers)
    print
}

/^[^(]/

/^\(/ {
    answers[length(answers) + 1] = $0
}

END { outputanswers(answers) }

它的工作原理是在数组中累积答案answers,并在必要时以随机顺序输出其内容。如果行以左括号开头,则被认为是答案(我希望这是对您的规范的有效简化)。

答案2

Perl 方法:

perl -00 -M"List::Util qw/shuffle/" -lpe 'if(/^\(/){$_=join "\n",shuffle split(/\n/); }' file

与注释脚本相同:

#!/usr/bin/env perl

## Import the shuffle function from the List::Util module.
## This is done by the -M in the one-liner .
use List::Util qw/shuffle/;
    
## Enable paragraph mode, where each input record is a paragraph.
## This is equivalent to -00 in the one-liner. 
$/ = "";

## set output record separator (done by -l when after -00)
$\ = "\n\n";

## Read each record of standard input into the special variable $_.
## -p in the one-liner adds a similar implicit loop around the code
## given to -e.
while (<>) {
  ## strip trailing newlines (done by -l in the one-liner)
  chomp;

  ## If this record starts with a parenthesis
  if(/^\(/){
    ## Split the record (here, the entire paragraph, the whole section
    ## until the next sequence of one or more empty lines) on newline
    ## characters and save in the array @lines. In the one-liner, I skipped 
    ## creating this temporary array and joined the split directly
    @lines = split(/\n/);
    ## Set the special variable $_ to hold the shuffled contents of
    ## the @lines array, now connected with newline characters again.
    $_ = join "\n",shuffle @lines
  }

  ## Print whatever is in the $_ variable. That's the additional thing
  ## -p does compared to -n.
  print
}

而且,只是为了好玩,这里有一个稍微短一点的版本:

perl -MList::Util=shuffle -00lpe'$_=join"\n",shuffle split/\n/ if/^\(/' file

答案3

perl:

perl -F'\n' -MList::Util=shuffle -pal -00e '$"=$\;
  $_ = "@{[/^\([X ]\)/ ? shuffle(@F) : @F]}"
     . ($", $,)[eof];
' file
  • 在段落模式 -00 和 autosplit -a 中调用换行符 -F'\n' 上的 para,字段存储在零索引数组 @F 中
  • 加载 List::Util 模块 -M 并从中导入 shuffle 函数。
  • 对于以 (X) 或 ( ) 开头的段落,我们会重新排列字段,而对于其他段落则不会。

GNU sed

sed -ne '
  /^([X ])/!{p;d;}       ;# just print noninteresting lines
  :loop 
    H;$bshfl           # accumulate the interesting lines in hold space 
    n
  //bloop
  :shfl
  x;s/.//       ;# retrieve hold n strip leading newline 
  s/.*/printf %s\\n "&" | shuf/ep  ;# shuffle 
  z;x;/^([X ])/!s/^/\n/;D   ;# null hold n loop back for more 
' file

输出:从当前运行开始

% QUESTION
Who played drums for The Beatles?

( ) John
( ) Georgen
( ) Paul
(X) Ringo

% QUESTION
What is the first line of MOBY DICK?

( ) eggsn
(X) Call me Ishmael.
( ) bar
( ) spam
( ) foo

答案4

只需通过管道传输您想要打乱的行shuf并按原样打印其余部分:

$ awk '/^\(/{print | "shuf"; next} !NF{close("shuf")} 1' file
% QUESTION
Who played drums for The Beatles?

( ) John
( ) Paul
(X) Ringo
( ) George

% QUESTION
What is the first line of MOBY DICK?

(X) Call me Ishmael.
( ) foo
( ) spam
( ) eggs
( ) bar

相关内容