シェルスクリプトからid作成

改行を含むレコードがある場合は正しく動かないので
訂正します。
csvはパーサーがないと編集することが難しいので、
シェルスクリプトからpythonを使います。


下の例はpython3
(python2だとprintするときにバイト列になっているかもしれない。)
cat staff.csv | python -c '
import csv
import sys


#idcolumnがいる場合はtrueにする。
idc = True
#外側からデリミタを指定できるようにする。
delimiter = ","

cin = csv.reader(sys.stdin,delimiter=delimiter)
i = 0 if idc else 1
for row in cin:
if i == 0:
row = ["id"] + row
else:
row = [str(i)] + row
print(delimiter.join(row))
i += 1
' | cat >> 指定のファイル等

上の例の場合は
csvのヘッダー付きで出力します。
(使う場合は、変数
idc,delimiterをシェルから
引数を渡せるようにするなどして
外から変更できるように書いてください。)

理解につまずきそうなのは
cin = csv.reader(sys.stdin,delimiter=delimiter)
の部分だろうか。

なぜ、ファイルの指定のはずなのに、
sys.stdinと標準入力をここで渡すことができるのか
わからない人がいるかもしれないが、

厳密に言うと
標準出力もファイルなのでこのようなことができる。

上流に|(パイプ)があるので、
これにより、
下流のpythonで標準入力から、
catで読み込んだ内容を受け取れるというわけである。

これはpython自体の仕様でなく、
コンピュータ自体の仕様
なので、
pythonだろうが、rubyだろうがC言語だろうが
同じようなことができる。

もちろんOSが変わっても同様なことができる
(Windowsのcmdにはパイプはないが、bashのパイプのように
標準入力を渡すという機能があるものがあれば、やはり正しく動く。)


これでとりあえず、pythonのcsvが正しく
パースしている限り、すべての形式のcsvに対して
正しく動く


pythonのこの処理をbashの関数内で呼ぶことも考えたが、
python,ruby,perl等
スクリプト言語に
そのような仕事をさせることは向かないので、
コンパイル言語であるGo言語で後日書く。

!!訂正
ここ以下の文章はcsvに改行を含まないレコードの場合のみ
動くので注意!!(改行を含まないレコードの場合はこれでよい。)

csvファイルなどに
id列を追加したい場合は、
nlコマンドを使う。

下の例はすべて、
6桁まで対応できるが、
idが100万以上必要な場合は
nl -w7
以上にすること。


カラム名がないcsvファイルの場合
$ nl -n 'rz' -s ',' csvファイル名 | sed 's/^0*//'

nlはファイルを行番号付きで表示させるコマンド。nl -nのrzは行番号を右詰めで表示して、長さが足りない場合は左側を0で表示させる。


最後に先頭の0を消去することで、idを表示させている。

カラム名つきのcsvファイルの場合
$ cat csvファイル名 | sed '1s/^/#id,/' | nl -n 'rz' -s ',' -bp^[^¥(#id,¥)] | sed '1s/^ *,#id/id/' | sed 's/^0*//'

解説すると


  1. 最初にカラム名が付いている一行目だけ、#id,とid用のカラム名を足している。#とつけたのは、後でnlで行番号を追加するときにこの行だけ、採番を行わないようにするため。
  2. nl -bpは採番を行う行を正規表現を用いて選択するオプション。nl -bp^[^¥(#id,¥)]で先頭が#id,となっている行だけ除いて採番を行うようにしている。
  3. 最後に (半角スペース何個か)#id,となっている部分をとって、上と同様に左詰めの0を削除している。

#id,から始まる行が他にもあったら、正しく動かないが、まずないはず。


これを関数にまとめると↓のようになる。
function pasteid() {

    if [ -z $1 ]; then
        return 0
    fi
    local filename=$1
 
    if test ! '-c'  = $2 || [ -z $2 ]; then
       nl -n 'rz' -s ',' $filename | sed 's/^0*//'
    else
       cat $filename | sed '1s/^/#id,/' | nl -n 'rz' -s ',' -bp^[^¥(#id,¥)] | sed '1s/^ *,#id/id/' | sed 's/^0*//'
    fi

}


これでファイルにidをつけたものが表示される。

$ pasteid  ファイル名 -c(cはオプションつけなくてもよい。)

ファイルにid列を上書きする場合は下のように行う必要がある。

$ tempfile=$(mktemp)
$ trap 'rm ${tempfile}' EXIT 
$ pasteid ファイル名 -c | cat > $tempfile
$ mv $tempfile ファイル名

一時ファイルの作成、trapで削除については、下のサイトを参考にさせていただきました。
https://hydrocul.github.io/wiki/commands/mktemp.html

コメント

人気の投稿