2015年12月31日 星期四
龜毛的 HP server
以為是風路被阻擋,重排硬碟的位置,甚至把光碟機也拔掉,但情形還是未見改善。也到 BIOS 中,改變溫度相關的選項,情況還是沒改善。
後來,回憶在整理硬碟時,把硬碟一顆一顆插上去時,插到某一顆,風扇就開始提昇。的確,在設 Disk Array 時,一直有兩顆硬碟顯示警告訊息,說非認證的硬碟,那兩顆就是 Toshiba 的產品,有 5 年保固。後來,把那兩顆換掉,又恢復安靜無聲的狀態。
HP 的 SAS 硬碟,真的是超級貴的啊,已經買它的 server 了,還要用這一賤招來卡使用者。將來,要評估一下,還有那家的伺服器可以買,不然真的會被它卡的死死的,任其宰割啊。
2015年11月8日 星期日
複製 (Clone) Linux 系統 (包括 Gentoo, CentOS)
完整複製 (clone) 整個檔案系統
下面是使用 cpio 完整複製檔案系統的指引
HOWTO: Using cpio to Copy a Partition in Linux
Using cpio to copy everything from one partition to another in Linux, maintaining all file permissions, symlinks, and timestamps.
# File: copy_partition.notes # Auth: burly # Date: 03/20/05 # Desc: A method of copying everything from one # partition to another # Change directories to the top level of the # partition you want to copy cd /part # Use find to locate all objects and cpio to copy them find . -mount -print0 | cpio -0dump /path/to/new/part
How to move your filesystem to a new hard drive
ref: http://www.greenfly.org/tips/filesystem_migration.html
Despite the best planning, it eventually happens. You start to run out of space on your linux system. While there are many different ways to copy files from one hard drive to another, some work better than others when transferring the full / directory. Below I will outline a method I have used about 50 times to transfer full systems and partitions from one machine to another.
Why this can be complicated
When you are copying a full linux system from one partition to another, there are a few issues you need to consider:
- Preserving permissions - If your files aren't owned by the same people, and with the same permissions, your system won't run as expected to say the least.
- Properly handling special files - Certain methods of copying a system don't properly handle the /dev and /proc filesystems, as a result, you will boot on the new drive only to find you have no device entries listed.
- Spanning filesystems - When you are copying one filesystem to another, especially the root filesystem, you don't want to span across filesystems. That is, say you have the new hard drive mounted at /mnt/temp, if you recursively copy / to /mnt/temp and allow spanning filesystems, you could end up in a situation where /mnt/temp is copied into /mnt/temp/mnt/temp and /mnt/temp/mnt/temp is copied into /mnt/temp/mnt/temp/mnt/temp (not to mention the rest of the filesystem you have copied into /mnt/temp). To avoid this, most copying programs have some sort of option to only copy the mounted filesystem it is started from, and not to continue onto other filesystems.
What to do
With all these in mind, the best method I've found for copying the / filesystem is as follows (assuming for the example the new drive is mounted at /mnt/temp):
cd /; find ./ -xdev -print0 | cpio -pa0V /mnt/temp
注意,一定要到 source 的目錄下,才能成功執行。
到 source 的目錄下,再執行備份指令
find ./ -xdev -print0 | cpio -pa0V /mnt/temp
產生的 cpio 指令為
cpio: /mnt/temp/./usr/share/icons/Adwaita/24x24/actions/mail-mark-unread.png
假如使用下面的指令
find / -xdev -print0 | cpio -pa0V /mnt/hdd
執行 cpio 指令就會失敗,錯誤訊息如下
cpio: /mnt/hdd not created: newer or same age version exists
If you have other filesystems mounted on other partitions, you do something somewhat similar. Say I had /boot mounted on a different filesystem, and I wanted to back it up onto /mnt/temp:
cd boot; find ./ -xdev -print0 | cpio -pa0V /mnt/temp
You simply would mount each new partition you want to copy onto, to /mnt/temp and repeat this above operation for each of those. Upon execution, you will see a series of dots fly across the screen. Each dot represents a file that is being copied. I often will go to another terminal and run watch df to watch the progress.
After all the filesystems have been copied, you probably won't actually have a bootloader. What I usually do, is go ahead and move the new drive into the place of the old one, and boot up on a Knoppix disc. Then I mount the new drive, say at /mnt/hda1, and then run grub-install –root-directory=/mnt/hda1 /dev/hda to copy my new bootloader to the new hard drive. You could probably do something similar just from your old install.
Backup and Restore
You can also use this method for a backup and restore. For example, say you want to back up your current / on machine1 onto a different Linux machine, machine2, temporarily at /home/foo/drivebackup/ (to resize your current partition or perhaps to format to a new filesytem). First take the drive from machine2 to machine1, mount machine2's drive at /mnt/temp, and run:
find / -xdev -print0 | cpio -pa0V /mnt/temp/home/foo/drivebackup
Once you do whatever it is you need to do on machine1, take the hard drive to machine2 (as repartitioning or formatting would have blanked it obviously) mount that hard drive on /mnt/temp and then run:
cd /home/foo/drivebackup; find ./ -xdev -print0 | cpio -pa0V /mnt/temp/
This will copy all the data back onto your old drive.
使用 dd
Cloning a partition \ From physical disk /dev/sda, partition 1, to physical disk /dev/sdb, partition 1:
# dd if=/dev/sda1 of=/dev/sdb1 bs=64K conv=noerror,sync status=progress
使用 tar
參考 https://help.ubuntu.com/community/BackupYourSystem/TAR
cd / tar -cvpzf /mnt/hdd/root_backup.tar.gz --one-file-system \ --exclude=/proc \ --exclude=/tmp \ --exclude=/mnt \ --exclude=/dev \ --exclude=/sys \ --exclude=/run \ --exclude=/media \ --exclude=/var/log \ --exclude=/var/cache \ --exclude=/var/run /
To understand what is going on, we will dissect each part of the command.
- tar - is the command that creates the archive. It is modified by each letter immediately following, each is explained bellow.
- c - create a new backup archive.
- v - verbose mode, tar will print what it's doing to the screen.
- p - preserves the permissions of the files put in the archive for restoration later.
- z - compress the backup file with 'gzip' to make it smaller.
- f <filename> - specifies where to store the backup, backup.tar.gz is the filename used in this example. It will be stored in the current working directory, the one you set when you used the cd command.
- –exclude=/example/path - The options following this model instruct tar what directories NOT to backup. We don't want to backup everything since some directories aren't very useful to include. The first exclusion rule directs tar not to back itself up, this is important to avoid errors during the operation.
- –one-file-system - Do not include files on a different filesystem. If you want other filesystems, such as a /home partition, or external media mounted in /media backed up, you either need to back them up separately, or omit this flag. If you do omit this flag, you will need to add several more –exclude= arguments to avoid filesystems you do not want. These would be /proc, /sys, /mnt, /media, /run and /dev directories in root. /proc and /sys are virtual filesystems that provide windows into variables of the running kernel, so you do not want to try and backup or restore them. /dev is a tmpfs whose contents are created and deleted dynamically by udev, so you also do not want to backup or restore it. Likewise, /run is a tmpfs that holds variables about the running system that do not need backed up.
- It is important to note that these exclusions are recursive. This means that all folders located within the one excluded will be ignored as well. In the example, excluding the /media folder excludes all mounted drives and media there.
- If there are certain partitions you wish to backup located in /media, simply remove the exclusion and write a new one excluding the partitions you don't want backed up stored within it. For example:
–exclude=/media/unwanted_partition
- / - After all options is the directory to backup. Since we want to backup everything on the system we use / for the root directory. Like exclusions, this recursively includes every folder below root not listed in the exclusions or other options.
Linux file system 選擇
複製的目標檔案系統不需和原來的一樣,可以挑想要的檔案系統
關於 file system 的效能,可以參考 http://www.phoronix.com/scan.php?page=article&item=linux-40-hdd&num=1
- XFS: 有不穩定的傾向,不要試了
- ReiserFS: 一直留在 Gentoo 安裝指引的名單上,讓我一直想試。唉,試過之後,發現不支援 fstrim,在 SSD 上,會有問題。只好重新 format 成 ext4,最保險了。
- Ext4 is the recommended all-purpose all-platform filesystem. 選它,大概就是一個四平八穩的方案。
目前硬碟的使用情形如下
/,根目錄,目前用了大概 40GB,就設 80GB 吧/boot,使用 30MB,設 120 MB 吧。
swap,先前沒設? 好像也沒問題,就不需要了嗎
mkfs.ext4 /dev/sdb1
mkfs.ext4 /dev/sdb2
要 copy 整個 partition,有幾種作法,包括 dd,rsync,cpio。在下面的網址找到 cpio 的作法,就用它吧。
http://greenfly.net/tips/filesystem_migration.html
因為在 root 目錄下,有一些是目錄是 mount 其他 partition,所以,就再重新 mount 在另外的目錄下,再進行 copy。
mount /dev/sda2 /mnt/org
mount /dev/sdb2 /mnt/temp
cd /mnt/org
find ./ -xdev -print0 | cpio -pa0V /mnt/temp
umount /mnt/org
umount /mnt/temp
mount /dev/sda1 /boot
mount /dev/sdb1 /mnt/temp
cd /boot
find ./ -xdev -print0 | cpio -pa0V /mnt/temp
umount /mnt/temp
執行 GRUB,我還是用舊的 0.97 版,其缺點是不支援 gpt,也就是硬碟超過 2TB 會有問題。但目前還可用,就將就著吧。
硬碟代碼,sda = hd0,sdb = hd1,sdc = hd2。
進入 grub 後,執行安裝指令。
grub> root (hd1,0)
grub> setup (hd1)
grub> quit
接下來,就週整硬碟順序,就 OK 了。
假若是進 chroot 來 cpio 系統,然後要用 chroot 進新的磁碟,/mnt/gentoo/proc 可以直接 umount,但使用 mount --rbind 來掛載的 sys 和 dev,要用下面的方式 umount。
# grep /mnt/gentoo/sys /proc/mounts | cut -f2 -d" " | sort -r | xargs umount -n
複製 CentOS 系統
以下針對 CentOS 7.9,而且沒有使用 lvm。參考 [Basic System Recovery]。
分割區的規劃如下。
# df -h
檔案系統 容量 已用 可用 已用% 掛載點
/dev/sdb3 74G 5.7G 64G 9% /
/dev/sdb1 926M 226M 637M 27% /boot
/dev/sdb5 375G 5.5G 351G 2% /home
# mkdir /mnt/sysimage
# mount /dev/sdb3 /mnt/sysimage
# mount /dev/sdb1 /mnt/sysimage/boot
# chroot /mnt/sysimage
# grub2-install --target=i386-pc /dev/sdb
Installing for i386-pc platform.
grub2-install:錯誤: cannot find a device for /boot/grub2 (is /dev mounted?).
參考 Gentoo 的安裝指引。
# mount --types proc /proc /mnt/sysimage/proc
# mount --rbind /sys /mnt/sysimage/sys
# mount --make-rslave /mnt/sysimage/sys
# mount --rbind /dev /mnt/sysimage/dev
# mount --make-rslave /mnt/sysimage/dev
#
# chroot /mnt/sysimage /bin/bash
# source /etc/profile
# export PS1="(chroot) ${PS1}"
再次執行安裝 grub 指令,就不會出錯了。
# grub2-install --target=i386-pc /dev/sdb
Installing for i386-pc platform.
Installation finished. No error reported.
接下來要設定開機選單。
# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-1160.25.1.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.25.1.el7.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-1160.21.1.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.21.1.el7.x86_64.img
Found linux image: /boot/vmlinuz-3.10.0-1160.6.1.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.6.1.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-ed726d3da8c142da88e77d06804e9ef0
Found initrd image: /boot/initramfs-0-rescue-ed726d3da8c142da88e77d06804e9ef0.img
done
列出產生的開機選單。
# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /boot/grub2/grub.cfg
0 : CentOS Linux (3.10.0-1160.25.1.el7.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-1160.21.1.el7.x86_64) 7 (Core)
2 : CentOS Linux (3.10.0-1160.6.1.el7.x86_64) 7 (Core)
3 : CentOS Linux (0-rescue-ed726d3da8c142da88e77d06804e9ef0) 7 (Core)
檢查前次開機項次。
# grub2-editenv list
saved_entry=CentOS Linux (3.10.0-1160.25.1.el7.x86_64) 7 (Core)
最後,看新硬碟是否可以成功開機,若不成功,再重複檢查上述步驟。
2015年11月7日 星期六
調整 mp4 檔案的音量 (normalize)
參考連結
How can I normalize audio using ffmpeg? 其中 [Manually normalizing audio with ffmpeg]
Normalize audio in an avi file
Manually normalizing audio with ffmpeg
volume
filter to change the volume of a track. Make sure you download a recent version of the program.Find out the gain to apply
ffmpeg -i video.avi -af "volumedetect" -vn -sn -dn -f null /dev/null
/dev/null
with NUL
on Windows.The
-vn
, -sn
, and -dn
arguments instruct ffmpeg to ignore non-audio streams during this analysis. This drastically speeds up the analysis.[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] mean_volume: -16.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] max_volume: -5.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] histogram_0db: 87861
Apply the volume filter:
volume
filter to an audio file. Note that applying the filter means we will have to re-encode the audio stream. What codec you want for audio depends on the original format, of course. Here are some examples:- Plain audio file: Just encode the file with whatever encoder you need:
ffmpeg -i input.wav -af "volume=5dB" output.mp3
Your options are very broad, of course. - AVI format: Usually there's MP3 audio with video that comes in an AVI container:
ffmpeg -i video.avi -af "volume=5dB" -c:v copy -c:a libmp3lame -q:a 2 output.avi
Here we chose quality level 2. Values range from 0–9 and lower means better. Check the MP3 VBR guide for more info on setting the quality. You can also set a fixed bitrate with-b:a 192k
, for example. - MP4 format: With an MP4 container, you will typically find AAC audio. We can use ffmpeg's build-in AAC encoder.
ffmpeg -i video.mp4 -af "volume=5dB" -c:v copy -c:a aac -b:a 192k output.mp4
Here you can also use other AAC encoders. Some of them support VBR, too. See this answer and the AAC encoding guide for some tips.
-c:v copy
. If there are subtitles in your input file, or multiple video streams, use the option -map 0
before the output filename.步驟摘要整理
找出聲音的大小,峰值為 -7.5 dBffmpeg -i v1.mp4 -af "volumedetect" -f null /dev/null
[Parsed_volumedetect_0 @ 0x1589580] n_samples: 397301760
[Parsed_volumedetect_0 @ 0x1589580] mean_volume: -40.3 dB
[Parsed_volumedetect_0 @ 0x1589580] max_volume: -7.5 dB
將聲音提升 7.5 dB
ffmpeg -i v1.mp4 -af "volume=7.5dB" -c:v copy -c:a aac -strict experimental -b:a 128k v1n.mp4
處理後,得到的值
[Parsed_volumedetect_0 @ 0x2159720] n_samples: 397303808
[Parsed_volumedetect_0 @ 0x2159720] mean_volume: -32.9 dB
[Parsed_volumedetect_0 @ 0x2159720] max_volume: -0.2 dB
分離 mp3,另用 audacity 處理
ffmpeg -i ch1.mp4 -q:a 0 -map a ch1.mp3
https://www.youtube.com/watch?v=NgedD2zKhdc
Audacity Tutorial: Amplification, Compression, Normalizing and Exporting Your Podcast
使用 compressor 將大小聲做調整,不致動態太大。
http://manual.audacityteam.org/o/man/compressor.html
2015年11月5日 星期四
PHP -- 程式員的萬用瑞士刀
在Windows的環境下,相對於大部分的語言,都要安裝後才能用,PHP 則是下載,解壓縮後,即可使用。熟悉它的設定後,隨時隨地都以使用。
尤其,在Windows的環境下,要寫出功能強大的 batch 程式,幾乎是不可能任務。PHP 的 command mode 可用來執行 batch 程式。因此,PHP 真的可以說是程式人員的萬用工具。
2015年10月26日 星期一
Laravel 5.0 安裝與目錄調整
composer create-project laravel/laravel laravel-5.0-empty "~5.0.0" --prefer-dist
rm vendor/compiled.php
composer update
php artisan fresh (
remove this scaffolding)安裝後,共 19.3 MB,其中 vendor 下,有4,526 個檔案,共 18.4 MB。
記得設定 config/app.php 中的時區,
// 'timezone' => 'UTC',
'timezone' => 'Asia/Taipei',
在使用 Laravel 5.0 開發網頁時,每次要備份,vendor 目錄下,檔案很多,要備份時,花很多時間來掃描。因此希望將 vendor 由應用程式分開,可以減少備份時間。另外,storage目錄下的東西,也是無需備分的。
更進一步,再調整一下目錄,也有可能多個 App 共用 vendor。
此外,index.php 原先是放在 public 的目錄下,目錄不好設定。因而,個人習慣是,將 index.php 改為放在 App 的根目錄下,然後將程式放在子目錄下,例如 app-src 下。
調整後,希望目錄長成這樣子
[myapp]
|
+-- [app-src]
| |
| +- [app], [bootstrap], ...
|
+-- [L5-vendor] /* this can be moved out the project */
|
+-- [storage]
|
+-- index.php, favicon.ico, ...
在此,使用 IIS 6 的 web server,網頁的根目錄為 "\Inetpub\wwwroot"
要修改的檔案內容如下
1. 修改 la-tst/myapp/index.php
define('BASEDIR', __DIR__.'/app-src');
define('VENDORDIR', __DIR__.'/L5-vendor');
require BASEDIR.'/bootstrap/autoload.php';
$app = require_once BASEDIR.'/bootstrap/app.php';
$app->useStoragePath(__DIR__.'/storage');
若要將 vender 目錄移到專案外,讓多個專案共用。必須修改 config/session.php,設定不同的cookie 名稱,不然會出現 "TokenMismatchException in VerifyCsrfToken.php" 的錯誤。
2. 修改 app-src/bootstrap/autoload.php
require VENDORDIR.'/autoload.php';
3. 修改 L5-vendor/composer 下的 4 個 autoload 檔,包括 autoload_classmap.php、autoload_files.php、autoload_namespaces.php、autoload_psr4.php
$vendorDir = VENDORDIR;
$baseDir = BASEDIR;
調整好後,有可能要重新啟動 web server,才能正常運作。
瀏覽的 url:
http://10.161.86.192/la-tst/myapp/index.php
在此稍加說明,我是用 composer 安裝一個空的 Laravel 5.0 專案。啟動一個新的 AP 時,建立一個新的 App 目錄,直接 copy 整個目錄,然後,進行開發。
因此,在正規的作法,composer 目錄下的檔案是以 composer 來產生。但在此,直接手動改內容,是有點不太正規,但我是打定主意,不 update 系統。因為,目前已是 Laravel 5.1 是主流,5.0 已不太會更新。
另一調整目錄方式
在使用一段時間後,還是有必要 update。就是把根目錄下的全部東西,都移到 app-把 public 目錄下的東西,提到根目錄下調整後,希望目錄長成這樣子
[myapp]
|
+-- [app-src]
| |
| +- [app], [bootstrap], [vendor]...
|
+-- [storage]
|
+-- index.php, favicon.ico, ...
這樣,只要修改 index.php 的內容,能夠 include 到檔案即可,然後再改一下 storage 的目錄。這樣的目錄安排,不用修改伺服器設定,也可以 update package,缺點是,有些 artisan 的指令不能用,只能自行取捨了。
2015年10月21日 星期三
Synology NAS RS810+ 升級 8TB 硬碟
Synology NAS RS810+,購入時間為 2011年11月,當時最大的 3.5 吋 SATA 硬碟為 2TB,而且,為了省錢,只用 WD 綠標的硬碟。在 2014 年時,4TB 硬碟的價格已降至可接受的範圍,升級為 WD 紅標 4TB 硬碟。到 2015年8月,Seagate 出的 8T 硬碟,價格已降至和去年 4TB 的價格差不多了。
由於在群暉的規格中,並未明確說明 8TB 的硬碟是否可用。因此先買一顆來試用,結果可以正常抓到,且可正常運作。確定之後,再買齊其他的 3 顆,目前還在同位元資料一致性檢查中,不曉得還要跑多久。
安裝時,有一點不順之處,不曉得是不是 Seagate 為了塞下更多的 disk,少了兩個螺絲孔,因此,只好只鎖兩顆螺絲。還好,裝在機殼內,不會隨便亂動。
由於,為了儘可能有最大的可用空間,只設成一顆容錯。因此,拆下硬碟後,要儘快裝回去。硬碟裝回去,且等系統抓到硬碟後,要選擇「儲存空間1 」,再點選「管理」,然後選擇執行修復。
修復的過程包括
- 重建,這一步驟蠻快的,恢復有一顆硬碟的容錯保護
- 同位元資料一致性 (consistency check),很久,而且有資料異動,又會從 0.00%從頭開始。
- 新增硬碟 ,好像也要很久,超過24小時。
既然,無法有更多的硬碟空間,乾脆打掉重建,變成有2顆容錯的組態,可用容量約為 14TB左右,這樣比較安心。折騰了半天,可用空間沒有增加多少,換來的只是多一點安心而已。
驚! 才剛把設定改成有2顆容錯的組態,磁碟陣列重建完,開始把資料 copy 回去,就發生抓不到硬碟的錯誤。將硬碟拔出來,重新插入,可以抓到。但是,在進行修復不到一個小時,硬碟又消失了。才用不到兩個月的硬碟,就發生這種情形,真是讓人驚出一身冷汗。還好,有多買一顆,換上去看看,能撐多久,這次就當白老鼠吧。
2016-08-05
又再來試一次, 這次買的是 WD 的紅標 NAS 碟, 一顆1萬1左右。螺絲孔位與Seagate 的8TB硬碟一樣,只能鎖2顆螺絲。
換完以後,仍然顯示只有 14.31TB。在資料備份之後,重新設定為2顆硬碟容錯,直至目前,1個月左右,還好,沒有硬碟消失的情形。還在持續觀察中。
2015年10月9日 星期五
Laravel 3.2 與 5.0 的粗略比較
一些小小的比較,列表如下
Laravel 3.2 | Laravel 5.0 | |
---|---|---|
source files | 439 files | 4595 files |
source-size | 3MB | 15MB |
cache scripts | 151 scripts | 587 scripts |
cache used-memory | 7.6 MB | 13.29 MB |
Requests per second | 290.33 [#/sec] | 122.94 [#/sec] |
其中,針對安裝後,空的 framework,source files 為其所包含檔案數,source-size 為其使用的硬碟空間。然後在執行時,使用 Zend OPcache 加速,得到其統計資料,scripts 為 cache 住的 script 數目,userd_memory為其使用的記憶體大小。
速度比較,使用 Apache 的 ab 程式,簡單的測試其處理效能,程式只是傳回 5 byte 的 "Hello" 字串,Requests per second 為每秒回應的 request 數。不過,在效能上,通常程式並非是決定性的影響,反而是資料庫的存取決定了應用程式的效能,因此,要提升程式的效能,應對資料庫的調校下一些功夫。
從以上的粗略比較,得知,不論檔案數和使用的空間,Laravel 5.0 都大約是 Laravel 3.2.14 的 2 倍以上;而相對的,在效能上,前者大約為後者的一半以下。隨著硬體效能的進步,寫程式越來越不需要在意效能的問題,包括大小與速度。在意的是,開發的速度和所花的精力,還有日後在維護程式上的方便。這也是使用較新的 framework 的主要考量。
2015年9月28日 星期一
MVC 使用心得
剛開始時,會想要遵循 Model/View/Controller 的拆解原則來安排程式。可是在移轉一些舊有的 ASP 或 PHP 程式之後,發現最基本的架構,只用到 Controller + View,然後把一些共用的程式碼抽出來,放到 Lib 下。而資料庫存取,大都用 DB query builder 來完成。因此,那個 Model 就變得很奇怪的角色,不曉得該如何歸類,或該放些什麼進去。
到了開始使用 Laravel 5.0 時,發現它把 Model 的目錄給拿掉了。有明確目錄位置的是 Controller 和 View,其餘的自己看著辦。看來大勢所趨,網頁程式,大抵是就是,Route + Contoller 扮演 dispatcher 的功能,View 輸出結果。其餘的,就是 Bussiness Logic 了,大概就是建一堆 Class,擺在適當的目錄下。
2015年9月12日 星期六
通識教育,是營養學分?
其實,在社會上工作之後,會發現,一個人所學範圍越廣,知識越廣,瞭解的層面越寬廣,發展可能性就會越好。
另一方面,依據大部人的經驗,很多人在畢業之後就業,和他在校所學,百分之七八十都沒關係。
所以,通識課程,在廣面來講,對於個人來講是有幫助的。
引述 臺灣大學,江才健老師的 <科學在文化中的定位和挑戰>
連結 -- http://get.aca.ntu.edu.tw/getcdb/handle/getcdb/253687
2015年9月7日 星期一
在 Grails 下,使用 Apache FileUpload
在 Grails 下,自行建立的 Servelet 中,使用 Apache FileUpload 處理上傳檔案,程式碼大致如下
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List /* FileItem */ items = upload.parseRequest(request);
不論怎麼試,所傳回的 items 一直都是空的。
好不容易,才找到問題所在,因為 Grails 會在預先處理 request 的資料。因此要修正 config.groovy,將 disable 處理 multi part requests 的設定設為 true
// whether to disable processing of multi part requests
// grails.web.disable.multipart=false
grails.web.disable.multipart=true
原因,依據 Apache's FAQ page for FileUpload 中的說明,"A Request can only be parsed once."
Question: Why is parseRequest() returning no items?
Answer: "This most commonly happens when the request has already been parsed, or processed in some other way. Since the input stream has aleady been consumed by that earlier process, it is no longer available for parsing by Commons FileUpload."
2015年8月26日 星期三
使用 PHP 內建的 HTTP server
https://docs.phalconphp.com/en/latest/reference/built-in.html
https://gist.github.com/tamagokun/3801087
http://www.sitepoint.com/taking-advantage-of-phps-built-in-server/
在 PHP 5.4.0 之後,就內建 HTTP server,可以很方便的測試網頁。甚至代替網芳,用來傳檔案也很方便。
只要下這樣的指令,就可以了。
php -S localhost:8000 -t /web_root
只是不支援 Apache 的 .htaccess 和 mod_rewrite,對於省略 index.php 的網址,必須用其他方式達成。
2015年8月4日 星期二
在 Wine 下安裝 Sublime Text 3
雖然,有 Linux 版的 Sublime Text 3,中文輸入也已設定成功,但是,text drag and drop 的功能在 Linux 下是無效的,我的決擇是捨中文輸入,選擇文字拖曳的功能。因此,又在想著如何透過 Wine 來執行 Sublime Text 3。
照著先前的作法,裝了 vcrun2010,仍然是意外死亡。最後,抱著試一試的想法,使用 winetricks,將上面可以裝的東西,都裝裝看,看是否運氣好,能夠讓 Sublime Text 3 跑起來。運氣還不錯,終於成功執行。總計裝了 dotnet20, dotnet20sp1, dotnet30sp1, glut,vcrun2010 倒沒有裝。不曉得是那個有用。將目錄整個砍掉,重來一次就知道了。
== Related Information ==
OS: Gentoo Linux, kernel: 3.18.12Wine: 1.7.47, Winetricks: 20150706
Sublime Text 3 - build 3083
Required package: dotnet20sp1 or dotnet20sp2
Native DLL: dwrite.dll
一樣,為個別的軟體建個專用目錄,建立虛擬目錄。
$ WINEARCH=win32 WINEPREFIX=~/wine-sublime3 winecfg
選擇Windows 版本: Windows 7
設定虛擬目錄的環境變數
$ export WINEPREFIX=~/wine-sublime3
安裝 Sublime Text
可以執行$ wine explorer
點選安裝檔,進行安裝。或是把安裝檔 copy 至 ~\wine-sublime3\drive_c\ 下,然後執行
$ wine c:\\Sublime\ Text\ Build\ 3083\ Setup.exe
或
$ wine Z:\\home\\ajax\\Downloads\\Sublime\\Sublime\ Text\ Build\ 3126\ Setup.exe
執行看看,
$ wine "C:\\Program Files\\Sublime Text 3\\subl.exe"
嗯,出現嚴重錯誤!
先裝 .NET 2.0 Framework SP1 或 SP2
$ winetricks dotnet20sp1
再試一次,可以成功執行,但畫面是亂的,這已有經驗了。透過 winecfg 的函式庫功能,將 dwrite.dll 設成原生 (native) 即可。dwrite.dll 是一個系統的 DLL 檔,該檔的描述為 Microsoft DirectX Typography Services。
大功告成,可以舒服的用了。
另外,package control 也不能用,那就用手動安裝吧,反正也做慣黑手了。
InputHelper
Linux 版本的 Sublime Text 會有中文輸入的問題,在 Wine 下執行,也同樣無法直接輸入中文,只好暫時用 copy and paste 的方式。很幸運的,經過一些嘗試,可以用 InputHelpler 輸入中文了。依網路上找到的資料,下載 InputHelper,然後複製到 ~/wine-sublime3/drive_c/users/ajax/Application Data/Sublime Text 3/Packages/ 的目錄下,再修改下列的部分2. 修改 inputhelper.py 的下面兩個地方
(1) if sublime.platform() == 'windows':
(2) args = ["cmd", "/c", location]
3. 修改 linux_text_input_gui.py 的第一行為 #!/usr/bin/env python2,因為 pygtk 目前只有 Python 2 的版本,而我的 OS 預設是 Python 3。
注意,依照上述方式執行,Sublime 是在 WINE 的環境中執行,但 InputHelper 則是在 Linux 的環境中執行。
衍生問題,ctrl_space 在 sublime 中,已有定義。編輯時不小心按到,會切換中英文輸入模式,但無法輸入中文,更進一步,有一些快速鍵也會失效。因此,在執行 Sublime 之前,必須 unset XMODIFIERS 環境變數,使中文輸入在 Sublime 中不會有作用。
#!/bin/bash
unset XMODIFIERS
export WINEPREFIX="/home/ajax/wine-sublime3"
wine "C:\\Program Files\\Sublime Text 3\\subl.exe"
設定 sidebar 的字型大小
透過 [Browse Packages] 功能,找出其下的 [User] 目錄,建立 Default.sublime-theme,加上下面的內容[
{
"class": "sidebar_label",
"color": [0, 0, 0],
"font.bold": false,
"font.size": 12
}
]
假如是使用別的 theme,則依其名稱而不同,例如
Material-Theme-Darker.sublime-theme
補記
目前,我的筆電還是裝 Wine 的穩定版是 Wine 1.6.2,照著上面的步驟再裝一次,但安裝 Sublime Text 3後,無法成功執行。記得在先前的試裝過程中,看到 mono 的訊息,Wine 1.6.2 中,裝的是 wine-mono-0.0.8.msi,而 Wine 1.7.47 裝的是 wine-mono-4.5.6.msi,差別可能在此吧。升級至 Wine 1.7.47 ,就可以成功執行,舊版的 Wine 也就不再多試了。2017-06-01 記
升級到 app-emulation/wine-any-2.7,仍是 mask,會自動裝 app-emulation/wine-desktop-common、app-emulation/wine-gecko、app-emulation/wine-mono-4.7.0,幾乎不用再做任何設定,直接裝 sublime-text 即可使用。2017-09-15 記
在前一版推出的一年之後,2017年9月13日,Build 3143 發行。馬上安裝試用,結果字型變得很難看,也無法使用先前的 Color Scheme,只好再回去用 Build 3126。為了一直避免跳出 update 的提示視窗,在 setting 裡加上下面的設定"update_check": false,
2018-10-09 記
不知是那裡改變了,變成 InputHelper 可以輸入中文,但無法貼到編輯的文件中。然後,設定 XMODIFIERS=@im=gcin 就可以輸入中文了。WINE 的功能表
用 WINE 執行安裝之後,會在功能表上出現選項,例如 Wine > Programs > Sublime Text 3 對應的檔案在~/.local/share/applications/wine/Programs/Sublime Text 3.desktop
2015年8月3日 星期一
VM 中使用智慧卡讀卡機
在 Linux host 中,安裝 pcsc-lite 及 ccid driver。再啟動 pcscd 的 daemon。
Vmare 的 config 中,要加上 usb.ccid.useSharedMode = "TRUE" 的設定
參考
https://pubs.vmware.com/workstation-9/index.jsp?topic=%2Fcom.vmware.ws.using.doc%2FGUID-8AA77FED-BE63-45CB-8FB6-6D7C4A35F76B.html
假如不要使用分享的方式使用智慧卡讀卡機
則修改 VM 的設定檔,例如 Win_XP.vmx ,加入
usb.generic.allowCCID = "TRUE"
usb.ccid.disable = "TRUE"
VMware Workstation Pro 16.0
usb.ccid.useSharedMode = "FALSE"
2015年8月1日 星期六
在 Linux 版的 Sublime Text 3 中,使用 gcin 輸入中文
參考連結如下。
http://www.sublimetext.com/forum/viewtopic.php?f=3&t=7006&start=10#p41343
http://www.sublimetext.com/forum/viewtopic.php?f=3&t=7006&start=20#p61143
(以上為 Sublime Text forum)
http://whitequark.org/blog/2014/04/14/xcompose-support-in-sublime-text/
PS: 我使用的是 Gentoo Linux
原始碼
sublime-imfix.c
Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
By Cjacker Huang <jianzhong.huang at i-soft.com.cn>
By whitequark@whitequark.org
How to compile:
gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
How to use:
LD_PRELOAD=./libsublime-imfix.so sublime_text
Changes:
2014 06-09
1, Fix cursor position update for sublime text 3.
2, Combine the codes from whitequark(fix for xim immodule) and add cursor update support for XIM immodule.
*/
/*for RTLD_NEXT*/
#define _GNU_SOURCE
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef VERBOSE
#define DEBUG(fmt, ...) do { \
FILE* err = fopen("/tmp/libsublime-immethod-fix.log", "a"); \
if (err) { \
fprintf(err, fmt, __VA_ARGS__); \
fclose(err); \
} \
} while(0)
#else
#define DEBUG(fmt, ...)
#endif
typedef GdkSegment GdkRegionBox;
struct _GdkRegion
{
long size;
long numRects;
GdkRegionBox *rects;
GdkRegionBox extents;
};
GtkIMContext *local_context;
//this func is interposed to support cursor position update.
void gdk_region_get_clipbox (const GdkRegion *region,
GdkRectangle *rectangle)
{
g_return_if_fail (region != NULL);
g_return_if_fail (rectangle != NULL);
rectangle->x = region->extents.x1;
rectangle->y = region->extents.y1;
rectangle->width = region->extents.x2 - region->extents.x1;
rectangle->height = region->extents.y2 - region->extents.y1;
GdkRectangle rect;
rect.x = rectangle->x;
rect.y = rectangle->y;
rect.width = 0;
rect.height = rectangle->height;
//The caret width is 2 in sublime text 2
//And is 1 in sublime text 3.
//Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
if((rectangle->width == 2 || rectangle->width == 1) && GTK_IS_IM_CONTEXT(local_context)) {
gtk_im_context_set_cursor_location(local_context, rectangle);
}
}
//this is needed, for example, if you input something in file dialog and return back the edit area
//context will lost, so here we set it again.
static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context)
{
XEvent *xev = (XEvent *)xevent;
if(xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
GdkWindow * win = g_object_get_data(G_OBJECT(im_context),"window");
if(GDK_IS_WINDOW(win))
gtk_im_context_set_client_window(im_context, win);
}
return GDK_FILTER_CONTINUE;
}
void gtk_im_context_set_client_window (GtkIMContext *context,
GdkWindow *window)
{
GtkIMContextClass *klass;
g_return_if_fail (GTK_IS_IM_CONTEXT (context));
klass = GTK_IM_CONTEXT_GET_CLASS (context);
if (klass->set_client_window)
klass->set_client_window (context, window);
//below is our interposed codes to save the context to local_context.
if(!GDK_IS_WINDOW (window))
return;
g_object_set_data(G_OBJECT(context),"window",window);
int width = gdk_window_get_width(window);
int height = gdk_window_get_height(window);
if(width != 0 && height !=0) {
gtk_im_context_focus_in(context);
local_context = context;
}
//only add this event_filter when using 'fcitx' immodule.
//for xim immodule, this function is as same as original from gtk2.
const gchar * immodule = g_getenv("GTK_IM_MODULE");
if(immodule && !strcmp(immodule, "fcitx")) {
gdk_window_add_filter (window, event_filter, context);
}
}
/*below codes is from whitequark, fix for xim immodule */
/* See gtkimcontextxim.c */
GType gtk_type_im_context_xim = 0;
#define GTK_TYPE_IM_CONTEXT_XIM (gtk_type_im_context_xim)
#define GTK_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIM))
#define GTK_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
#define GTK_IS_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_IM_CONTEXT_XIM))
#define GTK_IS_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_XIM))
#define GTK_IM_CONTEXT_XIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
typedef struct _GtkIMContextXIM GtkIMContextXIM;
typedef struct _GtkIMContextXIMClass GtkIMContextXIMClass;
struct _GtkIMContextXIMClass
{
GtkIMContextClass parent_class;
};
typedef struct _StatusWindow StatusWindow;
typedef struct _GtkXIMInfo GtkXIMInfo;
struct _GtkIMContextXIM
{
GtkIMContext object;
GtkXIMInfo *im_info;
gchar *locale;
gchar *mb_charset;
GdkWindow *client_window;
GtkWidget *client_widget;
/* The status window for this input context; we claim the
* status window when we are focused and have created an XIC
*/
StatusWindow *status_window;
gint preedit_size;
gint preedit_length;
gunichar *preedit_chars;
XIMFeedback *feedbacks;
gint preedit_cursor;
XIMCallback preedit_start_callback;
XIMCallback preedit_done_callback;
XIMCallback preedit_draw_callback;
XIMCallback preedit_caret_callback;
XIMCallback status_start_callback;
XIMCallback status_done_callback;
XIMCallback status_draw_callback;
XIMCallback string_conversion_callback;
XIC ic;
guint filter_key_release : 1;
guint use_preedit : 1;
guint finalizing : 1;
guint in_toplevel : 1;
guint has_focus : 1;
};
static GClassInitFunc orig_gtk_im_context_xim_class_init;
static GType (*orig_g_type_module_register_type)(
GTypeModule *, GType, const gchar *, const GTypeInfo *,
GTypeFlags);
static gboolean (*orig_gtk_im_context_xim_filter_keypress)(
GtkIMContext *context, GdkEventKey *event);
static gboolean
hook_gtk_im_context_xim_filter_keypress(GtkIMContext *context, GdkEventKey *event)
{
GtkIMContextXIM *im_context_xim = GTK_IM_CONTEXT_XIM(context);
if (!im_context_xim->client_window) {
DEBUG("im_context_xim == %p\n", im_context_xim);
DEBUG("event->window == %p\n", event->window);
gtk_im_context_set_client_window(context, event->window);
}
return orig_gtk_im_context_xim_filter_keypress(context, event);
}
static void hook_gtk_im_context_xim_class_init (
GtkIMContextXIMClass *class)
{
orig_gtk_im_context_xim_class_init(class, NULL); /* wat? */
GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
assert(!orig_gtk_im_context_xim_filter_keypress);
orig_gtk_im_context_xim_filter_keypress = im_context_class->filter_keypress;
im_context_class->filter_keypress = hook_gtk_im_context_xim_filter_keypress;
DEBUG("orig_gtk_im_context_xim_filter_keypress: %p\n",
orig_gtk_im_context_xim_filter_keypress);
}
GType
g_type_module_register_type (GTypeModule *module,
GType parent_type,
const gchar *type_name,
const GTypeInfo *type_info,
GTypeFlags flags)
{
if (!orig_g_type_module_register_type) {
orig_g_type_module_register_type = dlsym(RTLD_NEXT, "g_type_module_register_type");
assert(orig_g_type_module_register_type);
}
if (type_name && !strcmp(type_name, "GtkIMContextXIM")) {
assert(!orig_gtk_im_context_xim_class_init);
orig_gtk_im_context_xim_class_init = type_info->class_init;
assert(sizeof(GtkIMContextXIM) == type_info->instance_size);
const GTypeInfo hook_im_context_xim_info =
{
type_info->class_size,
type_info->base_init,
type_info->base_finalize,
(GClassInitFunc) hook_gtk_im_context_xim_class_init,
type_info->class_finalize,
type_info->class_data,
type_info->instance_size,
type_info->n_preallocs,
type_info->instance_init,
};
DEBUG("orig_gtk_im_context_xim_class_init: %p\n", orig_gtk_im_context_xim_class_init);
gtk_type_im_context_xim =
orig_g_type_module_register_type(module, parent_type,
type_name, &hook_im_context_xim_info, flags);
return gtk_type_im_context_xim;
}
return orig_g_type_module_register_type(module, parent_type, type_name, type_info, flags);
}
編譯:
$ gcc -shared -o libsublime-imfix.so sublime-imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
設定環境變數:
$ export GTK_IM_MODULE=xim
執行:
$ LD_PRELOAD=./libsublime-imfix.so subl3
如上,Sublime Text 3 中文切換,必需將 GTK_IM_MODULE 設成 xim
但是會讓所有的程式,gcin 的輸入視窗都黏在視窗左上角,不會跟著程式的游標 。
只好要用 Sublime Text 時,才設成 xim。終究能夠直接輸入中文,比起以往必需用 copy and paste 來輸入中文,要好得多了。
另外,有下面一些小小的不便。
- 在輸入中文時,若想用 Backspace 來修正時,無法刪除輸入的字根,反而會刪除編輯的內容。必須按 Escape 鍵,取消所有的輸入,重新輸一次。
- 無法用 ctrl+, 或 ctrl+. 來輸入中文的豆號和句號。
Exec=bash -c 'GTK_IM_MODULE=xim LD_PRELOAD=/usr/local/lib/libsublime-imfix.so subl3' %F
關於中文輸入法的環境變數的設定,可以參考
http://moto.debian.tw/viewtopic.php?t=9525
2015年7月26日 星期日
Laravel, 自動載入自訂 Namespace
但若是自訂的 Namespace 或是檔案,是在與 app 同一層的目錄下,則必須自行設定,才會自動載入。
修改 composer.json 檔案,在 "autoload" 的下面,加上自訂的項目。下面的範例中,新增了要自動載入的項目 "myFunction.php" 及 "Nspace" 的 namespace。
"autoload": {
"classmap": [
"database"
],
"files": [
"myfolder/myFunction.php"
],
"psr-4": {
"App\\": "app/",
"Nspace\\": "Nspace/"
}
},
然後,執行 composer dump-autoload,其將在 vendor/composer 的目錄下,產生對應的 autoload 的檔案,例如 autoload_psr4.php 及 autoload_files.php 等。
其於 autoload_psr4.php 中,新增一行
'Nspace\\' => array($baseDir . '/Nspace'),
瞭解後,亦可自行手動加入吧。
後記 (2016-10-28)
雖然,經測試後,這樣做可以自動載入自訂的 Namespace,不過,我後來都偷懶,直接放在 APP的目錄下,比較省事。2015年6月20日 星期六
多讀書,持續成長
生活在都巿中的人,包括我身邊的同事,吃一餐飯,花個 3,5 百元,覺得是合理的。但是,一整年,可能不曾買過一本書。5百元的書,一般會認為太貴了。可是,看看歐美的書籍的訂價,折算成台幣,都超過千元。當然,中國的書,卻比台灣便宜許多。
那一本書的價值,到底多少,才會覺得划算呢?
從交易的角度來看,我認為能讓自己在交易過程中,賺回買那一本書的錢,在價值上,就已超過它的價格。所以,有時候買學習交易或投資的書,只是覺得其中某一段講得有道理。
我在剛開始學習交易 (或投資) 時,幾乎可以找到的書都看,一心想參透其中的奧秘。其中,有些東西,在當時,並不能體會,甚至覺得他言過其實。但在跌跌撞撞下,經過 7,8 年,有時在挫折或困境中,突然想起以前念過的某句話,或某種說法,才會明白,原來是在說這個。
大部分人都會有個疑問,那些書會說出真正有用的東西嗎? 我想或許可以自己問自己,我自己吃飯的本事,會完全告訴別人嗎? 噢,當然不會。
既然沒有人會把真正的秘訣寫出來,那為何要讀書呢? 我個人的看法是,就像瞎子摸象一般,多讀書,再加上練習,慢慢的能摸索出一條路來。
反過來說,常常我們在當下一心追尋的東西,並非真的是重要的,反而人家說出來真正有道理的東西,我們會覺得那太過平凡,甚至是笑話。就像,某本書上,作者提到曾問某人,交易最重要的是什麼,對方答 "成本",可是,大部分人並不同意。我自己在練習幾年後,發現每天不論賺賠,只有券商是穩定有收入的,自己簡直就是幫券商在打工。
2015年6月18日 星期四
獲利之道 -- 等待好機會,再大力出擊
前輩的經驗,獲利,不在勝率多少,而在獲利的多少。做對一次,可以彌補 3 次的失敗。
要確定,等待什麼樣的機會 -- 這必需多看歷史線圖,然後,天天練習,訓練感覺。
無法等待,是因為 -- 怕錯失機會,怕機會一去不回頭。
要克服此因素,只能採用試單的方式。
等待反轉再出手,相對上比較安全。
要從 pattern 上,才能判斷是要反轉或延續。
不管做對或做錯,出場都是很艱難的一部分。不論如何,假若進場後,並未如預期的發展,應該減少部位,減輕心理壓力,並且減少停損的損失。
2015年6月11日 星期四
我是天才
這是一個要開始賠錢的徵兆,而且會連續賠錢,直到承認自己一無是處為止。
學習交易的過程,就在自認是天才與笨蛋之間擺盪,直到認清自己為止。
我們天天都在走路,沒有人會認為走個 2,3百公尺,有什麼了不起,也不會覺得爬一層樓,會覺得很厲害。
可是,對一個剛學走路的小孩,這卻很不容易,值得高興一番。
交易,到最後,就是一種工作罷了,和在巿場賣炸雞排,沒什麼兩樣,只是比較不容而已。
到最後,輸贏只是工作的一部分。每天,照著策略,等待機會,嘗試,放棄;或嘗試,追擊,出場。
同時,也知道自己,隨著價格上上下下,心也上下不已,在貪婪與恐懼之間擺盪。
2015年6月10日 星期三
部位大小,取決於風險承受度
所持有的部位,在確定進場理由消失,或確認反轉前,振盪必須在能夠忍受的範圍內。
換句話說,就是停損出場,所賠旳錢,不會讓自己心疼。
只有這樣,才能等待趨勢的發展,而不會因為虧損超出自己所能忍受,而將部位提早平掉。