冰冷暴雨夜的山中湖Solo Camp

流水账

计划了一个星期的Solo Camp。终于还是盼到了周末。

这次的目的地是山中湖边的みさきキャンプ場。在ゆるキャン第六话中「野クル」除了撫子之外的部员camp过的地方。

带着半分圣地巡礼的心情,周六起个大早,因为是solo,所以不用因为集合之类的拖延时间,前夜已经塞好所有装备。

装备

路线大概是这样的,

从我家=>東名川崎インター=>(東名高速)=>御殿場=>(国道138)=>(東富士五湖道路)=>山中湖=>(マリモ通り)=>みさきキャンプ場

7:30 从家出发。

东名的堵车也是厉害。大和トンネル先端开始10公里。直接给去程增加了一个多小时。

堵车

去目的地途中得先去当地的超市和home center买东西。

第一站是コメリハード&グリーン 忍野店

这家店是个home center,和ゆるキャン没有一毛钱关系。之所以先得来这里是因为出发前夜发现买的印有しまりん的旗子需要特定的旗杆才行。

搜遍了整个谷歌发现这家店幸好还有8个库存。

憋着在高速公路上的7成尿意跑到店里,拿起旗杆结账。

顺便吐槽一下,这家店里的厕所水龙头不出水。。。

第二站是漫画里也出现的超市オギノ山中湖店。

超市里BBQ的东西也是很充实,就是价格略贵。比如一把烧火的柴居然要800日元。从良心上来讲,这钱在别的地方买的柴可以烧到天亮。

オギノ旁边的百元店东西很少,包括对面的药妆店,真的只是药妆店。一点BBQ相关的东西都没,本来想买便宜的着火剂和柴火,结果只好又跑回オギノ买了。

在オギノ买了一盘450g的烤肉拼盘,两个北极贝,四个蛤,两瓶水,两袋泡面,一包关东煮,一袋冰,一带零食,一捆柴,两块着火剂。

收银的妹子不错。不过下意识看到了无名指上的戒指233

买完东西沿着阴天的湖岸开了半圈就到了目的地みさきキャンプ場。

前前后后大概已经有十几个帐篷。单人帐篷+停车券 1700日元,游鱼券600日元。也许是觉得特么湖里根本没有鱼,作为最后的良心,游鱼券上印着附近温泉的100日元优惠券。

望了一圈,比较开阔的场地感觉已经没有靠近河边的位置了。把行李放上拖车,拖着走到林子里,找了个河边,开始驻扎。

完成的样子如下。

驻扎效果

插旗子的杆子就是刚才在home center买的。

以前觉得旗子直接有个pole一插就行了。后来看了一下,日本这种旗子,还得专门的旗杆。其特征是有个固定的横杆。这样旗子没有风的时候也是展开的。

这种样式其实也并不陌生,大街小巷的店门口,比如パチンコ之类的店,门外就是这种旗子。

午饭是关东煮。

关东煮

其实泡面也行,随便凑活一下。风略大,burner有点力不从心了。感觉得买个挡板。

吃完饭抄起鱼竿钓鱼哎。

湖钓ルアー釣り还是第一次。和海钓的区别是,鱼竿比较轻,毕竟要经常抛甩。鱼线直接挂上ルアー就行了。

换了几个地方,除了扎到几根水草就没有任何收获。

另外还有个看起来很专业的大叔在钓鱼。腰里别着两根鱼竿,直接穿着捕鱼那种可以下水的衣服走到湖里。

大概到了三点半。天突然开始下雨了。

天气预报真是垃圾。早上看的说今天也就是阴到多云,结果直接就是倾盆大雨,远处还有轰鸣的雷声。

大叔也上岸了。跑到我帐篷门口和我打个招呼。

大叔说能不能拍一下しまりん的旗子。我说好呀,大叔拍完,欲言又止,最后还是和我说。

小伙子你甩鱼竿动作太大了,这样侧着轻轻一甩就行了。另外不需要把线扯来扯去,比起乱暴的小鱼,鲈鱼还是比较喜欢攻击安静游动的小鱼。云云。。。

我问大叔有没有钓到,大叔说,这年头,其实大家都知道这湖里根本没鱼。自己也是抱着被剃光头的心态来的。。。

大叔走了之后,雨越下越大,タープ上的水滚下来就像瀑布一样。

天也开始冷起来。我决定去温泉暖一下身子。

紅富士の湯

也是漫画里出现的温泉。车程大概15分钟,这里吐槽一下BMW的Navi也是垃圾。目的地是设施的话不能直接自动定位成停车场,害得我在狭窄的上坡路上惊悚地调了次头。

虽然是雨天,停车场也是水泄不通。山中湖毕竟世界驰名,游客的身影也是层出不穷。

浴室的窗外就是富士山,不过下雨,烟雾笼罩啥都看不到。男汤里面有两个池子,外面走下台阶也有两个。

我本来也不是特别喜欢泡着,暖和一下身子就出来了。

也没休息个几分钟,赶紧在天黑之间回到了camp场。下雨天湖边的路乌漆麻黑,加上对向车道闪眼的车灯,只好稳重再稳重。

在タープ下面坐下,接下来就是要做最后幸福感的事情了。

在雨中烤肉和生火。

天色已经暗了下来。在生火的时候,有个小哥拿着零食跑过来说分给我的,雨太大了,自己的装备撑不住先回去了。看到しまりん的旗子感觉好棒哈哈。一路平安~

大鹅

有个大鹅游过来。叫了几声又游走了。到了晚上睡觉时候我才反应过来应该是我把它平时的地盘给占了。

烤肉1

老顾介绍的合成木炭质量真心好。所有烤完只用了6快碳。而且真是一点烟有没。

Weber 炭

软广告

烤肉2

北极贝放着烤的样子很棒,味道嘛,其实我也不是特别感冒。下次多买盒肉算了。

大概到8点左右雨也停了,开始生火。月亮也出来了。

生火

顺便用公司手机的流量补了两话棒子剧,扫了几眼纪录片。

尝了一口小哥的爆米花,特么已经湿了,吃了口薯片,一股柴火味。算了,扔了哎。

800块钱的柴火烧到11点多就没了。这次比较后悔的是没带大的烧烤炉,

因为是solo,所以用了uniflame的 ユニセラtg-iiiミニ这款。烤肉正好,不过要烧柴就有点力不从心了。

横着竖着总之还是会掉外面。

倒是消火罐看起来挺大,放两根柴进去也能烧得很壮观。

十一点半,睡觉。

躺下才感觉特么三季节用帐篷已经不抵寒意了。

室外温度大概3度,室内估计撑死5度。毕竟flyer和底子之间透气的空隙太大,冷风直接从下面钻进来的感觉。

最佳温度5度的睡袋,上半身还好,下半身就别说了,反正晚上是冻到脚抽筋了。

这次最后悔的就是没把电热毯带过来。

就这么挨过一夜。被尿憋醒。上完厕所之后索性就开始收拾东西了。

早饭是,泡面+咖啡。

收拾完东西稍微散步走去看富士山。

富士山

天真特么好。昨天的鬼天气像一个梦一样。

回来路上又去了一次温泉。

中饭在温泉的食堂里顺便解决了。

山梨县名物,ほうとう

ほうとう

不知道这个东西有没有中文译名,没有的话我来取一个。山梨刀削面。

也许是出发早的缘故,东名高速也没堵车,很顺畅得就滚回了家。

收拾完坐下,才下午三点半。

反省

  • 三季节用的帐篷已经不是时候了。该考虑入手冬天专用的保暖增强性帐篷

  • 用普通的锅子烧水简直要命,烧个半小时也不开,该买 Jet boiler了

  • 得买个solo用的火架或者暖炉

  • 钓鱼是个败笔,下次先调查一下有没有鱼再买券

Camp装备入门1

从家里走出来的阿宅

随着近期动画化的作品「ゆるキャン」(中文渣译成 摇曳露营) 的火爆,出现了原本一些已经根治无望的阿宅从家里出门,投入到户外活动的奇迹。

杨教授当年的电击疗法于此相比也只能是相形见绌。

当大量的阿宅将本来投入到塑料小人,光碟小卡片上的钱投入到露营装备中的时候,一时间整个岛国的Camp场地都有种水泄不通,一地难求的感觉。
趁着Camp的火热,我也来安利一波Camp大法。

奢侈的户外运动

camp是项很烧钱的户外运动。

买齐一套Camp的装备大概成本不下20万日元。

装备齐了就算了吗? 除非你是考虑走路去camp,或者坐电车去camp,亦或者是在自己家阳台上camp,否则有个车也基本是必须的。

这么一来直接就把camp的门槛提高了很多。

当然,上面这些复杂的开销有个前提,那就是你自己想组织camp,或者是自己想一个人去solo camp。

最低限度的装备

作为第一次尝试camp,跟着有装备的大佬就行啦。那么这个时候,作为最低限度的装备,都需要哪些呢?

帐篷

当然跟着大佬出去也可以几个人挤一个大帐篷。不过很多情况,比如男女之间不方便之类的原因,还是买一顶自己的帐篷比较好。

一般来说,第一次买帐篷的人,经常容易犯的错误就是买个大帐篷。在一些地方有限的camp场,大帐篷几乎没有施展能力的空间。更何况大帐篷意味着更大的体积和重量,出门携带也是很不方便的。

这里我推荐几款1-2个人用的帐篷。虽然说1-2人,但是实际上一个人用最舒适。

这个帐篷的set已经包含了垫在帐篷与地面之间起保护作用的Ground Sheet以及帐篷内部的垫子。另外送两面亮骚的小旗。Pole(杆子)一根就可以支撑起来。简单却不失趣味。价格也适中。

轻量以及低价是这个帐篷的亮点。当然我不建议买更便宜的一些帐篷。便宜货一般接缝的地方很粗糙,下雨天漏水是常有的事情。montbell作为日本大厂,质量还是值得信赖的。

睡袋(シュラフ)

睡袋的形状各种个样。有方方正正的信封型的。有木乃伊型的。

选择睡袋的主要标准还是两个,一个是使用温度,一个是打包后的体积。

春夏用的睡袋可以比较随意。

基本ドンキー卖1000块钱的睡袋可以适合5-10月上旬的绝大地方的camp。上面这款和ドンキー里面卖的睡袋的区别是,有名maker的睡袋,质量相对较好。

秋冬用的睡袋需要特别注意使用温度。另外有时候温度标记会有两种,一个叫最低使用温度,一个是舒适使用温度。

基本上我们得根据舒适使用温度来选择睡袋。

虽然说明上写了3 season用。但是,coleman是北美的maker。所谓的冬季那是北美的寒冬,和日本的冬季不是一个次元的。这款使用温度为-5度的睡袋足够适合秋末冬初的camp。

座椅

无论是像Fuji Rock 那种野外Live,还是Camp,有把轻便又舒适的椅子肯定是必不可少的。

大多数折叠椅子的价格和质量基本大相径庭,可以产生价格差距的关键因素是椅子的重量。

这把椅子中和了舒适度,价格,以及重量,性价比已经相当高。

这把椅子有点小贵。之所以贵的因素是,这把椅子的总重量不过500g。野外fes之类的活动太适合了。在Fuji Rock的时候实际使用的就是这把椅子。不过,好像涨价了呢。

睡垫&床

有了帐篷和睡袋,就可以直接钻进去睡觉了? 除非那是你家地板。

野外的地面比想象得差很多。特别是小石子,沙粒,高低不平,坑坑洼洼的地面,没有个垫子肯定是睡不安稳的。

一般睡袋和帐篷之间得铺一个垫子。或者,直接架在地面上的床。

垫子可以分充气的和不充气的。很显然,充气的携带方便,但是可能睡起来不自然,垫子的话虽然可以多重折叠,但是想要舒适平坦的话,还是得选择比较厚的垫子。

这款是折叠型的。看起来也比较厚实。

充气型的要比折叠型的贵一点。不过收纳起来很方便。夏天的话充气型的垫子也比较凉爽。

这是个可以架起来的小床。通过和地面隔离,可以起到夏天阴凉,冬天隔热保暖的作用。缺点嘛就是比较重。价格倒也还合适。

后话

入手了以上的装备,基本就可以愉快地参与Camp了。当然,上面的仅仅是解决了最基本的住的问题。所以离Solo Camp 出道还是很远。

至于其他一些装备以及进阶装备, 那就放到以后慢慢介绍吧。

git merge 当整个文件conflict的时候

背景

啊呀为什么我的pull request里面有整个文件全部conflict了?

仔细看个个commit,明明只是修改了文件中的一部分呀?

Git Merge 之后的差分

假设我们现在branch名为feature,目标branch为master。

为了试着修正conflict,我们试着[feature]$ git pull master

这个时候产生了整个文件的冲突。vim打开冲突文件。我们发现每一行末尾都有^M

但是为什么当我们vim单独打开feature branch下的这个文件的时候,却什么都看不到呢?

fileformat

众所周知不同的os的默认换行是不一样的。

当我们单独打开feature branch下的目标文件,发现其格式为utf-8[dos] 而位于master下面的目标文件,其格式为

utf-8[unix],这个差异造成了我们上述的整个文件的conflict。

解决方法很简单,用vim打开在feature branch下的目标文件。

1
:set fileformat=unix

即可。

后记

不同OS下的换行。

1
2
3
4
5
6
7
LF・・・UNIX
CR・・・MacOS(version 9之前,现在已经不常用)
CR+LF・・・Microsoft Windows

CRLF : ↵
CR  : ←
LF  : ↓
vim

Apache httpd 最近的一些问题录以及解决方法

http_response_code 的困惑

相同问题也在StackOverflow自问自答了。

在使用php-fpm 和 httpd 的时候我发现了个奇怪的现象。

如下面的例子。

1
2
3
4
<?php
$result = http_response_code(200);
var_dump($result);
phpinfo();

当设置response为200的时候,什么问题也没有,访问这个扔在web根目录的php文件,会成功在页面上输出dump的内容以及phpinfo的内容。但是,当我们把200改成400的时候,就出现了httpd自带的400页面。

这种现象给人产生一种错觉,那就是这个页面执行到http_response_code(400) 就中断了。因为同样尝试在非php-fpm的配置下执行相同的php脚本,是可以看到dump输入400的内容以及phpinfo的。

解决这个问题的关键是,你确定改脚本执行到http_response_code(400)就中断了吗?因为没有输出在页面上,所以很容易就产生了脚本中断的错觉。

验证这个错觉的方式很简单,把var_dump 改成向某个文件里面输入一些内容就行了。实验也证明的确文件中存在着我们输出的内容。

那么其实httpd做的事情就很明显了。

当我们返回非正常状态的时候(4xx,5xx) httpd匹配了类似下面的配置。

1
2
3
ErrorDocument 403 /error.html
ErrorDocument 404 /error.html
ErrorDocument 500 /error.html

那要如何取消这种默认的匹配而让我们的脚本来处理这种错误呢? 在config里面赫然发现了ProxyErrorOverride on 这个设定。把on设置称off。相当于告诉httpd 我们php-fpm的错误状态还是交给php-fpm来处理。

ProxyPass and ProxyPassReverse

在httpd中设置反向代理的时候我们常看到下面两行配置。

1
2
3
4
5
6
<VirtualHost myhost:80>
ServerName myhost
DocumentRoot /path/to/myapp/public
ProxyPass /app https://myapp:8080/
ProxyPassReverse /app https://myapp:8080/
</VirtualHost>

既然使用了ProxyPass ,那为啥要有ProxyPassReverse呢?

ProxyPassReverse是用来改写后端返回结果的header的。

当有如下302的跳转

1
https://myapp:8080/ => https://myapp:8080/new

我们服务器发送 /app 实际的导向会变成 /new, 而正确的导向应该是/app/new

所以ProxyPassReverse起到了查看后端返回的header,把里面的东西反过来加上/app

WKWebView中的Usercript插入

背景

ios端的浏览器没有插入Userscript机制。为了满足我们时常精虫上脑的需求,我们可以自己写一个基于WKWebView的应用来插入我们的Userscript。

方法

  1. 建立WKUserScript。
1
2
3
4
5
6
7
8
9
10
11
var jsSource = ""
let path = Bundle.main.path(forResource: "user_script", ofType: "js")!
if let data = NSData(contentsOfFile: path){
jsSource = String(NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)!)
}
let userScript1 = WKUserScript(source: jsSource, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: true)

let controller = WKUserContentController()
controller.addUserScript(userScript1)
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller

上面这段是我抄来的。我们需要将user_script.js放置在app project的根目录下。Swift这命名我特么也是要吐了。

  1. 创建WKWebView的实例
1
self.webView = WKWebView(frame: view.bounds, configuration: configuration)
  1. 调整WKWebView大小来迎合Device样式(这里就不废话了)

  2. load我们需要的插入Userscript的页面

1
2
3
let webUrl = URL(string: "https://某小电影网站")!
let myRequest = URLRequest(url: webUrl)
webView.load(myRequest)

快速生成icon

这个工具好评家鹅。

直接把png放到app的根目录然后app-icon generate就行了。

后话

飞速地写好了。然而并不打算公开。

获取当前Spotify播放歌曲的歌词

背景

标题是お盆休み之前给自己布置的作业。

很多时候上班带着耳机写代码,听到不错的歌曲很自然而然想去找歌词。Spotify这种垃圾东西,有歌词的歌曲又极其少,而且当你在termnial敲代码的时候自然不想离开termial。
这个时候,敲个命令就能显示当前播音乐的歌词多好。

解决方案设计

想要获取当前的歌曲的歌词,自然先得获取当前的歌曲名字(track)和专辑名(album)以及歌手名(artist)。

乍一想难上天啊,如果spotify不提供什么机制的话,这个就是强制进程通信啊。

还好,spotify有web console这种东西可以支持操作现在的播放设备。这里就不废话了,没有采用的理由是access token的默认寿命只有1小时。🤣

其实OS X提供了一个很牛逼的工具可以让我们达到获取歌曲名,歌手以及专辑名的方法。那就是osascript

1
2
3
4
5
6
➜  my_blog git:(master) ✗ osascript -e 'tell application "Spotify" to artist of current track as string'
Park Boram
➜ my_blog git:(master) ✗ osascript -e 'tell application "Spotify" to name of current track as string'
Will Be Fine
➜ my_blog git:(master) ✗ osascript -e 'tell application "Spotify" to album of current track as string'
Will Be Fine

wow,crazy. 接下来就是找个搜歌词的接口把这些塞进去就是了。

Genius 这个网站提供了搜歌词的接口,注册之后可以免费使用。从搜索的返回结果中找到歌词网页的path,最后用爬虫爬取页面获得歌词即可。

Demo

调用osacript和获取歌词path这些在shell脚本中完成即可。至于爬虫有些麻烦,我用nokogiri写了个ruby脚本,将歌词path作为参数传给ruby脚本处理最后输出歌词。

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/sh

artist=`osascript -e 'tell application "Spotify" to artist of current track as string'`;
track=`osascript -e 'tell application "Spotify" to name of current track as string'`;
album=`osascript -e 'tell application "Spotify" to album of current track as string'`;

lyrics_data=`curl -s "https://api.genius.com/search?q='"$track $artist $album"'" -H "Accept: application/json" -H "Content-Type: application/json" -H "Authorization: Bearer your_own_token" `

lyrics_path=`echo $lyrics_data | jq ".response.hits[0].result.path"`

song_title=`echo $lyrics_data | jq ".response.hits[0].result.full_title"`

ruby ./lyrics_parser.rb "$lyrics_path" "$song_title"

ruby 脚本就是个爬虫,最后输出歌词即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require "nokogiri"
require "open-uri"

url = "https://genius.com#{ARGV[0]}".gsub('"',"")

html = open(url) do |f|
charset = f.charset
f.read
end

doc = Nokogiri::HTML.parse(html, nil, nil)
doc.xpath('//div[@class="lyrics"]').each do |node|
puts ARGV[1] + "\n"
puts node.inner_text
end

运行效果如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
➜  cmd_lyrics ./lyx.sh
supercell
君の知らない物語
https://genius.com/Supercell-kimi-no-shiranai-monogatari-lyrics
"君の知らない物語 (Kimi no Shiranai Monogatari) by ​supercell"



Itsumodoori no aruhi no koto
Kimi wa totsuzen tachiagari itta
"konya hoshi o mi ni yukou"

"tamani wa ii koto iunda ne"
Nante minna shite itte waratta
Akarimonai michi o
Bakamitai ni hashaide aruita
Kakaekonda kodoku ya fuan ni
Oshitsubusarenaiyouni


Makkurana sekai kara miageta
Yozora wa hoshi ga furu youde


Itsu kara darou kimi no koto o
Oikakeru watashi ga ita
Douka onegai
Odorokanaide kiiteyo
Watashi no kono omoi o
....

渣渣渣。

后续

这个世界上本来就不存在一个很好的歌词库。

以上。

FujiRock 2018 计划

交通

7月26日18点,二子玉川出发。

環七=>上越道=>三国街道

下高速吃晚饭。月夜野IC。地点待定。

预计到达 23点。

携带物品

帐篷

折叠桌 x 1,折叠椅 x 2

睡袋 x 1

雨衣裤

中裤 x 2

T恤 x3

内衣内裤洗漱用品毛巾

矿泉水 500ml x 24

纸巾

湿巾

防晒霜

除虫驱蚊喷雾

移动电源

露营灯

铺垫

剪刀

水箱 10L

耳塞

眼罩

枕头

USB电风扇

TBD

温泉

猿ヶ京温泉温泉街

宿場の湯

备忘

TBD

Docker的CMD和ENTRYPOINT的区别

这个问题很基本。至于为什么到现在才想去弄清楚,大抵的理由就是平时不怎么用docker。用也是直接当应用程序一样一跑而已。

简而言之,ENTRYPOINT 指的是,docker container在启动的时候跑的命令。

任何docker container,如果不指定ENTRYPOINT,默认的是运行/bin/ch -c {cmd}

括号里的内容,可以是通过CMD定义的参数, 也可以是通过docker run -i -t image_name <cmd> 传递的参数。

于是,我们自然可以自定义ENTRYPOINT。

需要注意的是,无论是CMD还是ENTRYPOINT,都是只有最后定义的那个才是有效的。

以上。

你不懂MV

我们常常用mv source/a target/b 来重命名。 a,b 都是directory。
然后很多时候我们忽略了一个重点就是这个操作重命名成功的条件是b不存在或者为空。
当b不为空的时候,a 会跑到b的下面。target/b/a

如果这个时候想用a里面的内容替换b的内容,那就是老老实实rsync了。

rsync -av source/a target/b

咦为什么a还是跑到了b的下面?

其实正解是

rsync -av source/a/ target/b

写到这里,真是羞愧地低下了头。

Truffle使用web3.js调试智能合约的一些备忘

进入develop调试模式

1
2
$ truffle compile && truffle develop
$ deploy

deploy之后,我们可以在testrpc上调试智能合约。

获取地址balance

1
2
truffle(develop)> web3.eth.getBalance('0x627306090abab3a6e1400e9345bc60c78a8bef57').toNumber()
96722887800000000000

注意balance的单位是wei。转换成eth则是

1
2
truffle(develop)> web3.fromWei(96722887800000000000, 'ether')
'96.7228878'

payable的函数调用

1
Loto.deployed().then(function(instance){return instance.Buy(5, {value: web3.toWei(0.1, "ether")});}).then(function(value){return value});

纯Get函数调用

1
Loto.deployed().then(function(instance){return instance.getSlots.call();}).then(function(value){return value});