Jex’s Note

Deploy Rails

(最後更新: 2016-04-14)

[1] 安裝 Passenger + Nginx

安裝 passenger

sudo apt-get install libcurl4-openssl-dev       # passenger 需要
gem install passenger

安裝 nginx

rvmsudo passenger-install-nginx-module          # 用 passenger 安裝 nginx
  • 安裝在預設路徑 /opt/nginx, 千萬不要改, 免得 nginx 指令會無效
  • 如果有出現 export rvmsudo_secure_path=1 執行它
  • 記憶體太少可能會導致安裝失敗, 如果顯示要你增加 swap 可參考此篇

如果安裝 passenger-install-nginx-module 發生錯誤

/home/web-admin/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/specification.rb:2158:in `method_missing': undefined method `this' for #<Gem::Specification:0xdf627c passenger-5.0.27> (NoMethodError)
from /home/web-admin/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/specification.rb:1057:in `find_active_stub_by_path'
from /home/web-admin/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:64:in `require'
from /home/web-admin/.rvm/gems/ruby-2.3.0/gems/passenger-5.0.27/bin/passenger-install-nginx-module:33:in `<top (required)>'
from /home/web-admin/.rvm/gems/ruby-2.3.0/bin/passenger-install-nginx-module:23:in `load'
from /home/web-admin/.rvm/gems/ruby-2.3.0/bin/passenger-install-nginx-module:23:in `<main>'
from /home/web-admin/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `eval'
from /home/web-admin/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `<main>'

看起來可能是我預設用 ruby-2.3 發生問題,所以先降回 ruby-2.2

rvm install ruby-2.2.4
rvm use ruby-2.2 --default

再安裝一次就成功了!

[2] 安裝成功後設定根目錄

建立 /var/www 目錄

設定 /opt/nginx/conf/nginx.conf, 將 location / { 範圍內註解掉, 並在 http { 內加上 :

error_log /var/www/rails_app/log/nginx_error.log;
access_log /var/www/rails_app/log/nginx_access.log;
server {
    listen 80;
    server_name 106.185.47.26;  # or domain name
    root /var/www/rails_app/public;
    passenger_enabled on;
}
  • nginx 執行時的 user 可以不用特別指定, 預設是
  • 如果要跑 development 環境則加上 rails_env development;

[3] 安裝 nginx 指令

如果有開 tmux 記得關掉再執行 :

cd /tmp
wget -O init-deb.sh https://www.linode.com/docs/assets/660-init-deb.sh
sudo mv init-deb.sh /etc/init.d/nginx
sudo chmod +x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults

start

sudo /etc/init.d/nginx start
or
sudo service nginx start

[4] 把 code 放到 /var/www

bundle install

如果顯示 bundle 沒安裝,執行:gem install bundle

[5] 環境

development :

rake db:migrate RAILS_ENV=development

production :

1) 產生 Key

rake secret             # 產生key

2) 設定 secret, 有兩種方式, 擇一就好

第一種

config/secrets.yml
    production:
      secret_key_base: cf2d4472039660a31a002b21cd3ded0cf7cc2c5a0d82f24dcdf5097b79c1900241f97eb85542f8e4a349f32fac37b618bc663b21f16de2706bb897885d6cc3f0

第二種

1) nginx.conf :
    passenger_env_var SECRET_KEY_BASE "cf2d4472039660a31a002b21cd3ded0cf7cc2c5a0d82f24dcdf5097b79c1900241f97eb85542f8e4a349f32fac37b618bc663b21f16de2706bb897885d6cc3f0";

2) config/secrets.yml
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

3) DB migrate + Assets precompile

First time :

RAILS_ENV=production rake db:create
RAILS_ENV=production rake db:migrate

Update code :

RAILS_ENV=production bundle exec rake assets:precompile

4) Restart web server

sudo service nginx restart

網頁不通

出現錯誤 500 We're sorry, but something went wrong.

1) 判斷是 nginx 的 500 還是 Rails 的 500 (看頁面的 html 及 css 可以判斷)

2) 500 的話先看, 目錄權限有沒有問題, www.example.com/robots.txt 讀取 public/robots.txt 看通不通

注意, 如果放在 /root 下, 因為 /root 的權限還是 root 的, 即使網站 foler 改成 www-data 也沒用, 建議放在 /var/www 下, /var/www 權限記得要給 www-data

nginx 預設 user 是 nobody, 建議改成 www-data, 並且確定網站根目錄的權限也是 www-data

3) 當 nginx 及 rails log 都沒有異樣, 執行 RAILS_ENV=production rails c 看有沒有錯誤, 如果有錯誤會導致 nginx 的 500

4) 檢查在 development 環境是否正常 rails s -b 0.0.0.0

5) 檢查 production 是否正常 RAILS_ENV=production rails s -b 0.0.0.0

確定有做 assets precompile, 如果 public/assets 有檔案但 404

environments/production.rb, 改成 true :

config.assets.compile = true

確定網頁都沒有 404 等等之類的問題

6) 再回去看 log/production.log 有沒有異常

很有可能會發生內建的 http server : WEBrick 執行的權限是夠的, 但 nginx 執行權限不夠導致錯誤

I18n 導致錯誤

I, [2015-08-22T06:53:22.272463 #7927]  INFO -- : Completed 500 Internal Server Error in 490ms (ActiveRecord: 0.0ms)
F, [2015-08-22T06:53:22.273759 #7927] FATAL -- :
I18n::InvalidLocaleData (can not load translations from /usr/local/rvm/gems/ruby-2.2.1/gems/devise-i18n-views-0.3.4/lib/../locales/pt-PT.yml: #<Errno::EACCES: Permission denied @ rb_sysopen - /usr/local/rvm/gems/ruby-2.2.1/gems/devise-i18n-views-0.3.4/lib/../locales/pt-PT.yml>):
  app/controllers/application_controller.rb:18:in `set_locale'

調整權限

chmod -R 777 /usr/local/rvm/gems/ruby-2.2.1/gems

沒有 permission 問題後, 網頁就能 work 了

403 Forbidden

以下有幾個方向可以找出問題在哪裡 :

  • 重啟 nginx 看看有沒有出現什麼異常的訊息,有可能 passenger_root 路徑設定錯導致找不到檔
  • 確定網站根目錄的權限是 nginx 可以執行的
  • 去 nginx 的 error log 看看有沒有線索

完整的 nginx.conf

  • 區分 dev (3000 port) / production 環境 / ssl
  • passenger_root : 可用 passenger-config --root 得知
  • passenger_ruby : 可用 passenger-config --ruby-command 得知

/opt/nginx/conf/nginx.conf :

user web-admin;
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    passenger_root /usr/local/rvm/gems/ruby-2.2.1/gems/passenger-5.0.15;
    passenger_ruby /usr/local/rvm/gems/ruby-2.2.1/wrappers/ruby;

    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    server {
        listen 443 ssl;
        ssl_certificate /opt/nginx/ssl/example_combined.crt;
        ssl_certificate_key /opt/nginx/ssl/example.key;

        client_max_body_size       50M;

        listen       80;
        server_name  example.com;

        # non-www redirect to www
        if ($host = $server_name) {
            return 301 https://www.$server_name$request_uri;
        }

        # 將 http 導到 https
        if ($scheme = http) {
            return 301 https://www.$server_name$request_uri;
        }

        # 注意 owner 可能引發 500
        root /var/www/example/public;
        passenger_enabled on;
        rails_env production;

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

    server {
        listen 3000;
        server_name dev.example.com;

        client_max_body_size       50M;

        # 注意 owner 可能引發 500
        root /var/www/example/public;
        passenger_enabled on;
        rails_env development;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

其他

關閉 Development 環境的錯誤訊息

config/environments/development.rb

config.consider_all_requests_local       = false

重啟 Rails app

在網站根目錄下新增

touch tmp/restart.txt

Reload 頁面就會觸發重新啟動

Once Passenger has noticed that the file’s timestamp has changed, it will restart the application.

Mac

一般鍵盤接 Mac

  1. 系統偏好設定鍵盤
  2. TAG選鍵盤,再點下面的變更鍵
  3. 下拉選單選USB插上的鍵盤 → OPTIONOPTION

也就是兩個對調,ALT 就成了⌘ 鍵

RAR

安裝

brew install unrar

將 multi part rar files 解開

unrar x -e video.part1.rar

會自動將其它部份一起解開

iTerm

  • + d : 垂直 split windows
  • + shift + d : 水平 split windows
  • + t : 打開分頁
  • + shift + 左右鍵 : 移動 tab 位置
  • + 左右 : 切換 iterm2 的 tab
  • + shift + { or } : 切換 tab // 與上行功能一樣, 可刪?? 20140309
  • alt + 滑鼠左鍵 : 用滑鼠移動vim游標
  • + / : highlight 目前游標的位置

連接 samba (網路芳鄰)

finder -> 前往 -> 連接伺服器 -> 輸入 smb://192.168.1.236/samba

URL 後面的 samba 為主機定義

在 terminal 下使用 sublime 開啟檔案

ln -s /Applications/Sublime\ Text\ 2.app/Contents/SharedSupport/bin/subl /usr/local/bin/sublime

finder

  • + shift + g : 輸入指定路徑並前往該資料夾

觸控板

  • 觸控板 - 4指向兩側擴散 : 回到桌面
  • 觸控板 - 4指向中間集中 : 回到工作的視窗

符號

  • : Shift + Alt + k
  • Ctrl + + Space : 表情符號

複製/貼上

檔案複製

  • + C : 複製
  • + V : 貼上 (原檔案還在)

檔案剪下

  • + C : 複製
  • + option + V : 貼上 (原檔案不在, 會移動到新的位置)

其他

  • + alt + d : 查詢字典
  • + A : 全選
  • + Z : 復原
  • + P : 播放英文語音
  • + W : 關閉視窗
  • + Q : 結束應用程式
  • + + : 放大頁面
  • + : 縮小頁面

系統

  • + Option + Esc : 叫出強制結束應用程式列表
  • + Tab : 切換視窗
  • + , : 叫出目前程式的偏好設定

編輯

  • + 方向鍵右鍵 : 將Cursor移至行末
  • + 方向鍵左鍵 : 將Cursor移至行首
  • + 方向鍵上鍵 : 跳到文件頂端
  • + 方向鍵下鍵 : 跳到文件底端

截圖

  • + shift + 3 : Screenshot
  • + shift + 4 : Screenshot crop
  • + shift + 4 + space : Screenshot 選定的視窗

Safari && Chrome && Firefox && iTerm

  • + T : 開新Tab
  • + W : 關閉Tab

清除 DNS Cache

# OSX 10.10 版本使用
sudo discoveryutil udnsflushcaches

更新 vim

# 如果沒有 mercurial 要先更新
brew install mercurial
brew install vim

如果安裝完後, 用 vim -v 判斷判本不是用最新的 vim 的話

可能是 $PATH/usr/bin/usr/local/bin 前面, 所以實際上執行還是抓到 mac 內建的 vim (/usr/bin/vim)

只要將原本的 /usr/bin/vim rename, 應該就能吃到 brew 安裝的 /usr/local/bin/vim

sudo mv /usr/bin/vim /usr/bin/vim7.3

Terminal call Finder

pop-up / alert

osascript -e 'tell app "System Events" to display dialog "Hello World"'

open finder

open .

目前連接 USB 的硬體有哪些

ioreg -p IOUSB -w0 | sed 's/[^o]*o //; s/@.*$//' | grep -v '^Root.*'
或
system_profiler SPUSBDataType

Change screenshot save location

defaults write com.apple.screencapture location /Users/jex/Desktop/Screenshots
killall SystemUIServer

Resolution

如果接上的螢幕沒有太多解析度選, 就按著 option 再選 Scaled 就會挑出隱藏版的解析度了

Kill 一直再生的 process

e.g. forticlient

sudo kill .......
launchctl list | grep forti
launchctl remove com.fortinet.credential_store

Linux Mount 掛載

mount 唯讀

mount -r /dev/sda1 /test

掛載 usb

sudo fdisk -l
cd /mnt
sudo mkdir usb
sudo mount -v -t ntfs /dev/sdb5 /mnt/usb

-v, –verbose : Verbose mode. -t, –types vfstype : The argument following the -t is used to indicate the filesystem type. -t 不一定要明確指定格式, 也可以讓系統自動判別, ex: -t auto

卸載 usb

sudo umount /dev/sdb5

Shell Script Examples

時間

DATE=`date +%Y-%m-%d`               // 2016-12-24
DATE=`date +%Y-%m-%d:%H:%M:%S`      // 2016-12-24:10:28:58

取得網卡 mac

pi@raspbmc:~$ ifconfig |awk '/eth/{print $1,$5}'
eth0 b8:27:eb:7e:6e:3f

測試複製(cp)速度

#!/bin/bash
begin=`date "+%s%N"`
cp /test/iso /test/qq/iso
end=`date "+%s%N"`
rel=$(($end-$begin))
echo $rel
  • 顯示結果為奈秒。
  • date "+%s%N" 為奈秒

shell script 判斷套件是否已安裝, 如果未安裝自動安裝(自動輸入yes)

if dpkg -s ${package} | grep -q installed; then
    echo "${package} installed"
else
    echo "${package} isn't installed."
    echo "Install ${package}.."
    sudo apt-get --force-yes --yes install ${package}
fi

取得 route IP (Gateway IP)

route -n | grep 'UG[ \t]' | awk '{print $2}'

or :

ip r | awk '/^def/{print $3}'

or :

netstat -rn |awk '{if($1=="0.0.0.0") print $2}'

檢查是否能上網

router_ip=$(netstat -rn |awk '{if($1=="0.0.0.0") print $2}')
ping -c1 ${router_ip} >> /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo OK
else
    echo FAIL
fi

檢查 dns 是否運作正常

# Check DNS
set +e
echo "Check DNS ..."
function check_dns()
{
    is_successful=0
    for ((i=0; i<2; i++)); do
        host www.google.com > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            is_successful=1
            break;
        else
            echo "DNS can't function ..."
            echo "Restart networking and check again ..."
            sudo service networking restart
        fi
    done
    return $is_successful
}
check_dns
if [ $? == 0 ]; then
    echo "DNS can't function ..."
    exit
fi
set -e

選擇 partition (private)

set +e
dev_array=(`echo $(sudo fdisk -l | grep -E '^\/dev\/sda([3-9]|\d{2})' | awk '{print $1}') | cut  -d " " --output-delimiter=" " -f 1-`)
dev_array_count=$((${#dev_array[@]}-1))
until [ "${xfs_choice_confirm}" == "y" ] || [ "${xfs_choice_confirm}" == "Y" ]
do
    df
    sudo fdisk -l
    echo -e "\nDevice list :"
    echo "----------------------"
    for ((i=0; i<=$dev_array_count; i++)); do
        printf " \E[0;33;40m${i}\E[0m : ${dev_array[$i]}\n"
    done
    echo "----------------------"
    printf "Please choose XFS partition by entering a number : "
    read  dev_choice
    echo "${dev_choice}" | grep -o "^[0-9]*$" >> /dev/null 2>&1
    if [ ! $? -eq 0 ] || [ -z ${dev_array[${dev_choice}]} ]; then
        continue
    fi
    printf "You choose \E[0;33;40m${dev_array[${dev_choice}]}\E[0m, are you sure \E[0;31;40m(y/n)\E[0m ? "
    read xfs_choice_confirm
done
xfs_partition=${dev_array[${dev_choice}]}
set -e

顯示資料夾下的檔案及空間

d.sh :

for x in `find /tmp/destination -type f | sort`
do
  sum_y=`sum $x`
  echo "$sum_y $x";
done

顯示結果

$ bash d.sh
34198    19 /tmp/destination/dd.php
63612     3 /tmp/destination/qq/fff/ffddxx.php
53866     2 /tmp/destination/qq/fff/pp.php
07572     1 /tmp/destination/qq/qq.php

第一列數字不清楚 第二列數字(19, 3, 2, 1)是換送成kb的容量

php syntax checking

#!/bin/bash

# For checking php and js syntax

if [ -z $1 ]; then
    echo
    echo "  ***Missing arguments"
    echo
    exit
fi
for PATH in $*
do
    reg=".*(\.php|\.js)"
    if [[ $PATH =~ $reg  ]]; then
        if [ ${BASH_REMATCH[1]} == ".php" ]; then
            printf "\33[0;35;44m$PATH : \33[0m\n"
            /usr/bin/php -l $PATH
            echo "-------------------"
        elif [ ${BASH_REMATCH[1]} == ".js" ]; then
            printf "\33[0;36;44m$PATH : \33[0m\n"
            /usr/bin/sudo -H -u $USER bash -c "$HOME/test_project/bin/jslint -f $PATH"
            echo "-------------------"
        fi
    fi
done

產生亂數字串

This method uses SHA to hash the date, runs through base64, and then outputs the top 32 characters.

jex@jex:~$ date +%s | sha256sum | base64 | head -c 32 ; echo
MmM1MWFjMDVhYzBkMzk3ODRjM2JiZTQ5

This method used the built-in /dev/urandom feature, and filters out only characters that you would normally use in a password. Then it outputs the top 32.

jex@jex:~$ < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;
udqgTdSRa_zWN1m6E_1iKMzCjvqXUUpD

This one uses openssl’s rand function, which may not be installed on your system. Good thing there’s lots of other examples, right?

jex@jex:~$ openssl rand -base64 32
39iFjCtPB+4zjjsdJJ+FtFjBYPD3GUDRIZtXld5UyRM=

This one works a lot like the other urandom one, but just does the work in reverse. Bash is very powerful!

jex@jex:~$ tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
Rz27w5YH3FF8D4ughqbgHkHIcgds9T

Here’s an even simpler version of the urandom one.

jex@jex:~$ < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c6
RA0g58

You can even create a random left-hand password, which would let you type your password with one hand.

jex@jex:~$ </dev/urandom tr -dc '12345!@#$%qwertQWERTasdfgASDFGzxcvbZXCVB' | head -c8; echo ""
ZBFC@#AB

And here’s the easiest way to make a password from the command line, which works in Linux, Windows with Cygwin, and probably Mac OS X. I’m sure that some people will complain that it’s not as random as some of the other options, but honestly, it’s random enough if you’re going to be using the whole thing.

jex@jex:~$ date | md5sum
642a1b5de18a71b8585cfe87831d45a5  -

ref: random string

shell scripting and regular expression

echo "$password" | grep -q "[a-z]*[0-9][a-z]*"
if [ $? -eq 0 ] ;then
    echo "match found"
else
    echo "match not found"
fi
  • -q, –quiet, –silent
  • Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if
  • an error was detected. Also see the -s or –no-messages option. (-q is specified by POSIX.)

match $1

reg=".*(\.php)"
if [[ $PATH =~ $reg  ]]; then
    echo ${BASH_REMATCH[1]}
fi

input : t.php

result : .php

多行取代

sed "N;s/xxx/ccc/g" filename 因為加上 N 參數確實可以多行 matching, 但是當你要取代一個範圍(可能預期 match 到 3~6 行做取代) 卻是沒有辦法的..暈

那我將一個檔案讀取出來處理完再存到另一個檔案總沒問題了吧? 原則上是這樣沒錯..但是有一個小問題是行首的空白會不見.. 原來 bash shell 預設斷行是 space, tabs, new line(\n), 不過我們可以控制斷行的字元 - IFS(nternal Field Separator) 這個變數, 當 shell 內部讀取每一行時一遇到 \n 就中斷, 即可解決此問題, 請看以下程式碼 :

# 將檔案儲存到陣列
c=0
OLD_IFS=$IFS
IFS=$'\n'
while read -r line
do
    bashrcArray[$c]=$line
    c=$(expr $c + 1)
done < bashrc

# 這邊不做取代細節, 直接將陣列儲存成檔案
for i in "${bashrcArray[@]}"
do
    echo $i >> bashrcqq
done
IFS=$OLD_IFS
  • 注意 read 要加上 -r 參數, 否則反斜線(\)會被忽略
  • -r : If this option is given, backslash does not act as an escape character. The backslash is considered to be part of the line. In particular, a backslash-newline pair may not be used as a line continuation.

沒想到 shell script 處理多行取代居然會那麼麻煩

Linux Account

顯示 account 權限

show grants for jex@'%';

查詢所屬群組

$ id jex
uid=1000(jex) gid=1000(jex) groups=1000(jex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(sambashare),115(lpadmin)

useradd adduser差別

useradd 只會建立帳號,沒有Home, password, 系統shell

新增 account

sudo adduser newuser
或
RUN useradd --create-home -s /bin/bash jex

將帳號加上 sudo 權限

方法1

加入: 將帳號加入 sudo group

sudo usermod -a -G sudo jex
或
sudo adduser jex sudo
  • -a, –add USER add USER to GROUP
  • -d, –delete USER remove USER from GROUP

移除: 將帳號移除 sudo group

sudo gpasswd -d jex sudo

方法2

加入: 手動新增帳號到 /etc/sudoers :

# User privilege specification
root    ALL=(ALL:ALL) ALL
jex     ALL=(ALL:ALL) ALL

移除: 到 /etc/sudoers 刪除該筆帳號

刪除user

先退出group,再執行

sudo deluser my_account --remove-all-files

or

sudo userdel my_account
sudo rm -rf /home/my_account

List sudo account

執行

getent group sudo | cut -d: -f4

或者去 /etc/group 的 sudo 看

帳號相關資訊

/etc/passwd :

jex:x:1000:1000:jex,,,:/home/jex:/bin/bash
  • 可以改 sh 或 bash
  • 1000 為 uid

切換 www-data 帳號

直接用 su 會跳出錯誤訊息

su www-data
This account is currently not available.

應該用

su -s /bin/bash www-data

無法 ssh www-data 帳號

直接 ssh 是不行的

ssh www-data@localhost -p
www-data@localhost's password:
This account is currently not available.

因為 www-data 是被設定在 /usr/sbin/nologin

Linux 壓縮 (Gzip, Tar)

gzip

Usage: gzip [OPTION]... [FILE]...
Compress or uncompress FILEs (by default, compress FILES in-place).

Mandatory arguments to long options are mandatory for short options too.

  -c, --stdout      write on standard output, keep original files unchanged
  -d, --decompress  decompress
  -f, --force       force overwrite of output file and compress links
  -h, --help        give this help
  -l, --list        list compressed file contents
  -L, --license     display software license
  -n, --no-name     do not save or restore the original name and time stamp
  -N, --name        save or restore the original name and time stamp
  -q, --quiet       suppress all warnings
  -r, --recursive   operate recursively on directories
  -S, --suffix=SUF  use suffix SUF on compressed files
  -t, --test        test compressed file integrity
  -v, --verbose     verbose mode
  -V, --version     display version number
  -1, --fast        compress faster
  -9, --best        compress better
  --rsyncable       Make rsync-friendly archive

With no FILE, or when FILE is -, read standard input.

qq 資料夾下的檔案

$ ls qq
c.txt  d.txt  f.txt  q.txt

壓縮資料夾

$ gzip -r qq
$ ls qq
c.txt.gz  d.txt.gz  f.txt.gz  q.txt.gz

壓縮單檔

$ gzip qq.log
$ ls
$ qq.log.gz

解壓縮

$ gzip -rd qq
$ ls qq
c.txt  d.txt  f.txt  q.txt

gzip僅能壓縮單一檔案

tar

  • -z, –gzip, –gunzip, –ungzip filter the archive through gzip
  • -v, –verbose verbosely list files processed
  • -f, –file=ARCHIVE use archive file or device ARCHIVE

壓縮 qq 資料夾成 qq.tar.gz

tar -zcv -f qq.tar.gz qq

-c, –create create a new archive

查看 qq.tar.gz 裡有哪些檔案

tar -ztv -f qq.tar.gz

-t, –list list the contents of an archive

解壓縮 qq.tar.gz

tar -zxv -f qq.tar.gz

-x, –extract, –get extract files from an archive

解壓縮到 /tmp

tar -zxv -f qq.tar.gz -C /tmp
qq/
qq/d.txt
qq/c.txt
qq/f.txt
qq/q.txt
  • -C, –directory=DIR change to directory DIR
  • 原本的qq目錄也還會在

exclude 忽略檔案或資料夾

tar -zcv -f test.tar.gz test --exclude '.git*' --exclude 'cli.php'

忽略 .git 及 cli.php

gunzip

解壓縮 .gz 的檔案

gunzip access.log-20170725.gz

解壓縮 .zip 無法使用 targzip 來解壓縮

必須使用 zip / unzip 做

安裝 :

sudo apt-get install zip unzip

解壓縮 :

unzip weibo-phpsdk-v2-2013-02-20.zip -d qq

解壓縮並放在 qq 資料夾裡, 如果不加上 -d qq 檔案就會散落在目前資料夾

壓縮 :

zip -r qq.zip qq
  • 將 qq 資料夾壓縮成 qq.zip
  • -r : recurse into directories

PHP 讀寫檔案

php讀寫檔

/home/user_me/test/t.txt :

line 1
line 2
line 3
line 4
line 5

讀:

$test = fopen('/home/user_me/test/t.txt', 'r');
while ( ! feof($test))
{
    echo fgets($test);
}
fclose($test);

feof — Tests for end-of-file on a file pointer

寫:

[1] 將第一行移除

$path = '/home/user_me/test/t.txt';
$contents = file_get_contents($path);
$rows = explode("\n", $contents);
array_shift($rows);
file_put_contents($path , join("\n", $rows));

join — Alias of implode()

[2] 新增到最後一行

$path = '/home/user_me/test/t.txt';
file_put_contents($path, md5(uniqid(rand(), TRUE)), FILE_APPEND);

[3] 控制整份檔案內容

$file_path = '/home/user_me/test/q.txt';
$fp = fopen($file_path, 'w');
$rows[] = 'line1';
$rows[] = 'line2';
$rows[] = 'line3';
fwrite($fp, join("\n", $rows));
fclose($fp);

fputs — Alias of fwrite()

寫 csv 檔:

$file_path = '/home/user_me/test/q.txt';
$fp = fopen($file_path, 'w');
fputcsv($fp, array('field 1', 'field 2', 'field 3'));
fputcsv($fp, array('1', '2', '3'));
fputcsv($fp, array('4', '5', '6'));
fputcsv($fp, array('7', '8', '9'));
fclose($fp);

Git - Submodule 基本語法 & 實例操作

基本語法

git submodule foreach git pull origin master : 將每個submodule重新pull git submodule update --init : update submodule

git subomdule init :

根據 .gitmodules 的名稱和 URL,將這些資訊註冊到 .git/config 內,可是把 .gitmodules 內不用的 submodule 移除,使用這個指令並沒辦法自動刪除 .git/config 的相關內容,必須手動刪除;

git submodule update :

根據已註冊(也就是 .git/config )的 submodule 進行更新,例如 clone 遺失的 submodule,也就是上一段講的方法,所以執行這個指令前最好加上 --init

git submodule sync :

如果 submodule 的 remote URL 有變動,可以在 .gitmodules 修正 URL,然後執行這個指令,便會將 submodule 的 remote URL 更正

加入submodule :

git submodule add https://github.com/msanders/snipmate.vim.git bundle/snipmate.vim

clone時順便把submodule抓下來 :

git clone --recursive https://github.com/jex-lin/jex-vim.git clone

remove Git submodule (example : remove bundle/snipmate.vim)

$ oldPath="bundle/snipmate.vim"
$ git config -f .git/config --remove-section "submodule.${oldPath}"
$ git config -f .gitmodules --remove-section "submodule.${oldPath}"
$ git rm --cached "${oldPath}"
rm 'bundle/snipmate.vim'
$ rm -rf "${oldPath}"
$ rm -rf ".git/modules/${oldPath}"
$ git add .gitmodules
$ git commit -m "Removed ${oldPath}"
[master f36aa2e] Removed bundle/snipmate.vim
 2 files changed, 4 deletions(-)
 delete mode 160000 bundle/snipmate.vim

ref: http://blog.chh.tw/posts/git-submodule/

實例操作

practice_submodule => 假設它是如 jQuery 之類的第三方程式,我們要把它掛進來 test1 => 我 test2 => 是你同事

  • 3個都是獨立的repo
  • practice_submodule 是 push 到 github 的 practice_submodule.git,假設裡面目前已有 commit 紀錄了
  • test1、test2 目前是空資料夾也還不是個repo,之後會將它 push 到 github 的 test.git

我要先把 practice_submodule 掛進來,並且 push 到github,你同事再去 pull 下來,但發現 practice_submodule 沒東西,所以要做兩個關鍵的動作,然候”第三方程式”更新了,你同事將它的 practice_submodule 更新到最新版後 push 到 github,我將它 pull 下來,並且發現你的 practice_submodule 裡還是舊的,你要做一個關鍵動作把它更新到最新版…

我(test1) - 將 submodule 掛進來並且 push

jex_lin@devm3 ~/public_html/test1$ git init                                                             #初始化
Initialized empty Git repository in /home/jex_lin/public_html/test1/.git/
jex_lin@devm3 ~/public_html/test1$ git remote add origin https://github.com/jex-lin/test.git            #加入遠端位置
jex_lin@devm3 ~/public_html/test1$ git submodule add https://github.com/jex-lin/practice_submodule.git  #將 submodule 掛進來
Cloning into 'practice_submodule'...
remote: Counting objects: 27, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 27 (delta 5), reused 24 (delta 2)
 Unpacking objects: 100% (27/27), done.
jex_lin@devm3 ~/public_html/test1$ git add .                              #add
jex_lin@devm3 ~/public_html/test1$ git submodule init                     #記得 submodule 掛進來後要將 submodule 做初始化,git 才會知道
jex_lin@devm3 ~/public_html/test1$ git commit -m "add submodule"          #commit
[master (root-commit) a47ffce] add submodule
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 practice_submodule
jex_lin@devm3 ~/public_html/test1(master)$ git push origin master -f      #push 到 github。因為我遠端目前已有 commit,所以我將它強制 push
Username for 'https://github.com': jex-lin
Password for 'https://jex-lin@github.com':
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 329 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/jex-lin/test.git
 + 67b6611...a47ffce master -> master (forced update)

你同事(test2) - pull 回來,並且做 submodule 的處理

jex_lin@devm3 ~/public_html/test2$ git init                                                   #初始化
Initialized empty Git repository in /home/jex_lin/public_html/test2/.git/
jex_lin@devm3 ~/public_html/test2$ git remote add origin https://github.com/jex-lin/test.git  #加入遠端位置
jex_lin@devm3 ~/public_html/test2$ git pull origin master                                     #pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/jex-lin/test
 * branch            master     -> FETCH_HEAD

jex_lin@devm3 ~/public_html/test2(master)$ git submodule            #查看 submodule 的commit id
-0fae9e91890d86e633a48ba3d6c43d6d1b783360 practice_submodule        #減號...有問題!  什麼問題?  不知道,之後知道再補充
                                                                    #這時候進去 practice_submodule/ 裡面是沒有東西的,還需要做兩個步驟

jex_lin@devm3 ~/public_html/test2(master)$ git submodule init       #將 submodule 初始化
Submodule 'practice_submodule' (https://github.com/jex-lin/practice_submodule.git) registered for path 'practice_submodule'
jex_lin@devm3 ~/public_html/test2(master)$ git submodule update     #更新 submodule
Cloning into 'practice_submodule'...
remote: Counting objects: 27, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 27 (delta 5), reused 24 (delta 2)
Unpacking objects: 100% (27/27), done.
Submodule path 'practice_submodule': checked out '0fae9e91890d86e633a48ba3d6c43d6d1b783360'

jex_lin@devm3 ~/public_html/test2(master)$ git submodule                      #這時候再查看 submodule 的 commit id
 0fae9e91890d86e633a48ba3d6c43d6d1b783360 practice_submodule (heads/master)   #最前面沒符號了,舒服多了,再進去 practice_submodule/ 裡面就有東西了

第三方程式(practice_submodule) - 發行新版本了! (這部份指令省略,隨便改個檔案,並且 add, commit, push )

你同事(test2) - 接下來”你同事”發現新版本釋出了,趕緊來用用 :b

jex_lin@devm3 ~/public_html/test2(master)$ cd practice_submodule/                         #進入 submodule 的資料夾
jex_lin@devm3 ~/public_html/test2/practice_submodule((no branch))$ git checkout master    #發現是(no branch),這時 pull 是沒用的,要先切到 master
Switched to branch 'master'
jex_lin@devm3 ~/public_html/test2/practice_submodule(master)$ git pull                    #然候將 practice_submodule 最新版下載下來
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From https://github.com/jex-lin/practice_submodule
   0fae9e9..7ca8d97  master     -> origin/master
Updating 0fae9e9..7ca8d97
Fast-forward
 test | 1 +
 1 file changed, 1 insertion(+)

jex_lin@devm3 ~/public_html/test2/practice_submodule(master)$ cd ..             #回上一層
jex_lin@devm3 ~/public_html/test2(master)$ git submodule                        #查看 submodule 的 commit id
+7ca8d979e730727ccfbaed3c0dbfe5c8ff3b209e practice_submodule (heads/master)     #是加號

                  #(略)... 做 add、commit

jex_lin@devm3 ~/public_html/test2(master)$ git submodule                        #再查看 submodule 的 commit id
 7ca8d979e730727ccfbaed3c0dbfe5c8ff3b209e practice_submodule (heads/master)     #正常了
jex_lin@devm3 ~/public_html/test2(master)$ git push origin master               #push 到 github
Username for 'https://github.com': jex-lin
Password for 'https://jex-lin@github.com':
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 275 bytes, done.
Total 2 (delta 0), reused 0 (delta 0)
To https://github.com/jex-lin/test.git
   a47ffce..d920edf  master -> master

你(test1) - 現在”你”要 pull 更新”你同事”的變更

jex_lin@devm3 ~/public_html/test1(master)$ git pull origin master               #pull 下來
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 0), reused 2 (delta 0)
Unpacking objects: 100% (2/2), done.
From https://github.com/jex-lin/test
 * branch            master     -> FETCH_HEAD
Updating a47ffce..d920edf
Fast-forward
 practice_submodule | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
jex_lin@devm3 ~/public_html/test1(master)$ git submodule                        #查看 submodule 的 commit id
+0fae9e91890d86e633a48ba3d6c43d6d1b783360 practice_submodule (heads/master)     #是加號,代表 submodule 是舊的,現在要將 submodule 更新
jex_lin@devm3 ~/public_html/test1(master)$ git submodule update                 #更新 submodule ,在做這個動作之前你可以進去 practice_submodule 的資料夾裡面看看,資料還是上一版的資料,但應該要是最新版的才對阿!所以才需要更新 submodule
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From https://github.com/jex-lin/practice_submodule
   0fae9e9..7ca8d97  master     -> origin/master
Submodule path 'practice_submodule': checked out '7ca8d979e730727ccfbaed3c0dbfe5c8ff3b209e'

jex_lin@devm3 ~/public_html/test1(master)$ git submodule                            #查看 submodule 的 commit id
 7ca8d979e730727ccfbaed3c0dbfe5c8ff3b209e practice_submodule (remotes/origin/HEAD)  #沒問題了,再去 submodule 裡面看,檔案都是最新版的

將 submodule 掛進來的套件停用在某個 commit id

有時候我們在專案裡使用某些套件並不會想一直隨著套件更新而也跟著更新, 因為可能會導致原本功能是正常的, 更新完就不正常了, 所以希望將掛進來的套件就一直停在固定的版本就好了

  1. 將套件 fork 到自己的 github 裡
  2. 到該 submodule 下 checkout 到要的 tag 或 commit id (會變成 no branch, 但不用理會)
  3. 回到專案根目錄下 add 全部及 commit, git submodule 就會記住該套件的 commit id, 之後拉下來都會是那個 commit id 的 code 了

Sed, Awk 指令

sed

  • -i : is used to affect the file.
  • ^ : is a beginning of line
  • $ : is a end of line
  • d : delete if there is a empty line
  • ((.|\n)*) : multiple line

刪除含有jex.conf及jex_test這兩個字串的行

sed "s/^.*jex\.conf.*jex_test.*$//g" fstab

這作法該行被刪除,但是會是空的一行

改成 :

sed "/^.*jex\.conf.*jex_test.*$/d" file.txt

該行被刪除,不會變成空行

去除空行

sed -i "/^\s*$/d" file.txt

取代空白

\s*

取代 windows 換行

sed -i 's/\r$//' wrap-trim.log

修改檔案內容

假設有一個test檔:

$a = 'ccc';
$b = 'ddd';
$c = 'ccc';
$d = 'fff';

將取代結果印出,但還不會做取代的動作

$ sed "s/'ccc'/'QQQ'/g" test
$a = 'QQQ';
$b = 'ddd';
$c = 'QQQ';
$d = 'fff';

取代檔案內容

sed -i "s/'ccc'/'QQQ'/g" test

取代檔案內容, 還會建一個原始檔案的輩份檔(.bak, 可自訂SUFFIX)

sed -i.bak "s/'ccc'/'QQQ'/g" test

test.bak 為原始檔案的輩份 ; test 為取代後的檔案

執行多個取代

sed -e "s/line1/line3/g" -e"s/line2/line4/g" /tmp/q.txt

使用-e串在後面 -e script, –expression=script : add the script to the commands to be executed -i[SUFFIX], –in-place[=SUFFIX] : edit files in place (makes backup if extension supplied)

sed: -e expression #1, char 26: extra characters after command :

注意脫逸字元

-e script, –expression=script : add the script to the commands to be executed

取代資料夾下所有檔案的取代的字串(recursive)

find . -type f -print0 | xargs -0 sed -i 's/from/to/g'

取得指定行數的區間, 並輸出成一個新檔案

sed -n 5870,5900p handler.log > /tmp/tmp.log

awk

預設以空白分隔

df | grep "/dev/loop" | awk '{print $2}'

% 分隔

df | grep "/dev/loop" | awk -F "%" '{print $2}'

print要用單引號括起來`, 不能用雙引號"

找出 lengh > 200 的行

awk 'length>200' common.log

Samba 網路芳鄰

安裝

sudo apt-get install samba

關閉

$ sudo service smbd stop
smbd stop/waiting

開啟

$ sudo service smbd start
smbd start/running, process 15892

重新啟動

$ sudo service smbd restart
smbd stop/waiting
smbd start/running, process 7782

查看目前狀態

$ sudo service smbd status
smbd start/running, process 9395

設定 /etc/samba/smb.conf : (append到最下面)

[share]                     <= 這個samba位置的名字, 變更時URL也會變
   path = /home/jex/share   <= 自已指定放置網芳的位置
   public = yes
   writable = yes
   printable = no
   #only guest = yes
   #guest ok = yes

連線

windows連進samba輸入\\{IP/Domain name}\share :

ex: \\192.168.74.65\share)

注意!! 資料夾權限

因為public = yes,所以每個人都可以丟東西進來,傳上來會是nobody:nogroup

所以samba的資料夾權限一定要設定都可寫

chmod a+w /samba資料夾