CSVの末尾に任意の値を含む列を追加する

CSVの末尾に任意の値を含む列を追加する

ユーザーリストを含むCSVがあり、各ユーザーに一意でランダムに生成されたワンタイムパスワードを含む列を追加したいと思います。

私のスクリプトはうまくいきますが、引き続き行を追加し続けます。変数を設定するためにコードをループから移動すると正常に動作しますが、すべてのユーザーは同じパスワードを取得します。

最後の行で終了するにはどうすればよいですか?

#!/bin/bash
#add column to csv
ORIG_FILE="new-users2.csv"
NEW_FILE="Output.csv"
{ echo `head -1 $ORIG_FILE`",One Time Password" ; tail -n +2 $ORIG_FILE | \
  while read x ; OneTimePass=$(openssl rand -base64 14 | head -c 6) ; do echo "$x,$OneTimePass" ; done ; } > $NEW_FILE

答え1

ループ構文がwhile...do...done正しくありません。あなたはこれを実行しています:

while read x ; OneTimePass=$(openssl rand -base64 14 | head -c 6) ; do ...

キーワードの見積もり形式whileは以下に説明されていますhelp while

$ help while
while: while COMMANDS; do COMMANDS; done
    Execute commands as long as a test succeeds.
    
    Expand and execute COMMANDS as long as the final command in the
    `while' COMMANDS has an exit status of zero.
    
    Exit Status:
    Returns the status of the last command executed.

ここでは2つのコマンドを提供します。read x ;そして、OneTimePass=$(openssl rand -base64 14 | head -c 6)2番目のコマンドは常に機能し、いつでもコマンドを再実行できるため、シャットダウンはありませんopenssl。そのため、whileループは終了せず、さらに多くの行を作成します。あなたが追求するのはこれです:

while read x; do
    something
done

以下は、正しい引用符や変数名の大文字の使用を防ぐなど、他のいくつかの改善点を含む作業バージョンのスクリプトです。

#!/bin/bash

#add column to csv
orig_file="new-users2.csv"
new_file="Output.csv"

printf "%s,%s\n" "$(head -1 "$orig_file")" "One Time Password" > "$new_file"
tail -n +2 "$orig_file" |
  while read -r x; do
    OneTimePass="$(openssl rand -base64 14 | head -c 6)"
    printf '%s,%s\n' "$x" "$OneTimePass"
  done >> "$new_file"

個人的には、ファイル名をハードコーディングすることはスクリプトを使いにくくして使いやすさを落とすので避けたいと思います。入力ファイル名を引数として取り、stdout で印刷することで、目的の出力ファイルを選択できます。

#!/bin/bash

#add column to csv
orig_file=$1

printf "%s,%s\n" "$(head -1 "$orig_file")" "One Time Password"
tail -n +2 "$orig_file" |
  while read -r x; do
    OneTimePass="$(openssl rand -base64 14 | head -c 6)"
    printf '%s,%s\n' "$x" "$OneTimePass"
  done

その後、次のように実行できます。

foo.sh new-users2.csv > Output.csv 

次の入力ファイルでテストしました。

$ cat new-users2.csv 
name,age
Bob,45
Alice,36

結果:

$ foo.sh new-users2.csv 
name,age,One Time Password
Bob,45,BTkLQW
Alice,36,CzQa4U

答え2

使用ミラー、そして恥ずかしく借りた@terdonのサンプルファイル

$ mlr --csv put -S '
    $["One Time Password"] = substr(system("openssl rand -base64 5"),0,5)
' new-users2.csv 
name,age,One Time Password
Bob,45,kIrlrl
Alice,36,h1OBp3

openssl rand6つのBase64文字を生成するために36ビット以上を必要としないので、パラメータを14バイトから5バイトに変更しました。何らかの理由で重要な場合はもう一度変更してください。)

awkの機能を使用して同様のことを実行できますが、system()CSV構造とヘッダーを処理する方が混乱しています。おそらくawkを使用するよりきれいな方法は、getline次のものを使用することです。

$ awk -F, '
    BEGIN {OFS=FS; cmd = "openssl rand -base64 5"} 
    NR==1 {$(NF+1) = "One Time Password"} 
    NR >1 {cmd | getline var; close(cmd); $(NF+1) = substr(var,1,6)} 
    1
' new-users2.csv
name,age,One Time Password
Bob,45,CovLkz
Alice,36,PgLbD4

Millerのバージョンとは異なり、これは「単純な」CSVでのみ機能します(たとえば、フィールドに含まれる「、」文字を処理しません)。

関連情報