Google+ Followers

2017年2月7日 星期二

[Linux] 使用 xargs 執行任務,參數不只接在指令尾部

原先很習慣用 find 找到東西後,搭配 -exec 去執行任務,這時要串起來的指令很方便。然而,若是要在一個清單內,找到東西後,再接一串指令時,就會試試 xargs 這指令。只是 xargs 習慣都是把變數接在指令後頭:

$ grep 我 user.dict | xargs -n 1 echo | head -n 5
分我杯羹
天知地知,你知我知
禮豈為我設
誨爾諄諄,聽我藐藐
惠子知我


此例就是找到的關鍵字”我”,再丟給 echo 去印出來。但如果,想要把找到的關鍵字都包起來時:

找到”分我杯羹”了
找到”天知地知,你知我知”了
找到”禮豈為我設”了
找到”誨爾諄諄,聽我藐藐”了
找到”惠子知我”了


這時就會卡卡的,沒錯,就派 shell 上場了:

$ grep 我 user.dict | xargs -n 1 sh -c 'echo "找到\"${@}"\"了' "${0}" | head -n 5
找到"分我杯羹"了
找到"天知地知,你知我知"了
找到"禮豈為我設"了
找到"誨爾諄諄,聽我藐藐"了
找到"惠子知我"了


如此一來,透過 sh 在包一層,就可以完美使用 xargs 做事了。而我真正的需求是檢查一個檔案清單,查看清單內的檔案是否都存在:

$ grep keyword file-list.txt | xargs -n 1 sh -c 'test -r "${@}" || echo "${@} NOT FOUND" ' "${0}"

2017年2月3日 星期五

Ansible 筆記 - 透過 Reverse SSH Tunnel 維護機器

有時候就是這樣,某些機器本身連不進去,這時要測試發佈流程時,就適合從中間機器建立 SSH Tunnel 啦。例如 A <-> B <-> C 關係中,B 可以分別連到 A 跟 C ,但 A 跟 C 彼此無法連上。這時就從 B 的角色建立個 Reverse SSH Tunnel 了

@B:

$ ssh -NR 2266:C_HOST_IP:22 [email protected]_HOST_IP


如此一來,在 A 機器中,就可以自連 ssh 127.0.0.1:2266 就可以直達 C 機器囉!

接著,若在 A 機器發動 ansible deploy 任務,就會有需要指定 ansible-playbook 運作時,那個 ssh port 要換一下,所幸的,用 ansible-playbook -h 就有教學啦 XD 其中 sftp 跟 scp/ssh 的參數名不一樣,所以得個別設定,完整的範例:

@A:

$ HOST=tag_Ansible_MY_Task DATA="['127.0.0.1']" ansible-playbook -i bin/echo.sh MY_Task.yml --private-key=ssh-private-key.pem --sftp-extra-args="-oPORT=2266" --scp-extra-args="-p 2266" --ssh-extra-args="-p 2266"


此例 bin/echo.sh 只是一個讓我組出 ansible-playbook 所需的格式:

$ cat bin/echo.sh
#!/bin/bash
echo "{\"$HOST\":$DATA}"
$ HOST=tag_Ansible_MY_Task DATA="['127.0.0.1']" bin/echo.sh
{"tag_Ansible_MY_Task":['127.0.0.1']}
$ head -n 7 MY_Task.yml
---
- hosts: tag_Ansible_MY_Task
  remote_user: ubuntu
  become: true
  become_user: root
  become_method: sudo

2017年1月13日 星期五

[NodeJS] 批次處理 Website snapshot 並存進 MySQL DB @ Ubuntu Server 14.04

延續之前 [NodeJS] 使用 WebShot 進行網頁截圖、顯示正確的中文(CJK)等編碼 @ Ubuntu 14.04 Server 的部分,稍微改幾行 code 就支援批次處理啦

前置環境:

$ sudo apt-get install nodejs npm xfonts-wqy xfonts-kaname
$ sudo ln -s /usr/bin/nodejs  /usr/bin/node
$ mkdir -p job/images && cd job
$ npm install webshot
程式主體:

$ vim build.js

var output_dir = 'images';
var concurrent_limit = 10;
var running_task = 0;
var total_task = [
{ domain: 'tw.yahoo.com', url: 'https://tw.yahoo.com' } ,
{ domain: 'facebook.com', url: 'https://facebook.com' } ,
];

function build_website_snapshot() {
while(total_task.length > 0 && running_task < concurrent_limit) {
var item = total_task.shift();
var url = item.url;
var domain = item.domain;

// https://github.com/brenden/node-webshot
webshot(url, output_dir+'/'+domain+'.png', {
screenSize: {
width: 320,
height: 480,
},
shotSize: {
width: 320,
height: 320,
},
timeout: 20000,
renderDelay: 3000,
userAgent: 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.20 (KHTML, like Gecko) Mobile/7B298g'

}, function(err) {
if(err)
console.log(err);
running_task--;
if (running_task == 0)
console.log('done');
if (total_task.length > 0)
build_website_snapshot();
});
running_task++;
}
}


$ node build.js

如此一來,就稍微搞定批次產出了。若要把產出的東西存進 MySQL DB server,那可以再這樣做:

$ npm install mysql

$ vim import.js

var fs = require('fs');
var path = require('path');
var mysql = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'dbuser',
  password : 'dbpassword',
  database : 'dbname',
});

var scan_source_dir = 'images';
var files = [];
var sql_values = [];
fs.readdirSync(scan_source_dir).filter(function(file){
        //console.log(file);
        if (fs.statSync(path.join(scan_source_dir, file)).isFile() && file.lastIndexOf('.png') == (file.length - 4)) {
                var domain = file.substring(0, file.length - 4);
                //files[domain] = fs.readFileSync(path.join(scan_source_dir, file), {encoding: 'binary'});
                files[domain] = fs.readFileSync(path.join(scan_source_dir, file));

                sql_values.push([domain, files[domain], Math.round(new Date().getTime()/1000), Math.round(new Date().getTime()/1000)]);
        }
});
// console.log (sql_values);
/*
CREATE TABLE `snapshot_table ` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `domain` varchar(64) NOT NULL DEFAULT '',
  `image` blob,
  `createtime` int(11) DEFAULT NULL,
  `updatetime` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/

var sql = "INSERT INTO snapshot_table (domain, image, createtime, updatetime) VALUES ? ON DUPLICATE KEY UPDATE image=VALUES(image), updatetime=VALUES(updatetime) ";
connection.query(sql, [sql_values], function(err) {
        console.log(err);
});
connection.end();


如此一來,就可以自動掃目錄下符合 *.png 的檔案,並將 binary data 紀錄至 db server 中。

2016年12月29日 星期四

[PHP] 使用 str_getcsv 處理 CSV 格式 (Google Docs export CSV format)

這議題已經很久,之前一直沒踩到什麼,直到處理 Google doc export the CSV format ...

大部分的分析都用:

foreach ( @explode(file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...'), "\n") as $line ) {
$field = str_getcsv($line);
}


但是,若有 enclosure 的方式(例如 "...\n....\n") 就會出包啦,這在 http://php.net/manual/en/function.str-getcsv.php 網站上也有人提到,解法就是一樣用 str_getcsv 去切 $row:

$row = str_getcsv( file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...'), "\n");
foreach( $row as $line ) {
$field = str_getcsv($line);
}


但是,在處理 Google Docs 又發現切不對,最後發現改用 "\r\n" 即可 :P

$row = str_getcsv( file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...'), "\r\n");
foreach( $row as $line ) {
$field = str_getcsv($line);
}

2016年12月3日 星期六

[C] 使用 SQLite - sqlite3_bind_text 之 value 一直配對(綁定)錯誤

最近整理兩年前的程式,一直有空時會重構,由於時間拉太長了,我已不記得改過什麼,又懶得看 git log,就這樣東摸摸西摸摸,最後發現 bug 了。

有一張表:

CREATE TABLE IF NOT EXISTS test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
);


接著,不斷新增資料,並且透過 sqlite3_bind_text 維護:

INSERT OR IGNORE INTO test (data) VALUES (?),(?),(?)

然而,透過 sqlite3_bind_text 綁定時,一直出錯,起初以為是 UTF-8 問題,但早期程式也沒問題,透過觀察,發現記憶體不如預期,追了好一陣子,發現是 sqlite3_bind_text 的參數問題 Orz

https://www.sqlite.org/c3ref/c_static.html
Constants Defining Special Destructor Behavior

typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC      ((sqlite3_destructor_type)0)
#define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)


原因在於我一直用 SQLITE_STATIC 啦,但我的結構已經是 std::vector<std::unordered_map<std::string, std::string>> values 這種稍微複雜的,並且透過 iterator 不斷變換資料,因此,必須改用 SQLITE_TRANSIENT 才對。

std::vector<std::unordered_map<std::string, std::string>> values;
std::vector<std::string> fields ({ "data" });
for (auto item : values) {
for (auto field: fields) {
if (item.find(field) != item.end()) {
if (SQLITE_OK != sqlite3_bind_text(stmt, i, item[field].c_str(), -1, SQLITE_TRANSIENT)) {
}
} else {
if (SQLITE_OK != sqlite3_bind_null(stmt, i)) {
}
}
}
}


最後還是埋了個 gtest 定期測試了。