ここでは、以下の常識をチェックします:
( ) 内はおおまかな目安時間です。
foo,3 baa,1 john,5 foo,1出力例:
john,5 foo,4 baa,1
| Id | Name | Score |
|---|---|---|
| 1 | Alice | 100 |
| 2 | Bob | 50 |
| 3 | Carol | 75 |
a001.txt, a002.txt, ... という名前のついた
複数のファイルがあるとする。これらの名前を一括して
a001.html, a002.html, ... に変更するような
シェルスクリプトを書け。 (5分)
files.txt 中に一行ずつ書かれている場合は
スクリプトをどう変更すべきか。(5分)
特殊な記号 ({, }, $, *, ;) を 引数に渡す場合は '〜' で囲む。
-t」というファイル中から
* という文字列を検索する grep コマンドを書け。
a="foo" echo "$a" b="$a $a" c='$a $a' d='$a '"$a"
「こんなコマンドを実行したはずなんだけど、なんだっけ」
$ history | grep なんか
注意: historyファイルはときどき消されることがある。
function _prompt_cmd {
local s=$?
echo "`date '+%Y-%m-%d %H:%M:%S'` $HOSTNAME:$$ $PWD ($s) " \
"`history 1`" >> $MYHISTFILE
}
PROMPT_COMMAND=_prompt_cmd
コマンドラインの記録は、そのまま実験ノートにもなる。 あとで実験手順を再現したいときに参考になる。
$ ls -l
$ ls -l | wc
$ ls -l | sort
$ ls -l | sort -k4
$ ls -l | sort -k4 -r
$ ls -l | sort -k4 -r -n
$ ls -l | awk '{print $5;}'
$ ls -l | awk '{a+=$5;}'
$ ls -l | awk 'BEGIN{a=0;} {a+=$5;} END{print(a);}'
$ ls -l | awk '/euske/ {print $4;}' | uniq
$ ls -l | awk '/euske/ {print $4;}' | uniq -c
$ ls -l | awk '/euske/ {print $4;}' | uniq -c | wc
$ find ~ $ find ~ -type f $ find ~ -type f | grep test $ find ~ -type f | grep -i test $ find ~ -type f | grep -i test | wc $ find ~ -type f -name '*test*' $ find ~ -type d -ctime -3 $ find ~ -type d -mtime +3 $ find ~ -type d -mtime -3
shスクリプトはこの行から始める。
#!/bin/sh または #!/bin/bash
$ chmod 755 foo.sh
echo "$0" a=$1 shift b=$1 c="$@"
`コマンド` または $(コマンド)
if 式; then ...; else ...; fi
if 式; then
...
else
...
fi
for 変数 in 式; do
...
done
while read 変数; do
...
done
case 式 in
パターン1)
...
;;
パターン2)
...
;;
*)
...
;;
esac
$ cat files.txt a.txt b.txt c.txt $ cat files.txt | xargs echo $ cat files.txt | xargs cat $ find -type d | xargs ls
UNIXプログラムのお約束:
$ cmd1 input.txt | cmd2 | cmd3 > output.txt
$ cmd1 input.txt
$ cmd1 input.txt > temp.txt $ cmd2 < temp.txt | cmd3 > output.txt
$ cmd1 input.txt | tee temp.txt | cmd2 | cmd3 > output.txt
$ コマンド [オプション] < 入力ファイル > 出力ファイル
$ コマンド [オプション] 入力ファイル
ファイル名が与えられない場合は標準入力を使用する。$ コマンド [オプション] 入力ファイル1 入力ファイル2 ...
こうしておくと
のようにできる。$ find ... | xargs コマンド [オプション]
また、実験パラメータの変更はコードをじかに変更するのではなく、 コマンドラインオプションとして処理すること。
$ exp1 -a1 -p2 -k0 input.txt > output_a1_p2_k0.txt $ exp1 -a2 -p3 -k5 input.txt > output_a2_p3_k5.txt ...
ログは、たとえ人間が読む場合でも、 なるべく機械的に処理できるようにしておくこと。 (grep/awk 等でのおおまかな統計が簡単にとれる。)
print("kokodayo", x, y)
print(f"DEBUG: data read complete: x={x}, y={y}.")
#!/bin/sh exec </dev/null exec >log exec 2>&1 renice +20 -p $$ echo "*** START `date` ***" /usr/bin/time -v 実際のコマンド echo "*** END `date` ***"
Pythonスクリプトは慣例によりこの行から始める。
#!/usr/bin/env python
だいたい以下のようなパターンで書くと、 上に示した「お約束」に沿ったコマンドになる。
import sys
import fileinput
def doit(args):
for line in fileinput.input(args):
print(line)
return
def main(argv):
import getopt
def usage():
print(f'usage: {argv[0]} [-d] [-o output] [file ...]')
return 100
try:
(opts, args) = getopt.getopt(argv[1:], 'do:')
except getopt.GetoptError:
return usage()
debug = 0
output = None
for (k, v) in opts:
if k == '-d': debug += 1
elif k == '-o': output = v
return doit(args)
if __name__ == '__main__': sys.exit(main(sys.argv))
sys.argv でコマンドライン引数を取得。
getopt.getopt でオプションを取得。
usage() でヘルプを表示。
main() 関数はコマンドラインの解析のみをおこない
実際の処理は doit() 関数に正式なパラメータを渡してやらせる。
(これにより、他モジュールから利用することが可能)
$ python test.py input.txt または $ ./test.py input.txt または $ cat input.txt | test.py または $ cat files.txt | xargs test.py
debug により doit() 内の
なんらかの挙動が変わるようにせよ。
-p オプションを追加せよ:
$ test.py -p 4 input.txt
たいていの研究では、複数の対象を異なる条件で実験する。 このような実験条件・実験対象はよく記録し忘れるため、 実験プロセス全体をシェルスクリプトにし、さらにそれを git で管理するのがおすすめ。 (スクリプトとその履歴が実験ノートになる)
基本戦略は、シェルのワイルドカード (*) で ある条件をもったファイルだけを簡単に指定できるようにすることである。
src_exp1_ver2.csv
result-v4-20190803-temp.txt
out.3cddd4d137ad4f794a8ccf0763b4d5a6450934b5
jikken, jiken2, ..., kekka3
jikken_1, jikken_2, ..., jikken_3
jikken_2.input
jikken_2.output
jikken_2.graph
jikken_2.graph.svg
f1, f2, ..., f443
f0001, f0002, ..., f0443
基本的にUNIXのファイル名は逐次探索である。 したがって、あまり1個のディレクトリに沢山のファイルを置くと遅くなる (せいぜい1000個程度)。
それからパス名が長くなりすぎると見にくいし、入力も大変。
input/, output/)
s201909121012/, ...)
data_seg01_p3_q4/, ...)
data_201909121012_seg01_p3_input, ...)
実験結果は、たいていの場合あとで解析可能な形式で記録しておく必要がある。 実験に時間がかかる場合・実験が複数のステージに分かれている場合などは、 その中間的な状態を記録しておく必要がある。
重要: できるだけ既存のツール・ライブラリで処理できるようにする。
おすすめしない。 もしやるとしたら、parseが簡単にできるようにすること。
# コメント +キー1 バリュー1 +キー2 バリュー2 (空行がレコード区切り)
rec = {}
for line in fp:
line = line.strip()
if line.startswith('#'): continue
if line.startswith('+'):
(k,_,v) = line.partition(' ')
rec[k] = v
elif not line:
yield rec
rec = {}
超おすすめしない。
簡単なデータだけならいいかも (たとえば 32ビット列の羅列ひたすら1G個とか)。
「SQLite は fopen() に対抗するために作られた」
複雑な構造 × 膨大な数があるときに使う方法。
ProtocolBuffer, HDF, MongoDB, ...
導入に手間がかかりすぎて、個人でやる実験には向かない。
import csv
with open('output.csv', 'w') as fp:
writer = csv.writer(fp)
writer.writerow(['a', 'b', 'ccc'])
import csv
with open('input.csv') as fp:
for row in csv.reader(fp):
print(row)
import json
with open('output.json', 'w') as fp:
data = {'a':123, b:['x','y']}
fp.write(json.dumps(data))
import json
with open('input.json') as fp:
for line in fp:
data = json.loads(line)
C から SQLite を使う場合は SQLite C/C++ Interface を参照。
import sqlite3
db = sqlite3.connect('data.db')
cur = db.cursor()
cur.executescript('''
CREATE TABLE Student (
Id INTEGER PRIMARY KEY,
Name TEXT,
Score INTEGER);
''')
for (name,id,score) in scores:
cur.execute('INSERT INTO Student VALUES (?, ?, ?);', (id, name, score))
import sqlite3
db = sqlite3.connect('data.db')
cur = db.cursor()
for row in cur.execute('SELECT Name,Id FROM Student;'):
(name,id) = row
SVG (Scalable Vector Graphics) 形式とは、テキスト形式の一種で、 図形を文字によって記述する。
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='300' height='200'> <rect x='10' y='10' width='200' height='100' stroke='#000000' fill='#ff0000' /> <circle cx='200' cy='100' r='50' stroke='#000000' fill='#0000ff' /> </svg>
作成したファイル first.svg は、ブラウザで開くことができる。
すると、以下のような図が表示される:
上の SVG は、以下のような情報を表している:
#000000 (黒) で、内部の塗りは #ff0000 (赤)。
#000000 (黒) で、内部の塗りは #0000ff (青)。
単位はすべてピクセルである。また、色は #RRGGBB のように
赤 (R)、緑 (G)、青 (B) の各原色が
16進数 00 (0) 〜 ff (255) で表されている。
つまり、黒は #000000 であり、白は #ffffff となる。
座標のような数値は '〜' または "〜" で囲む。
first.svg を実際に入力し画面に描画せよ。
#…) にはどのような値を指定すればよいか?
SVG の基本構造は以下のようになっている。
まず <svg> 〜 </svg> で
囲まれる文字列があり、その中に描画コマンドが並んでいる。
<svg> のような文字列を タグ (tag) という。
最初の <svg> タグでは、図形全体の幅と高さをピクセル単位で指定する。
「xmlns='http://www.w3.org/2000/svg' version='1.1'」の部分は固定である。
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='幅' height='高さ'>
...描画コマンド...
</svg>
rect)
<rect x='10' y='10' width='100' height='80' fill='none' stroke='#000000' stroke-width='2' /> <rect x='80' y='60' width='50' height='40' fill='#ffcc00' stroke='#0000ff' stroke-width='4' />
x、y に左上の座標を指定する。
width、height に幅と高さを指定する。
fill には塗る色を指定する。(塗らない場合は none)
stroke には線の色を指定する。(線がない場合は none)
stroke-width には線の幅を指定する。(省略した場合は 1)
<rect ... の最後に必ず /> をつけること。
line)
<line x1='10' y1='10' x2='100' y2='80' stroke='#000000' stroke-width='2' />
x1、y1 に始点の座標を指定する。
x2、y2 に終点の座標を指定する。
stroke, stroke-width は同じ。
polygon)
<polygon points='10,90 50,10 90,90' fill='#00ff00' stroke='#000000' stroke-width='2' />
points に点の座標列を指定する。x,y x,y ... のように 2個ずつ区切って記述する。
(カンマなしで x y x y ... と書いてもよい)
fill、stroke、stroke-width は同じ。
circle, ellipse)
<circle cx='50' cy='50' r='40' fill='none' stroke='#000000' stroke-width='2' /> <ellipse cx='200' cy='50' rx='80' ry='40' fill='#ff00ff' stroke='#000000' stroke-width='2' />
cx、cy に中心座標を指定する。
r (円の場合) または rx、ry (楕円の場合) に半径を指定する。
fill、stroke、stroke-width は同じ。
text)
<rect x='10' y='10' width='200' height='80' fill='none' stroke='#000000' /> <text x='10' y='40' text-anchor='start'>左寄せ</text> <text x='110' y='60' fill='red' text-anchor='middle'>中央寄せ</text> <text x='210' y='80' fill='white' stroke='#000000' text-anchor='end'>右寄せ</text>
x、y に起点の座標を指定する。
text-anchor に起点の方法
(start、middle、end のいずれか) を指定する。left、center、right ではない。
<text>タグはやや特殊で、
表示したい文字列を <text> 〜 </text> で囲む。
なお、複数行にわたる文字は一度に書けない。
fill で指定する。
stroke は文字の縁取り色として使われるので、
通常は none にしておく。
すべての描画コマンドにいちいち stroke や fill を
書くのは面倒くさいので、このような場合は <g> タグによるグループ化を使う。
<g> 〜 </g> で囲んだ部分には、
同じ色・線幅が適用される。
<g fill='none' stroke='#0000ff' stroke-width='2'> <rect x='10' y='10' width='50' height='30' /> <line x1='35' y1='25' x2='100' y2='50' /> <circle cx='100' cy='50' r='30' /> </g>
$ cat foo.gv
digraph G {
Nfoo [label="foo"];
Nbar [label="bar"];
Nbaz [label="baz"];
Nfoo -> Nbar;
Nbar -> Nbaz;
Nfoo -> Nbaz;
}
$ dot -Tsvg foo.gv > foo.svg (GV → SVGに変換)
$ rsvg-convert -f pdf -o foo.pdf foo.svg (SVG → PDFに変換)