bashの変数に入れ子になったコマンドの出力を割り当てることはできません。

bashの変数に入れ子になったコマンドの出力を割り当てることはできません。

ファイルから任意の行を選択する次のコマンドを変数に割り当てようとしましたが、機能しません。

givinv@87-109:~$ head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1
cower
givinv@87-109:~$

以下は、変数に割り当てようとしたときに発生するエラーです。

givinv@87-109:~$ VARIA=`head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1`
bash: command substitution: line 1: unexpected EOF while looking for matching `)'
bash: command substitution: line 2: syntax error: unexpected end of file
bash: command substitution: line 1: syntax error near unexpected token `)'
bash: command substitution: line 1: ` + 1)) file | tail -1'
-l: command not found
givinv@87-109:~$

同じforループを試しましたが、うまくいきませんでした。

givinv@87-109:~$ for i in `head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1`;do echo $i ;done
bash: syntax error near unexpected token `<'
givinv@87-109:~$ 

答え1

エスケープされていないバックティックをネストしようとしているため、機能しません。

VARIA=`head -$((${RANDOM} % `wc -l < file` + 1)) file | tail -1`

head -$((${RANDOM} %実際には、最初に単一のコマンドで実行しようとします。これにより、2つの最初のエラーが発生します。

$ VARIA=`head -$((${RANDOM} % `
bash: command substitution: line 1: unexpected EOF while looking for matching `)'
bash: command substitution: line 2: syntax error: unexpected end of file

その後、実行を試みます。

wc -l < file` + 1)) file | tail -1`

これは+ 1)) file | tail -1、バックティック間で評価を試み、次のエラーが発生することを意味します。

$ wc -l < file` + 1)) file | tail -1`
bash: command substitution: line 1: syntax error near unexpected token `)'
bash: command substitution: line 1: ` + 1)) file | tail -1'

バックティックをエスケープすることでこの問題を解決できます。

VARIA=`head -$((${RANDOM} % \`wc -l < file\` + 1)) file | tail -1`

しかし、通常、バックティックをまったく使用しないことが最善です。ほとんど常にこれを使用する必要があります$()。より強力でより簡単な構文を使用して、無限に入れ子にすることができます。

VARIA=$(head -$((${RANDOM} % $(wc -l < file) + 1)) file | tail -1)

答え2

このコマンドを使用してください。

VARIA=$(head -n "$((${RANDOM} % $(wc -l < test) + 1))" test | tail -n 1)

コマンドの結果を私たちが使用する変数に代入します$(...)(古代の`...`型は入れ子にするのが難しいです)。

答え3

ファイルから任意の行を読み取る(そしてそれを変数に割り当てる)代わりに単純化することを検討してください。貯水池サンプリングメソッド、変換済みThrigのPerlの実装awakでPeter.Oのシーディング改善:

VARIA=$(awk -v seed=$RANDOM 'BEGIN { srand(seed) } { if (rand() * FNR < 1) { line=$0 } } END { print line }' /usr/share/dict/words)

以下は素敵にパッケージ化されたawkスクリプトです。

awk -v seed=$RANDOM '
BEGIN { 
  srand(seed) 
}
{ 
  if (rand() * FNR < 1) { 
    line=$0
  } 
}
END { 
  print line 
}' /usr/share/dict/words

srand()awkの仕組みのせいで会議同じ値を得るもし同じ瞬間にこのスクリプトを実行します。〜しない限り別のランダムな項目としてシードを指定します。ここでは bash の $RANDOM をシードに渡します。ここでは、/usr/share/dict/wordsの単語をテキストソースとして選択します。

この方法はファイルに何行があるかは関係ありません(私のローカルコピーには479,828行があります)、非常に柔軟でなければなりません。

プログラムの計算を見るために、さまざまな行番号と確率を繰り返すラッパースクリプトを作成しました。

デモファイル

#!/bin/sh

for lineno in 1 2 3 4 5 20 100
do
  echo "0 .. 0.99999 < ( 1 / FNR == " $(printf 'scale=2\n1 / %d\n' "$lineno" | bc) ")"
  for r in 0 0.01 0.25 0.5 0.99
  do
    result=$(printf '%f * %d\n' "$r" "$lineno" | bc)
    case $result in
      (0*|\.*) echo "Line $lineno: Result of probability $r * line $lineno is $result and is < 1, choosing line" ;;
      (*)      echo "Line $lineno: Result of probability $r * line $lineno is $result and is >= 1, not choosing line" ;;
    esac
  done
  echo
done

結果:

0 .. 0.99999 < ( 1 / FNR ==  1.00 )
Line 1: Result of probability 0 * line 1 is 0 and is < 1, choosing line
Line 1: Result of probability 0.01 * line 1 is .010000 and is < 1, choosing line
Line 1: Result of probability 0.25 * line 1 is .250000 and is < 1, choosing line
Line 1: Result of probability 0.5 * line 1 is .500000 and is < 1, choosing line
Line 1: Result of probability 0.99 * line 1 is .990000 and is < 1, choosing line

0 .. 0.99999 < ( 1 / FNR ==  .50 )
Line 2: Result of probability 0 * line 2 is 0 and is < 1, choosing line
Line 2: Result of probability 0.01 * line 2 is .020000 and is < 1, choosing line
Line 2: Result of probability 0.25 * line 2 is .500000 and is < 1, choosing line
Line 2: Result of probability 0.5 * line 2 is 1.000000 and is >= 1, not choosing line
Line 2: Result of probability 0.99 * line 2 is 1.980000 and is >= 1, not choosing line

0 .. 0.99999 < ( 1 / FNR ==  .33 )
Line 3: Result of probability 0 * line 3 is 0 and is < 1, choosing line
Line 3: Result of probability 0.01 * line 3 is .030000 and is < 1, choosing line
Line 3: Result of probability 0.25 * line 3 is .750000 and is < 1, choosing line
Line 3: Result of probability 0.5 * line 3 is 1.500000 and is >= 1, not choosing line
Line 3: Result of probability 0.99 * line 3 is 2.970000 and is >= 1, not choosing line

0 .. 0.99999 < ( 1 / FNR ==  .25 )
Line 4: Result of probability 0 * line 4 is 0 and is < 1, choosing line
Line 4: Result of probability 0.01 * line 4 is .040000 and is < 1, choosing line
Line 4: Result of probability 0.25 * line 4 is 1.000000 and is >= 1, not choosing line
Line 4: Result of probability 0.5 * line 4 is 2.000000 and is >= 1, not choosing line
Line 4: Result of probability 0.99 * line 4 is 3.960000 and is >= 1, not choosing line

0 .. 0.99999 < ( 1 / FNR ==  .20 )
Line 5: Result of probability 0 * line 5 is 0 and is < 1, choosing line
Line 5: Result of probability 0.01 * line 5 is .050000 and is < 1, choosing line
Line 5: Result of probability 0.25 * line 5 is 1.250000 and is >= 1, not choosing line
Line 5: Result of probability 0.5 * line 5 is 2.500000 and is >= 1, not choosing line
Line 5: Result of probability 0.99 * line 5 is 4.950000 and is >= 1, not choosing line

0 .. 0.99999 < ( 1 / FNR ==  .05 )
Line 20: Result of probability 0 * line 20 is 0 and is < 1, choosing line
Line 20: Result of probability 0.01 * line 20 is .200000 and is < 1, choosing line
Line 20: Result of probability 0.25 * line 20 is 5.000000 and is >= 1, not choosing line
Line 20: Result of probability 0.5 * line 20 is 10.000000 and is >= 1, not choosing line
Line 20: Result of probability 0.99 * line 20 is 19.800000 and is >= 1, not choosing line

0 .. 0.99999 < ( 1 / FNR ==  .01 )
Line 100: Result of probability 0 * line 100 is 0 and is < 1, choosing line
Line 100: Result of probability 0.01 * line 100 is 1.000000 and is >= 1, not choosing line
Line 100: Result of probability 0.25 * line 100 is 25.000000 and is >= 1, not choosing line
Line 100: Result of probability 0.5 * line 100 is 50.000000 and is >= 1, not choosing line
Line 100: Result of probability 0.99 * line 100 is 99.000000 and is >= 1, not choosing line

元の式:

rand() * FNR < 1

数学的には、次のように書き直すことができます。

rand() < 1 / FNR

...これは、行番号が増加するにつれて右側に減少する値を表示するため、より直感的です。方程式右辺の値が減少すると、rand()関数が右辺より小さい値を返す確率はますます小さくなります。

各行番号について、テストする式の表現である範囲と rand() が出力する「1 を行番号で割った値」を印刷します。次に、いくつかのサンプルランダム値を繰り返して、そのランダム値が与えられたときに行が選択されていることを確認します。

見てみる価値があるいくつかの例は次のとおりです。

  • 行1では、rand()は0 <= rand()< 1の範囲の値を生成するため、結果は常に(1 / 1 == 1)未満であるため、常に行1が選択されます。
  • 行2では、任意の値が0.50未満でなければならないことがわかります。これは、2行が選択される確率が50%であることを意味します。
  • 100行目では、rand()はその行を選択するために0.01未満の値を生成する必要があります。

関連情報