php はホスティングなどのリソースを上手に扱う関係上、実行時間が限られてる。
なので、終了しない無限ループをキックしようと思うとこんな感じなる。
#!/usr/bin/env php <?php set_time_limit(0); # forever program!
どこで使うかというと、WEBから起動する場合に、apacheが起動させちゃうもので、ずっと動画配信したいものなど。
この動画なんだっけ?wmv?avi? mpegts ?
ファイル名の変換をミスった。拡張子がトンだ。もうしにたい。仕方ないのでコーデック情報見ながら各種データの拡張子を探ることに
ffmpeg で動画の情報を見るには ffprobe を使う。
ffprobe -i movie.mov
まぁこれだけでも見るだけなら 標準エラーにffmpeg と同じ情報が出てきてログ見たらわかるんだけど。もっと手軽にほしいところだけ見たいな的な。
ネット見てたら、ffmpeg/ffprobe のエラーストリームへログをgrep してたりしてたいへんだ。
-print_format json
ffprobe には show_format があってJSONでストリーム情報を取り出せる。
ffprobe -i movie.mov -show_streams -print_format json
これで、ストリーム情報を取り出せる。
サポートしてるフォーマットとかの情報がうるさいので黙らせる。-loglevel quiet
で指定する。
ffprobe -i movie.mov -loglevel quiet -show_streams -print_format json
-print_format json
と jq
を組み合わせる。ここまで来たら後は楽ちん。 jq と組合あせて戦う。
takuya@Desktop$ ffprobe -loglevel quiet -show_streams -print_format json "$f" | jq '.streams[] | {"index":.index,"codec_type":.codec_type,"codec_name":.codec_name} ' -r { "index": 0, "codec_type": "audio", "codec_name": "aac" } { "index": 1, "codec_type": "video", "codec_name": "h264" } { "index": 2, "codec_type": "data", "codec_name": null } { "index": 3, "codec_type": "data", "codec_name": null } takuya@Desktop$
ffprobe -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name -r
function ffmpeg_show_video_codec() f=$1 ffprobe -loglevel quiet -show_streams -print_format json "$f" | jq .streams[0].codec_name -r }
コレで行けそうですね。
json と jq はどうしても煩雑なので、 XML+XPATH のほうが楽だった。
-print_format xml
XML で出力してXPATHの方が //
の省略記法があるので圧倒的に楽
ffprobe /Users/takuya/.Desktops/2017-02-10/IMG_4046.MOV-1.mov -loglevel quiet -show_streams -print_format xml | xpath '//stream/@codec_name'
やっぱりJSONめんどくさいんだよ。読みやすいけど、コードが煩雑。廃れてほしい。*1
などがある
formats 以外に見れるものもある。
ffprobe の man を眺めているとアレコレあるみたい
-show_data -show_data_hash algorithm -show_error -show_format -show_format_entry name -show_entries section_entries -show_packets -show_frames -show_streams -show_programs -show_chapters
時間無くて調べきれない。-show_programs
番組のprogram_id などが見れる TS のストリーム情報などはコッチ見た方がいいのかな。
動画の拡張子を wmv / mp4 にわける。
#!/usr/bin/env bash for i in ./movies/* ; do name=$(ffprobe -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name -r) echo $name $i case "$name" in wmv*) : echo mv "$i" "${i/.*/.wmv}" # mv "$i" "${i/.*/.wmv}" ;; h264|aac|mpeg4) : echo mv "$i" "${i/.*/mp4}" ;; esac done
alias ffmpeg_info_json ='$ ffprobe -loglevel quiet -show_streams -print_format json -i '
qpdf があればコピーを不可、編集不可、閲覧不可などのパスワードをつけることが出来る
--encrypt
qpdf --encrypt パスワード1 パスワード2 キー フラグ -- in.pdf out.pdf
最後にいれる --
はオプションフラグの識別のために必要
-- in.pdf out.pdf
USER_PASSWD='ユーザーパスワード' OWNER_PASSWD='オーナパスワード' qpdf --encrypt $USER_PASSWD $OWNER_PASSWD 40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf
Mac の場合アイコンも変わる。
プレビューでも開けなくなってる。
誰でも閲覧は出来るが、編集不可で印刷不可にする場合
ユーザーパスワードには「空文字」が許可されるので、空文字を入れることで、オーナパスワードだけをつけることが出来る。
OWNER_PASSWD='オーナパスワード' qpdf --encrypt '' $OWNER_PASSWD 40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf
印刷するには、このパスワードダイアログ出てくる。
でもqpdf で閲覧状態のPDFをdecrypt して解除できちゃうんだけどね。知らない人にはちょっとプレッシャーをかけることが出来る。
networksetup が便利なんだけど。これを使うと毎回毎回にダイアログが出る。
/usr/sbin/networksetup
sudo したら解決するらしい。なんでや。。。
sudo /usr/sbin/networksetup
sudo で解決するので、sudo つけるか、sudoers に コマンド追加したら解決しますね。
takuya@Desktop$ ll /usr/sbin/networksetup -rwxr-xr-x 1 root wheel 199K 2016-07-09 12:03 /usr/sbin/networksetup
これだから、suid を付加しても解決する。ただしSIP(system integrity protection) があるのでオフにしないと解決できない。
chmod u+s /usr/sbin/networksetup
データベースにアクセスしてデータを扱うには Sequel が便利。ActiveRecordもいいんだけどアレはめんどくさい。
SQLに極めて近い構造で余計なことはなにもなく便利でいい。
gem install sequel
DB = Sequel.connect('sqlite://./sample.db') DB = Sequel.connect('mysql2://takuya:**pass**@192.168.100.1/takuya_db',:loggers => [Logger.new($stdout)])
DB.create_table!(:table) do primary_key :id String :user_name , :unique=>true Char :role, default: 'u' check(:role=>%w[a o u]) unique [:user_id, :group_id, :role] end
シンボルで、ハッシュキーでアクセスする。面倒なときはクラスを定義する(後述)
res = db[:tablename] res.all.each{|e| puts res[:id] puts res[:name] }
obj = { name: "takuya"} table = DB[:tablename] table.insert(obj)
table = DB[:tablename] table = table.where( 'id > 1000' ); table.each{|r| puts r[:id] }
res = db[:tablename].where('id > 100') res = db[:tablename].where('id > 100').where(' id < 1000 ')
res = db[:tablename].where('id > 100').where(' id < 1000 ') res.sql
table = DB[:tablename] table = table.where( 'id = 1000' ).imit(1); table.delete
table = DB[:tablename] table = table.where( 'id = 1000' ).imit(1); obj = { name: "takuya"} table.update(obj)
obj = { name: str1 , path: str2 } begin table = DB[:user_file_info] table.insert(obj) rescue Sequel::UniqueConstraintViolation => e puts e.backtrace raise e end
prepare = DB[:users].where('name like :n').prepare(:select,:select_by_name) prepare.call(:n=>"%#{e}%")
DB.create_table! # テーブルを削除して作る(データ消消える DB.create_table? # テーブルがなければ作る DB.create_table # テーブルがあればエラーになる
DB.create_table(:table) do primary_key :id foreign_key :user_id, :user end
次のようにカスケードを指定する場合もあり。
foreign_key :user_id, :user, :on_update=>true foreign_key :user_id, :user, :on_update=>true , :on_delte=>true foreign_key :user_id, :user, :on_update=>true , :on_delte=>true,:deferrable=>true
DB.create_table(:table) do primary_key :id Integer :user_id String :user_name foreign_key [:user_id, :user_name], :user end
DB.create_table(:table) do primary_key :id Integer :user_id Integer :group_id String : role # 複数カラムの場合 unique [:user_id, :group_id, :role]
デフォルト制約 role を [‘r’,‘o’,‘u’,] に制限する場合。
check(:role=>%w[a o u])
クラス名を指定して扱える。テーブル名をハッシュで指定する。
class ImageCache < Sequel::Model(:image_cache) end r = ImageCache.all.first r.url # ドットでアクセス可能になる。
テーブル名を省略するとクラス名の複数名になる。
class User < Sequel::Model(:users); end class User < Sequel::Model; end # 省略可能
ActiveRecordじゃないとだめな人には此の方が便利かもしれない。
irb でいっかいテーブルのクラス定義をしてしまうと、再定義がめんどくさいので。一旦消す方がいい。
pry を再起動せずに、クラスを消すには remove_const するのが楽
class UserInfoTable < Sequel::Model(:user_info_table); end Object.class_eval { remove_const :UserInfoTable }
バイナリを突っ込もうとしたらエラーになったことがあるので明示的にblob を指定する必要があった ( sqlite のとき )
obj = { image_url: image_url, image_data:Sequel.blob(bin) } table = DB[:image_cache] table.insert(obj)
ちょっとしたデータ処理でSQLでデータを格納したり、処理したりするのに便利。
ActiveRecordのようなモデルベースだと「アプリ」を使うには便利なんだけど、テーブルデータを触ったり増やしたりするにはとても不便なので、SequelはちょっとめんどくさいSQLをサボるのに便利だと思う。
http://stackoverflow.com/questions/25657286/ruby-sequel-error-table-already-exists http://stackoverflow.com/questions/10196624/how-to-check-constraints-in-sequel
http://takuya-1st.hatenablog.jp/entry/2015/12/28/204019
https://github.com/jeremyevans/sequel/blob/master/doc/schema_modification.rdoc
昔、教授が使ってて、なんやこれ!楽しそうと思ったファイラ。「これDOSですか?」「いいやちがうよ、古い頃から使ってるMS-DOSみたいなもんだな」「なんで?Finderあるじゃないですか?」「コレがいいんだよコマンドからはなれるとコレが使いやすいんだよ」
当時は何のことを指しているのか全く理解できませんでした。
MS-DOS時代を彷彿とさせるユーザーインターフェース。楽しそう。でも名前を忘れてて何かわからなかった。試すこともしなかった。
今日ふとした瞬間に見つけて懐かしい感じなにった。
Midnight Commander はビジュアルファイルマネージャです。ファイルやディレクトリー全体のコピー・移動・削除や、ファイルの検索、サブシェルでのコマンドの実行ができます。ビューアやエディタも内蔵しています。 Ncurses や S-Lang のような汎用のテキストインターフェイスを使っているため、通常のコンソール、X Window ターミナル、または SSH 接続を介してリモートシェルでも動作します。
takuya@~$ brew install midnight-commander
sudo apt install mc
(Terminal.appの方が自然に動く気がする)
takuya@~$ mc
マウスが使える(!)ので操作は最初はマウスからなれる
ファンクションキーで操作するあたりが、もうね、もうね、DOS時代です。
ファンクションキーの存在意義が曖昧にな現代のコンピュータで、ガンガンファンクションキーを使いまくる。
ファンクションキーに気づくことが出来る。F1-10が昔は如何様に使われいたか、そして現代ははどうなったのかを一瞬で体験できる。
SSH経由でターミナルだと、ファイル名を補完したり入力するのすらめんどくさいことが多いので、コレは最高だ。
NASのリモートサーバーにMC入れたら捗る。 ファイルの管理がすごく楽になった。コマンドだと ls 叩きまくりだしなぁ
とくに日本語ファイルが多くて補完が大変なときなどに重宝する。
もう、bash起動する必要なかったら、mc 直接起動しちゃえばいいんじゃないかな。
command="mc" ssh-rsa AABIwAAAQEA4pJgjWmRfoAZ6Ippi
まぁEmacsやVim NERDTree使えって話なんだろうけどね。
最初はとっつきにくいかもしれないけど、VNCやRDP上げてファイル操作するより、インストールも設定も楽だと思います。 なによりパケット量気にしない使えるのも嬉しい。
欠点といえば、画像をプレビューできないところくらいか。
昔からの人には当たり前なことだと思う。教授も日常で使ってたし。
昔からのCLI経由のインターフェースって完成されてるよね。。
http://takuya-1st.hatenablog.jp/entry/20110813/1313252992
takuya@:sites-available$ dig soa google.co.jp +short ns2.google.com. dns-admin.google.com. 149484063 900 900 1800 60 ^^^ ^^ ^^ ^^ ^^ │ │ │ │ └ minimun │ │ │ └─ expire │ │ └─ retry │ └─ refresh └─ serial
レコードを新規追加すると、通常だと次のような流れになります。
先に問い合わせが来てしまった場合。
プライマリ→セカンダリの同期前にセカンダリに問い合わせると、セカンダリにはレコードがなく問い合わせ結果がネガティブになり、その情報をSOAに従ってネガティブキャッシュしてしまう。なのでますます反映が遅くなることがある。
一般の人(NS運営してない)がSOAで見る情報はなにより、ネガティブキャッシュ設定だと思います。
なんでこんなことを調べてるのかというとV◯lue-d◯mainの更新の遅さに苦労しててちょっと調べてたわけです。
ひさしくpython2.7 だし全然不満がないのですが。python3.6 の文字列展開とjinja2 が便利そうだしそろそろ python3 使ってみようと思い立って、まずは基本的なリクエストを投げるところから。
urllib.request は 公式ドキュメントにすら Requests を使ったら?と書かれてたりする。 まぁ、そうなんだけど。py3から使い方も変わったしおさらいしておくことにする。
import urllib,urllib.request url = 'https://t.co' req = urllib.request.Request(url=url) f = urllib.request.urlopen(req) print(f.read().decode('utf-8'))
Request
を作って urlopen(request)
するのが基本的な流れ。
もちろんGETするだけならもっとシンプルに使える
urllib.request.urlopen(url)
でも、getして終わりってわけじゃないよね。プログラム言語を使い込んでいくならかならず POST/GET/PUT と JSON の送信なんかが出て来る。
#!/usr/bin/env python3 import http.client import urllib import urllib.request ## phpinfo の内容をjson で返してくれるURL url = 'http://[::1]/~takuya/info_json.php' ## GET SAMPLE def sample_get(): req = urllib.request.Request(url=url) f = urllib.request.urlopen(req) print(f.read().decode('utf-8')) ## POST SAMPLE def sample_post(): req = urllib.request.Request(url=url, data=b'var=1') f = urllib.request.urlopen(req) print(f.read().decode('utf-8')) ## POST with Custom HEADER def sample_post_with_header(): headers={ 'Content-type':'application/json' } req = urllib.request.Request(url=url,headers=headers, data=b'{ "a" : 1 }') f = urllib.request.urlopen(req) print(f.read().decode('utf-8')) ## POST json with Custom HEADER def sample_post_with_header_and_json(): headers={ 'Content-type':'application/json' } json_str='{"a":1}' req = urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8')) f = urllib.request.urlopen(req) print(f.read().decode('utf-8')) ## PUT json with Custom HEADER def sample_put_with_header_and_json(): headers={ 'Content-type':'application/json' } json_str='{"a":1}' req = urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8'), method='put') f = urllib.request.urlopen(req) print(f.read().decode('utf-8'))
HTTP(s)のリクエストを送信するときにアレコレする場合は、すべてurllib.request.Request
を作って、それを open
することになるとわかった。
引数増やすだけで対応できるので楽ですねぇ。
urllib.request.Request(url=url) # simple GET urllib.request.Request(url=url, data=b'var=1') # POSTする urllib.request.Request(url=url,headers=headers, data=b'var=1') # HEADERつける urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8'), method='put')
ハマりどころは、POSTデータ byte 型になるところ
data=b'var=1' # または data=str.encode('utf-8')
などとやっておかないとエラーになる。油断してると忘れそうになる。
これで各種APIを叩くのが楽になりますね。
https://docs.python.org/3.4/library/urllib.request.html#module-urllib.request
GETはすぐ覚えらるけど、GETと同等に使うPOSTはちょっとコツが居る。
curl で POST などの HTTP のメソッドを指定するには、 -X HTTP_METHOD
をつけて method を指定する。
curl -X GET curl -X DELETE curl -X POST --data 'name=value&id=1' curl -X PUT --data 'name=value&id=1'
POSTと ファイル名指定の -F と合わせ技
curl -X POST -F name=@path/to/name curl -X PUT -F name=@path/to/name
JSONを送る場合は、大抵の場合がPOSTなので、POSTと合わせて、--data
で指定する。
curl -X POST --data '{"id":1}' curl -X PUT --data '{"id":1}'
content-typeを状況により追加で付ける必要がある。
curl -X POST --data '{"id":1}' -H "Content-Type: application/json" curl -X PUT --data '{"id":1}' -H "Content-Type: application/json"
--data
つけるとPOSTわたしはPOSTやPUTであることを明示することで自分自身への確認としていますが。
--data
をつけるだけでPOSTになる・
つまり、次の2つは同じと言える。
curl -X POST --data '{"id":1}' curl --data '{"id":1}'
RESTなAPI好きなんだけど、HTTPメソッドで切り分けるとすると、リクエスト側でメソッド指定するのが不便ですねぇ。
自分で探すときに検索でマッチしないのでキーワードを追加。
URLとPOST/GETのデータ以外に指定する項目が多すぎて嫌になる。
curl "https://api.example.com/api/v4/settings" \ -H "X-Auth-Email: takuya@example.com" \ -H "X-Auth-Key: gfhfdertgfdsertgf" \ -H "X-Auth-SID: xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json"
おおくが、HTTPに沿った認証してないない。認証API叩いて、セッションキーでCookie発行や、Digestで認証してくれたらいいのにな。。。
毎回指定しなきゃいけないヘッダは、省略する。
header ="X-Auth-Email: takuya@example.com" \ header ="X-Auth-Key: gfhfdertgfdsertgf" \ header ="X-Auth-SID: xxxxxxxxxxxxxxxx" \ header ="Content-Type: application/json"
curl -K server.com.curlrc "https://api.example.com/api/v4/settings"
かなりシンプルになる。
さらにaliasする
alias curl.server.com='curl -K server.com.curlrc '
curl.server.com "https://api.example.com/api/v4/settings" curl.server.com "https://api.example.com/api/v4/update" -X PUT --data '{}'
余計なことを考えずにURIとPOSTデータだけに集中できる。
個人的には、APIが HTTPの jsonで返す意味があんまりないと思うんだけよね。、ぶっちゃけ、GETクエリで検索条件を絞り込めないほうが不便なんだよね。マルっと一括して取ってくるプログラムと、ちまちま実行するUNIXパイプ志向との発想の違いなんだろうけど。欲しいデータだけをGETクエリで指定しちゃうほうが楽だよね。
api もっとシンプルにしてくれたらいいのに、無駄な労力書けて複雑なAPI作って、使う方も複雑になって大変です。
curl でよく使う設定をまとめる ~/.curlrc - それマグで!
なんでなんだろうね。アルファベット順よりも文字列長さ順でソートすると、出力がきれいに見えるんだよね。
command | awk '{print length() ,$0}' | sort -nr
takuya@~$ echo -e ${PATH//:/'\n'} | awk '{print length() ,$0}' | sort -nr 26 /Users/takuya/.rbenv/shims 26 /Users/takuya/.pyenv/shims 24 /usr/local/share/npm/bin 24 /Users/takuya/.pyenv/bin 18 /Users/takuya/.bin 15 /usr/local/sbin 14 /usr/local/bin 12 /opt/X11/bin 9 /usr/sbin 8 /usr/bin 5 /sbin 4 /bin
ああ、美しい。
takuya@~$ echo -e ${PATH//:/'\n'} | awk '{print length() ,$0}' | sort -nr | awk '{ print $2 }' /Users/takuya/.rbenv/shims /Users/takuya/.pyenv/shims /usr/local/share/npm/bin /Users/takuya/.pyenv/bin /Users/takuya/.bin /usr/local/sbin /usr/local/bin /opt/X11/bin /usr/sbin /usr/bin /sbin /bin
うん、美しい。
とくにPATHは文字列が長いほど、優先度が高く、/bin
などの共有コマンドを上書きする側になるので、コレが便利。
HDDのアクセスを早くしたい。Writeおせーんだわ。
あれこれ、実装があるけど lvm cache / dm-cache より、bcacheの方が圧倒的に簡単で速かった。
もちろん、こんなことをしなくても、よく使うデータはHDDからメモリにキャッシュされるので、実運用ではメモリに空き容量を増やすのがとにかく先決です。bcacheは趣味ですね。
sudo apt install bcache-tools
git から取得しインストール。
git clone https://github.com/g2p/bcache-tools.git cd bcache-tools make sudo checkinstall \ --pkgname=bcache-head \ --pkgversion="1:$(date +%Y%m%d%H%M)" \ --backup=no \ --deldoc=yes \ --fstrans=no \ --default
bcacheが使えるかどうか確認。
takuya@:~$ sudo modprobe bcache takuya@:~$ sudo lsmod | grep cache bcache 193649 0 dm_cache 41114 0 dm_persistent_data 49347 1 dm_cache dm_bio_prison 13056 1 dm_cache fscache 45542 1 nfs mbcache 17171 1 ext4 dm_mod 89405 17 dm_persistent_data,dm_cache,dm_bufio
今回は、LVM上のVGから100GB のストレージを切り出してブロックデバイスを作成して/dev/sda などの代わりに使う。
これをバッキングデバイスに指定した。
takuya@:~$ sudo make-bcache -B /dev/mapper/data-storage UUID: a057b697-795d-450c-8e68-454d6f244b93 Set UUID: 9778b366-5f4c-4947-a180-c66442f2a934 version: 1 block_size: 1 data_offset: 16
今回はSSD上のLVMから20GB ほど切り出して/dev/sdb などの代わりに使う。
これをキャッシュデバイスに指定した
takuya@:~$ sudo make-bcache -C /dev/mapper/acid-ssd UUID: 9ac55038-1b95-42fb-bd0f-3b4638476e5d Set UUID: aad94dbd-5ded-4d23-bccb-1f3c1f475c48 version: 0 nbuckets: 40960 block_size: 1 bucket_size: 1024 nr_in_set: 1 nr_this_dev: 0 first_bucket: 1
bacheデバイスが見えるようになったか確認する。
takuya@:~$ ls -l -R /dev/bcache** brw-rw---- 1 root disk 253, 0 2017-03-06 18:21 /dev/bcache0 /dev/bcache: 合計 0 drwxr-xr-x 2 root root 60 2017-03-06 18:21 by-uuid /dev/bcache/by-uuid: 合計 0 lrwxrwxrwx 1 root root 13 2017-03-06 18:21 a057b697-795d-450c-8e68-454d6f244b93 -> ../../bcache0
ここで作成された /dev/bcache0/
が bcacheでキャッシュされた、バッキングデバイス(/dev/sda)などを指している。
takuya@:~$ sudo bcache-super-show /dev/mapper/data-storage sb.magic ok sb.first_sector 8 [match] sb.csum D1AA8DCE13E9B35C [match] sb.version 1 [backing device] dev.label (empty) dev.uuid a057b697-795d-450c-8e68-454d6f244b93 dev.sectors_per_block 1 dev.sectors_per_bucket 1024 dev.data.first_sector 16 dev.data.cache_mode 0 [writethrough] dev.data.cache_state 0 [detached] cset.uuid 9778b366-5f4c-4947-a180-c66442f2a934
takuya@:~$ sudo bcache-super-show /dev/mapper/acid-ssd sb.magic ok sb.first_sector 8 [match] sb.csum 455541E08344F7F6 [match] sb.version 3 [cache device] dev.label (empty) dev.uuid 9ac55038-1b95-42fb-bd0f-3b4638476e5d dev.sectors_per_block 1 dev.sectors_per_bucket 1024 dev.cache.first_sector 1024 dev.cache.cache_sectors 41942016 dev.cache.total_sectors 41943040 dev.cache.ordered yes dev.cache.discard no dev.cache.pos 0 dev.cache.replacement 0 [lru] cset.uuid aad94dbd-5ded-4d23-bccb-1f3c1f475c48
sudo bcache-super-show /dev/mapper/acid-ssd \ | grep cset.uuid | awk '{ print $2 }' \ | sudo tee /sys/block/bcache0/bcache/attach
ここまでで、bcacheの準備ができたので、後はターゲット(/dev/bcache0)に対して操作をする。
takuya@:~$ sudo mkfs.ext4 /dev/bcache0 mke2fs 1.42.12 (29-Aug-2014) Discarding device blocks: done Creating filesystem with 26214398 4k blocks and 6553600 inodes Filesystem UUID: 5848c161-fd7c-4031-807a-01c56dc7c967 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424, 20480000, 23887872 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done
sudo mount /dev/bcache0 mnt
細かいテスト条件を考える暇がなかったのでかなり適当な条件ですが1GB程度の書込みで測定してみました。
時間がなくて、細かいテスト条件を考慮できてないので、単純にbw だけを見てみる。 読み込みは今回測定してない
ランダム書込みの測定
デバイス | 速度(bw) |
---|---|
ssdのみ | 242557KB/s |
hddのみ | 910KB/s |
bcache/writethrough | 1314KB/s |
bcache/writeback | 144015KB/s |
1GB程度のランダムデータは差が・・・でもまぁ少しは早くなる?
writebackで ssd にだけ書いて hdd にsync 待ちしないならそりゃ圧倒的に早いな
何かに使えそうですよね。
とりあえず、fio でぱぱっと測定。測定条件を全く考慮せず使いまわした・・・次やるときはちゃんと測定条件を考えないと。
[global] bs=4k ioengine=libaio iodepth=4 size=1g direct=1 runtime=60 directory=/home/takuya/mnt filename=ssd.test.file [rand-write] rw=randwrite stonewall
takuya@:~$ sudo fio myjob.fio rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4 fio-2.1.11 Starting 1 process rand-write: Laying out IO file(s) (1 file(s) / 1024MB) Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/1576KB/0KB /s] [0/394/0 iops] [eta 00m:00s] rand-write: (groupid=0, jobs=1): err= 0: pid=2016: Mon Mar 6 18:44:02 2017 write: io=78864KB, bw=1314.3KB/s, iops=328, runt= 60008msec slat (usec): min=6, max=757302, avg=104.79, stdev=5956.73 clat (usec): min=78, max=2122.7K, avg=12067.09, stdev=45932.99 lat (usec): min=89, max=2122.7K, avg=12172.05, stdev=46560.28 clat percentiles (usec): | 1.00th=[ 169], 5.00th=[ 207], 10.00th=[ 251], 20.00th=[ 5600], | 30.00th=[ 6688], 40.00th=[ 7136], 50.00th=[ 7392], 60.00th=[ 7648], | 70.00th=[ 7968], 80.00th=[ 8768], 90.00th=[11584], 95.00th=[20864], | 99.00th=[130560], 99.50th=[144384], 99.90th=[602112], 99.95th=[839680], | 99.99th=[2113536] bw (KB /s): min= 14, max= 3093, per=100.00%, avg=1438.70, stdev=699.48 lat (usec) : 100=0.07%, 250=9.92%, 500=1.09%, 750=0.29%, 1000=0.27% lat (msec) : 2=0.31%, 4=1.37%, 10=74.64%, 20=6.77%, 50=2.80% lat (msec) : 100=0.22%, 250=1.94%, 500=0.12%, 750=0.14%, 1000=0.04% lat (msec) : >=2000=0.02% cpu : usr=0.22%, sys=1.58%, ctx=17090, majf=0, minf=8 IO depths : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=0/w=19716/d=0, short=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=4 Run status group 0 (all jobs): WRITE: io=78864KB, aggrb=1314KB/s, minb=1314KB/s, maxb=1314KB/s, mint=60008msec, maxt=60008msec Disk stats (read/write): bcache0: ios=0/21318, merge=0/0, ticks=0/419156, in_queue=0, util=0.00%, aggrios=0/21324, aggrmerge=0/0, aggrticks=0/260408, aggrin_queue=260408, aggrutil=98.78% dm-4: ios=0/21434, merge=0/0, ticks=0/510812, in_queue=510812, util=98.78%, aggrios=0/19972, aggrmerge=0/1474, aggrticks=0/446100, aggrin_queue=446092, aggrutil=98.78% sda: ios=0/19972, merge=0/1474, ticks=0/446100, in_queue=446092, util=98.78% dm-5: ios=0/21215, merge=0/0, ticks=0/10004, in_queue=10004, util=5.14%, aggrios=0/26443, aggrmerge=0/2553, aggrticks=0/79524, aggrin_queue=79516, aggrutil=7.90% sdb: ios=0/26443, merge=0/2553, ticks=0/79524, in_queue=79516, util=7.90%
キャッシュデバイスに利用したSSDからLVMを切り出して使うと次の速度だった。
takuya@:~$ fio myjob.fio rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4 fio-2.1.11 Starting 1 process Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/237.4MB/0KB /s] [0/60.8K/0 iops] [eta 00m:00s] rand-write: (groupid=0, jobs=1): err= 0: pid=2146: Mon Mar 6 18:47:09 2017 write: io=1024.0MB, bw=242557KB/s, iops=60639, runt= 4323msec slat (usec): min=2, max=1436, avg= 7.19, stdev=13.71 clat (usec): min=12, max=48210, avg=57.96, stdev=215.17 lat (usec): min=26, max=48214, avg=65.23, stdev=216.07 clat percentiles (usec): | 1.00th=[ 33], 5.00th=[ 36], 10.00th=[ 37], 20.00th=[ 39], | 30.00th=[ 40], 40.00th=[ 41], 50.00th=[ 41], 60.00th=[ 42], | 70.00th=[ 43], 80.00th=[ 46], 90.00th=[ 58], 95.00th=[ 231], | 99.00th=[ 237], 99.50th=[ 241], 99.90th=[ 684], 99.95th=[ 1160], | 99.99th=[ 5856] bw (KB /s): min=169536, max=258520, per=99.67%, avg=241758.00, stdev=30286.36 lat (usec) : 20=0.01%, 50=83.98%, 100=9.08%, 250=6.55%, 500=0.24% lat (usec) : 750=0.05%, 1000=0.02% lat (msec) : 2=0.06%, 4=0.01%, 10=0.01%, 20=0.01%, 50=0.01% cpu : usr=11.29%, sys=44.42%, ctx=154698, majf=0, minf=8 IO depths : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=0/w=262144/d=0, short=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=4 Run status group 0 (all jobs): WRITE: io=1024.0MB, aggrb=242557KB/s, minb=242557KB/s, maxb=242557KB/s, mint=4323msec, maxt=4323msec Disk stats (read/write): dm-1: ios=644/251352, merge=0/0, ticks=144/14056, in_queue=14208, util=97.55%, aggrios=644/262180, aggrmerge=0/15, aggrticks=144/18640, aggrin_queue=18752, aggrutil=96.11% sdb: ios=644/262180, merge=0/15, ticks=144/18640, in_queue=18752, util=96.11%
takuya@:~$ sudo fio myjob.fio rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4 fio-2.1.11 Starting 1 process rand-write: Laying out IO file(s) (1 file(s) / 1024MB) Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/1420KB/0KB /s] [0/355/0 iops] [eta 00m:00s] rand-write: (groupid=0, jobs=1): err= 0: pid=2249: Mon Mar 6 18:50:09 2017 write: io=54776KB, bw=931985B/s, iops=227, runt= 60184msec slat (usec): min=5, max=371161, avg=137.76, stdev=5134.81 clat (usec): min=101, max=883763, avg=17428.75, stdev=51339.16 lat (usec): min=126, max=883769, avg=17566.73, stdev=51900.52 clat percentiles (usec): | 1.00th=[ 171], 5.00th=[ 209], 10.00th=[ 223], 20.00th=[ 5600], | 30.00th=[ 6752], 40.00th=[ 7200], 50.00th=[ 7456], 60.00th=[ 7712], | 70.00th=[ 8160], 80.00th=[ 9280], 90.00th=[17792], 95.00th=[125440], | 99.00th=[189440], 99.50th=[261120], 99.90th=[684032], 99.95th=[880640], | 99.99th=[880640] bw (KB /s): min= 17, max= 3032, per=100.00%, avg=991.52, stdev=724.89 lat (usec) : 250=11.17%, 500=0.91%, 750=0.04%, 1000=0.01% lat (msec) : 2=0.24%, 4=1.22%, 10=71.24%, 20=7.80%, 50=1.29% lat (msec) : 100=0.06%, 250=5.26%, 500=0.48%, 750=0.19%, 1000=0.09% cpu : usr=0.19%, sys=1.14%, ctx=11227, majf=0, minf=8 IO depths : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=0/w=13694/d=0, short=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=4 Run status group 0 (all jobs): WRITE: io=54776KB, aggrb=910KB/s, minb=910KB/s, maxb=910KB/s, mint=60184msec, maxt=60184msec Disk stats (read/write): dm-6: ios=0/14883, merge=0/0, ticks=0/462800, in_queue=473700, util=99.95%, aggrios=0/13821, aggrmerge=0/1075, aggrticks=0/443268, aggrin_queue=450992, aggrutil=99.95% sda: ios=0/13821, merge=0/1075, ticks=0/443268, in_queue=450992, util=99.95% takuya@:~$
takuya@:~$ echo writeback | sudo tee /sys/block/bcache0/bcache/cache_mode writeback
takuya@:~$ sudo fio myjob.fio rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4 fio-2.1.11 Starting 1 process Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/151.5MB/0KB /s] [0/38.8K/0 iops] [eta 00m:00s] rand-write: (groupid=0, jobs=1): err= 0: pid=2341: Mon Mar 6 18:54:19 2017 write: io=1024.0MB, bw=144015KB/s, iops=36003, runt= 7281msec slat (usec): min=1, max=6871, avg=12.32, stdev=16.83 clat (usec): min=19, max=14755, avg=98.10, stdev=368.31 lat (usec): min=28, max=14760, avg=110.48, stdev=368.54 clat percentiles (usec): | 1.00th=[ 34], 5.00th=[ 38], 10.00th=[ 39], 20.00th=[ 40], | 30.00th=[ 41], 40.00th=[ 43], 50.00th=[ 43], 60.00th=[ 44], | 70.00th=[ 46], 80.00th=[ 50], 90.00th=[ 63], 95.00th=[ 157], | 99.00th=[ 1864], 99.50th=[ 2480], 99.90th=[ 3152], 99.95th=[ 5024], | 99.99th=[13504] bw (KB /s): min=136680, max=161360, per=99.65%, avg=143503.93, stdev=6617.05 lat (usec) : 20=0.01%, 50=79.82%, 100=14.01%, 250=2.88%, 500=0.19% lat (usec) : 750=0.67%, 1000=0.42% lat (msec) : 2=1.12%, 4=0.81%, 10=0.06%, 20=0.03% cpu : usr=5.00%, sys=44.67%, ctx=67203, majf=0, minf=7 IO depths : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=0/w=262144/d=0, short=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=4 Run status group 0 (all jobs): WRITE: io=1024.0MB, aggrb=144015KB/s, minb=144015KB/s, maxb=144015KB/s, mint=7281msec, maxt=7281msec Disk stats (read/write): bcache0: ios=223/257488, merge=0/0, ticks=28/26512, in_queue=0, util=0.00%, aggrios=111/132361, aggrmerge=0/0, aggrticks=14/13326, aggrin_queue=13352, aggrutil=95.97% dm-4: ios=0/1, merge=0/0, ticks=0/0, in_queue=0, util=0.00%, aggrios=0/1, aggrmerge=0/0, aggrticks=0/0, aggrin_queue=0, aggrutil=0.00% sda: ios=0/1, merge=0/0, ticks=0/0, in_queue=0, util=0.00% dm-5: ios=223/264721, merge=0/0, ticks=28/26652, in_queue=26704, util=95.97%, aggrios=223/258582, aggrmerge=0/6444, aggrticks=28/15168, aggrin_queue=15116, aggrutil=95.91% sdb: ios=223/258582, merge=0/6444, ticks=28/15168, in_queue=15116, util=95.91% takuya@:~$
シーケンシャルIOの検出・バイパスオフ
takuya@:~$ echo writethrough | sudo tee /sys/block/bcache0/bcache/cache_mode writethrough takuya@:~$ echo 0 | sudo tee /sys/block/bcache0/bcache/sequential_cutoff 0 takuya@:~$ echo 0 | sudo tee /sys/fs/ bcache/ btrfs/ cgroup/ ext4/ fuse/ pstore/
takuya@:~$ echo 0 | sudo tee /sys/fs/bcache/aad94dbd-5ded-4d23-bccb-1f3c1f475c48/congested_read_threshold_us 0 takuya@:~$ echo 0 | sudo tee /sys/fs/bcache/aad94dbd-5ded-4d23-bccb-1f3c1f475c48/congested_write_threshold_us 0
この条件だと、測定結果は次のとおりになった。5%くらいはやい?あんまり変わらないね。
takuya@:~$ sudo fio myjob.fio rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4 fio-2.1.11 Starting 1 process Jobs: 1 (f=0): [w(1)] [8.1% done] [0KB/1810KB/0KB /s] [0/452/0 iops] [eta 11m:35s] rand-write: (groupid=0, jobs=1): err= 0: pid=4873: Mon Mar 6 19:12:02 2017 write: io=84528KB, bw=1408.7KB/s, iops=352, runt= 60007msec slat (usec): min=5, max=16313, avg=86.45, stdev=167.83 clat (usec): min=77, max=726096, avg=11262.20, stdev=31084.99 lat (usec): min=85, max=730582, avg=11349.47, stdev=31109.02 clat percentiles (usec): | 1.00th=[ 163], 5.00th=[ 209], 10.00th=[ 4576], 20.00th=[ 5792], | 30.00th=[ 6816], 40.00th=[ 7136], 50.00th=[ 7392], 60.00th=[ 7648], | 70.00th=[ 7968], 80.00th=[ 8768], 90.00th=[10944], 95.00th=[18816], | 99.00th=[129536], 99.50th=[140288], 99.90th=[544768], 99.95th=[634880], | 99.99th=[724992] bw (KB /s): min= 71, max= 3040, per=100.00%, avg=1479.37, stdev=684.76 lat (usec) : 100=0.06%, 250=6.80%, 500=0.60%, 750=0.18%, 1000=0.13% lat (msec) : 2=0.11%, 4=1.35%, 10=79.16%, 20=7.32%, 50=2.14% lat (msec) : 100=0.01%, 250=1.91%, 500=0.11%, 750=0.13% cpu : usr=0.73%, sys=3.81%, ctx=17268, majf=0, minf=8 IO depths : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=0/w=21132/d=0, short=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=4 Run status group 0 (all jobs): WRITE: io=84528KB, aggrb=1408KB/s, minb=1408KB/s, maxb=1408KB/s, mint=60007msec, maxt=60007msec Disk stats (read/write): bcache0: ios=0/21070, merge=0/0, ticks=0/242964, in_queue=0, util=0.00%, aggrios=30/21496, aggrmerge=0/0, aggrticks=0/178762, aggrin_queue=178812, aggrutil=99.98% dm-4: ios=0/21224, merge=0/0, ticks=0/355520, in_queue=355620, util=99.98%, aggrios=0/21184, aggrmerge=0/51, aggrticks=0/296676, aggrin_queue=296624, aggrutil=99.97% sda: ios=0/21184, merge=0/51, ticks=0/296676, in_queue=296624, util=99.97% dm-5: ios=60/21768, merge=0/0, ticks=0/2004, in_queue=2004, util=2.88%, aggrios=60/22260, aggrmerge=0/71, aggrticks=0/1788, aggrin_queue=1752, aggrutil=2.54% sdb: ios=60/22260, merge=0/71, ticks=0/1788, in_queue=1752, util=2.54%
stop に 1 を書き込んだらデバイスを停止できる。
takuya@:~$ echo 1 | sudo tee /sys/block/bcache0/bcache/stop 1
もしまた使いたいときは、wipefs でフラッシュする
takuya@:~$ sudo wipefs -a /dev/mapper/acid-cache
主な操作は /sys/fs
経由で行う。
http://www.slideshare.net/nobuto_m/bcachessd-hdd
http://unix.stackexchange.com/questions/225017/how-to-remove-bcache0-volume
http://www.tech-g.com/2015/05/10/bcache-linux-ssd-caching-for-hard-drives-on-debian-jessie/
https://pommi.nethuis.nl/ssd-caching-using-linux-and-bcache/
https://jp.linux.com/news/linuxcom-exclusive/413722-lco20140225
lzop って圧縮伸張がそこそこ速くて便利だけど、それでも数十GBやると結構時間がかかるんですよね。
progress コマンドも候補に入るらしいので調べたら、lzop は非対応だった。
cp, mv, dd, tar, cat, rsync, grep, fgrep, egrep, cut, sort, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, adb, gzip, gunzip, bzip2, bunzip2, xz, unxz, lzma, unlzma, zcat, bzcat, lzcat
lzop ないよ。。うわーん
mv / cp なら、かわりにrsync --prgoress
使うとか。
dst/src
だから $(( $(stat -c %s dst ) / $(stat -c %s dst ) ))
を見ればいいわけですよね。でも、圧縮伸張の場合は、ファイルサイズが変わるのでどうしたものかと。
pv はパイプを通過したファイルサイズをカウントする方法。
そうか、パイプを通せばいいのか。
パイプからデータを読み込んで処理するプログラムなら、入力ストリームを通過したサイズと元ファイルのサイズを比較すればある程度のデータ処理進捗は見えるわけですよね。なるほど
brew install pv
$ pv ubuntu.usb.img.lzo | lzop --verbose -d -o out.img - decompressing <stdin> into 4.94GiB 0:00:59 [9.86MiB/s] [=======================================> ] 69% ETA 0:00:26
おお、行けるぞコレ。
これで、圧縮伸張などの進捗を表示することが出来ますね。ffmpeg なんかもイケルと思う。ファイルベースのパイプのアクセスって本当に便利ですね。
巨大な mysql のSQLを読み込んでるときとか、ファイルとパイプベースで処理されるコマンドならほぼ全てコレで解決する。
apt-get install pv
簡単です
Linuxで『progress』コマンドを使って動いてるcpやmv、tarの進捗状況を調べる | 俺的備忘録 〜なんかいろいろ〜
進捗どうですか? - 時間のかかる処理の進捗を確認 - Qiita
https://www.ivarch.com/programs/pv.shtml
若干追記
ページ数の多いPDFがなかなか表示されなくて、イライラしたことがありませんか?
とくに、企業がサイトに設置している取説PDFなどが遅い。全部のダウンロードが終わらないと見えないのはめんどくさい。またスライドなどのPDF資料も全部ダウンロードしてからプレビューが始まったりする。
プレビューで開くのも遅い。AdobeReaderで開くのも遅い。遅くて辛い。
調べたら、頭から順に読み込んで表示できないPDFの仕様があるので、これを頭から順に読み込んで表示するように出来ることがわかった。
qpdf --linearize input.pdf output.pdf
これをしておくことで、「ページ指定」した場合にその該当ページがすぐ表示される。表示したときに「先頭ページ」がすぐ表示されるようになる。スキャンしたPDFやスライドなどはこの処理をしておくと非常に快適になったのでメモメモ。
Acrobatなどでも設定ができるらしく、電子書籍のPDFなどは初めからこの設定が済まされていることも多かった。
取扱説明書のリニアライズは企業側に強く求めたい。とくに○○など。
サブシェルの大きな特徴は、サブシェルであるということ
サブシェルは、ほぼfork みたいなものだと思ったらいいと思います。
()
/ { }
の違い()
はサブシェルを起動し実行する。{}
は現在のシェルで実行する。現在のシェルの中でソースコードを実行するのか、フォークしてフォーク先でソースコードを実行するのかが一番大きな違い。この特徴があるので環境変数の保護や、作業ディレクトリの保護など変数にまつわることを隠蔽できるのが特徴になる。
サブシェルとしての特徴を見ていく実験をしておく。
コマンド
( sleep 20; )
実行中のプロセス
takuya@Desktop$ pstree 62776 -+= 62776 takuya -bash \-+= 63989 takuya -bash \--- 63990 takuya sleep 20
コレでわかるとおりです。
bash から bash が枝が生えて、枝bash からsleep が生えています。
グループで起動した、この場合のプロセスツリーはどうでしょうか
コマンド
{ sleep 20 ; }
実行プロセス
-+= 64457 takuya -bash \--= 64717 takuya sleep 20
グループでは、sleepを直接起動しています。これは
> sleep 20 ;
としているときと全く同じです。
サブシェルを使う場合、変数は受け継がれる。ただし変更は、戻されない。
これは起動時に変数を引継ぐが、起動後は別のプロセスになるので、変数への更新は反映されない。
takuya@Desktop$ a=1 ; ( echo $a; a=1234; echo $a; ) ; echo $a ; 1 # 元シェル $a 1234 # サブシェル内 $a 1 # 元シェル $a
リダイレクトをマトメられる特徴についても見ておく。
()
サブシェルと { }
グループの 特徴として リダイレクトをマトメられる。
サブシェルはサブシェルとして一つのグループになっているので、リダイレクトはマトメられる
echo 1234 > out.txt echo 5678 > out.txt echo 9012 > out.txt
などとするときに面倒だし、() / {}
で、マトメて一括リダイレクト指定して、楽に書くことが出来る。
( )
でのリダイレクト( echo 1234 echo 5678 echo 9012 ) > out.txt
{}
でのリダイレクトまたはグループを使うと。
{ echo 1234 echo 5678 echo 9012 } > out.txt
リダイレクトをマトメられるのでちょっと便利に使えるよね。
グループではまって面食らって「良くわからん」と使うのを断念しちゃう原因にシンタックスエラーがある。
{ }
の場合:シンタックスエラーtakuya@Desktop$ { echo 1234; } 1234 takuya@Desktop$ {echo 1234; } -bash: 予期しないトークン `}' 周辺に構文エラーがあります
開始の{
の直後に文字をつ続けてしまうと、制御構造として解釈されない
ところが、サブシェルの場合はシンタックスエラーにならない
(
は & | ;
なとと同等の優先度であるためだと思う。
( )
の場合:シンタックスOKtakuya@Desktop$ (echo 1234; ) 1234
この辺も知らないと戸惑う所。
理由は良くわからんが、どっちも関数定義に使える。
{}
で関数の定義takuya@Desktop$ func_a () ( echo 1 ; ); func_a ; 1
( )
で関数の定義!?takuya@Desktop$ func_a () { echo 1 ; }; func_a ; 1
気持ち悪さがある。ただ サブシェルが変数をサブシェル内にとどめてくれるので、とても楽にローカル変数を扱う、関数を作ることが出来る
つまり、作業ディレクトリ(wd) を覚えておく、元いた場所に戻すなど、ああいう面倒な作業が一切いらなくなる。
{ }
での場合の変数の状況takuya@Desktop$ pwd ; func_a () { cd ~; pwd ; }; func_a; pwd ; /Users/takuya/Desktop /Users/takuya /Users/takuya
( )
での場合の変数の状況takuya@Desktop$ pwd ; func_a () ( cd ~; pwd ; ); func_a; pwd ; /Users/takuya/Desktop /Users/takuya /Users/takuya/Desktop
サブシェルを使う点において同じになるので、少し知ってると便利かもしれない。
{ }
と ( )
の違いはサブシェルを経由するかどうかサブシェルを経由して、プロセスを生やすことを除き、殆どすべて同じであると思う。
サブシェルの特徴として次のとおりです。
サブシェルとグループに共通する特徴
個人的には () は fork だと思うことにしてる。
サブシェルを関数に出来たこと。
myFunc () ( echo aaaaa ) ; myFunc;
bashの使い方のまとめ記事のインデックス - それマグで!
[改訂新版] シェルスクリプト基本リファレンス ??#!/bin/shで、ここまでできる (WEB+DB PRESS plus)