シェルスクリプトが同じ正規表現に一致するすべての連続行グループを見つけて混在させることはできますか?

シェルスクリプトが同じ正規表現に一致するすべての連続行グループを見つけて混在させることはできますか?

私はマークダウンで学生のためのクイズを書いています。テストの1つは次のとおりです。

% 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

私はこのすべての選択肢オプションをランダムに選択したいと思います。だからシェルスクリプトが必要だと思います。

  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 -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 および自動分割 -a で newline-F'\n' に対して para を呼び出すと、フィールドは 0 インデックス配列 @F に格納されます。
  • List::Util モジュール -M をロードし、ここからシャッフル機能をインポートします。
  • (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

関連情報