php + MongoDB + Sphinx 实现全文检索 (二)
2016-12-27 21:12
751 查看
上一篇文章写了大体思路, IndexDriver 只给了个接口, mysql 数据转移, sphinx 索引配置文件的建立, 这些地方都没有给出.
本着 talk is cheap 的原则,将这部分代码在这里补全.
首先给出一个由其派生的对接mongo的抽象类,所有由mongo数据建立的索引都继承该类:
然后是一个具体的实现类:
有了上面的一个 driver 和两个工具类,就可以写一个统一调度的 php 脚本.
然后扫描 drivers 目录,将其中的 *Driver.php 文件加载进来.
然后调用上述两个工具类,完成 索引配置的生成 和 索引的建立.
其中将主动重建索引和由 crontab 发起的重建索引动作当做两个动作对待:
如果是由 crontab 发起的,则记录其重建时间,用以辅助判断下个 crontab 窗口是否要发起重建.
最后再由一个 bash 脚本负责调用 sphinx 的 searchd 命令建立索引.
这个简单,就是个壳而已,不再贴出.
最最后,贴一下 config.ini 配置文件,以便阅读:
最最最后,一个根据上述配置文件建立预设目录的脚本.
规则是,上述配置文件中字段名以 _file 结尾的,则建立其父目录; 以 _path 结尾的,则建立其目录.
本着 talk is cheap 的原则,将这部分代码在这里补全.
IndexDriver
这里仅列出一个示例性质的具体实现类.首先给出一个由其派生的对接mongo的抽象类,所有由mongo数据建立的索引都继承该类:
<?php abstract class MongoIndexDriver implements IndexDriver { private $mongo_host; private $index_name; private $mongo_conn; public function __construct($mongo_host) { $this->mongo_host = $mongo_host; } /** * {@inheritDoc} * @see IndexDriver::getIndexName() */ public function getIndexName() { // use driver file name as index name. in this way, the index name // is always unique. if (!$this->index_name) { $class_name = get_class($this); $this->index_name = chop($class_name, "Driver"); } return $this->index_name; } protected function setMongoHost($host) { $this->mongo_host = $host; } protected function table($table_name, $database_name = "kaiba") { if (!$this->mongo_conn) { $this->mongo_conn = new MongoClient($this->mongo_host); } return $this->mongo_conn->$database_name->$table_name; } }
然后是一个具体的实现类:
<?php require_once SCRIPT_PATH.'/base/IndexDriver.php'; require_once SCRIPT_PATH.'/base/MongoIndexDriver.php'; require_once SCRIPT_PATH.'/base/IndexField.php'; class ExampleDriver extends MongoIndexDriver { const REFRESH_INTERVAL = 7 * 24 * 60; private $fields; private $mongo_table; public function __construct() { $ini = parse_ini_file(CONFIG_PATH, true); parent::__construct($ini['mongo']['host'].":".$ini['mongo']['port']); $this->fields = array( IndexField::createIntField("_id1"), IndexField::createIntField("_id2"), IndexField::createIntField("_id3"), IndexField::createIntField("_id4"), IndexField::createIntField("code"), IndexField::createIntField("type"), IndexField::createField("name"), IndexField::createField("content"), IndexField::createField("message"), ); } /** * {@inheritDoc} * @see IndexDriver::getIndexFields() */ public function getIndexFields() { return $this->fields; } /** * {@inheritDoc} * @see IndexDriver::getValues() */ public function getValues($offset, $limit) { $mongo_cursor = $this->table("example_table") ->find(array(), array( "_id", "code", "type", "name", "content", "message", )) ->skip($offset) ->limit($limit); $result_count = $mongo_cursor->count(); if ($result_count <= 0) { return null; } $result = array(); foreach ($mongo_cursor as $k => $v) { $value = array(); $_id1 = $_id2 = $_id3 = $_id4 = ""; // mongoId -> int $id_string = $v['_id'].""; for ($j = 0; $j < 4; $j ++) { $id_sub = substr($id_string, $j * 6, 6); $segment = hexdec($id_sub); ${"_id".($j + 1)} = $segment; } $value['_id1'] = $_id1; $value['_id2'] = $_id2; $value['_id3'] = $_id3; $value['_id4'] = $_id4; $value['code'] = $v['code']; $value['type'] = $v['type']; $value['name'] = $v['name']; $value['content'] = $v['content']; $value['message'] = $v['message']; $result[] = $value; } return $result; } /** * {@inheritDoc} * @see IndexDriver::shouldRefreshIndex() */ public function shouldRefreshIndex($last_refresh_time) { $hour = (int) date('H'); // only refresh index in midnight if ($hour > 4) { return false; } $minutes = (time() - $last_refresh_time) / 60; return $minutes + 5 > self::REFRESH_INTERVAL; } /** * {@inheritDoc} * @see IndexDriver::generateDocument() */ public function generateDocument() { // TODO Auto-generated method stub } }
索引建立工具类:
<?php require_once SCRIPT_PATH.'/base/IndexDriver.php'; require_once SCRIPT_PATH.'/base/IndexField.php'; require_once SCRIPT_PATH.'/utils/Logger.php'; /** * @author lx * date: 2016-11-25 * generate sphinx conf. used by searchd and indexer. */ class SphinxConfCreator { private $mysql_host; private $mysql_port; private $mysql_user; private $mysql_password; private $mysql_database; private $conf_file; private $index_data_path; private $charset_dictpath; private $pid_file; private $log_file; private $query_log_file; private $drivers; private $logger; public function __construct(array $drivers) { $this->logger = new Logger("SphinxConfGenerator"); if (empty($drivers)) { $msg = "IndexDriver array empty"; $this->logger->e($msg); throw new Exception($msg); } foreach ($drivers as $name => $driver) { if (! $driver instanceof IndexDriver) { $msg = "need a valid IndexDriver"; $this->logger->e($msg); throw new Exception($msg); } } $this->drivers = $drivers; } public function setMysqlHost($host, $port = null) { $this->mysql_host = $host; $this->mysql_port = $port; return $this; } public function setMysqlUser($user, $password) { $this->mysql_user = $user; $this->mysql_password = $password; return $this; } public function setMysqlDatabase($database) { $this->mysql_database = $database; return $this; } public function setConfFilePath($path) { $this->conf_file = $path; return $this; } public function setConfIndexDataPath($path) { $this->index_data_path = $path; return $this; } public function setConfCharsetDictPath($path) { $this->charset_dictpath = $path; return $this; } public function setConfPidFilePath($path) { $this->pid_file = $path; return $this; } public function setConfLogFilePath($path) { $this->log_file = $path; return $this; } public function setConfQueryLogFilePath($path) { $this->query_log_file = $path; return $this; } public function create() { $this->checkPath($this->conf_file, "conf file"); $this->checkPath($this->charset_dictpath, "charset dict path", true); $this->checkPath($this->index_data_path, "index data path"); $this->checkPath($this->pid_file, "pid file"); $this->checkPath($this->log_file, "log file"); $this->checkPath($this->query_log_file, "query log file"); $content = ""; foreach ($this->drivers as $name => $driver) { $content .= $this->sourceConf($driver); $content .= $this->indexConf($driver); } $content .= $this->indexerConf(); $content .= $this->searchdConf(); file_put_contents($this->conf_file, $content); // in case below files does not exist... touch($this->pid_file); touch($this->log_file); touch($this->query_log_file); } // ========================================================== public function sourceConf($driver) { $attr_lines = ""; $fields = $driver->getIndexFields(); foreach ($fields as $field) { if (!$field->isSphinxField()) { $attr_lines .= $this->getSourceAttrLine($field); } } $str = <<<EOF source {$driver->getIndexName()}_src { type = mysql sql_host = {$this->mysql_host} sql_user = {$this->mysql_user} sql_pass = {$this->mysql_password} sql_db = {$this->mysql_database} sql_port = {$this->mysql_port} sql_query_pre = SET NAMES utf8 sql_query_range = SELECT MIN(id), MAX(id) FROM {$driver->getIndexName()} sql_range_step = 1000 sql_query = {$this->getSourceSqlQuery($driver)} {$attr_lines} } EOF; return $str; } // ========================================================== public function indexConf($driver) { $str = <<<EOF index {$driver->getIndexName()} { source = {$driver->getIndexName()}_src path = {$this->index_data_path}/{$driver->getIndexName()} docinfo = extern mlock = 0 morphology = none min_word_len = 1 html_strip = 0 charset_dictpath = {$this->charset_dictpath} charset_type = zh_cn.utf-8 } EOF; return $str; } // ========================================================== public function indexerConf() { $str = <<<EOF indexer { mem_limit = 128M } EOF; return $str; } // ========================================================== public function searchdConf() { $str = <<<EOF searchd { listen = 9312 read_timeout = 5 max_children = 30 max_matches = 1000 seamless_rotate = 1 preopen_indexes = 1 unlink_old = 1 pid_file = {$this->pid_file} log = {$this->log_file} query_log = {$this->query_log_file} } EOF; return $str; } // ========================================================== private function checkPath($path, $where = "", $check_readable = false) { $msg = ""; if ($check_readable) { if (!is_readable($path)) { $msg = "file not exists: $path"; } } else if (empty($path)) { $msg = "path empty"; } else { // check parent dir $dir = dirname($path); if (!file_exists($dir)) { mkdir($dir); } if (!is_dir($dir)) { $msg = "parent dir not exists: $dir"; } else if (!is_readable($dir)) { $msg = "parent dir not readable: $dir"; } } if (!empty($msg)) { $msg = "$where, $msg"; $this->logger->e($msg); throw new Exception($msg); } } private function getSourceAttrLine($field) { $line = sprintf(" %s = %s \n", $field->getSphinxType(), $field->getName()); return $line; } private function getSourceSqlQuery($driver) { $sql_query = "SELECT id,"; $fields = $driver->getIndexFields(); $table = $driver->getIndexName(); $field_count = count($fields); for ($i = 0; $i < $field_count; $i ++) { $field_name = $fields[$i]->getName(); $sql_query = "$sql_query $field_name"; if ($i + 1 < $field_count) { $sql_query = "$sql_query,"; } } $sql_query = "$sql_query FROM $table WHERE id>=\$start AND id<=\$end"; return $sql_query; } }
mysql 数据中转工具类:
<?php require_once SCRIPT_PATH.'/base/IndexDriver.php'; require_once SCRIPT_PATH.'/base/IndexField.php'; require_once SCRIPT_PATH.'/utils/Logger.php'; /** * @author lx * date: 2016-11-25 * transmit data from whatever source to mysql database. */ class MysqlTransmitter { const DEBUG = true; private $mysql_host; private $mysql_port; private $mysql_user; private $mysql_password; private $mysql_database; private $mysqli; private $value_query_step = 100; private $value_insert_step = 10; private $driver; private $logger; private $field_count; private $mysql_insert_query_head; public function __construct($driver) { $this->logger = new Logger("MysqlTransmitter", Logger::LEVEL_DEBUG); if (! $driver instanceof IndexDriver) { $msg = "need a valid IndexDriver"; $this->logger->e($msg); throw new Exception($msg); } $this->driver = $driver; $this->field_count = count($driver->getIndexFields()); } public function setMysqlHost($host, $port = null) { $this->mysql_host = $host; $this->mysql_port = $port; return $this; } public function setMysqlUser($user, $password) { $this->mysql_user = $user; $this->mysql_password = $password; return $this; } public function setMysqlDatabase($database) { $this->mysql_database = $database; return $this; } public function setValueQueryStep($step) { $this->value_query_step = $step; return $this; } public function setValueInsertStep($step) { $this->value_insert_step = $step; return $this; } public function transmit() { // prepare mysql $this->mysqlConnect(); $this->mysqlDropTable(); $this->mysqlCreateTable(); // fill data to mysql $source_row_count = 0; for ($i = 0; ; $i ++) { // get values from whatever data source, with offset and limit $offset = $i * $this->value_query_step; $limit = $this->value_query_step; $values = $this->driver->getValues($offset, $limit); $value_count = count($values); if ($value_count == 0) { break; } $source_row_count += $value_count; $this->logger->d("about to insert $value_count rows"); $this->mysqlInsertValues($values); } // check how many rows we've been inserted $table_name = $this->driver->getIndexName(); $result= $this->mysqli->query("SELECT count(*) as total from $table_name"); $data = mysqli_fetch_assoc($result); $this->logger->i("$table_name, insert result, ". "$source_row_count rows from source, {$data['total']} rows to mysql"); $this->mysqli->close(); } // ============================================ private function checkMysqlError() { if ($this->mysqli->error) { $this->logger->e("mysql error: {$this->mysqli->error}"); throw new Exception("MySQL error {$this->mysqli->error}", $this->msqli->errno); } } private function mysqlConnect() { $this->mysqli = new mysqli($this->mysql_host, $this->mysql_user, $this->mysql_password); if ($this->mysqli->connect_error) { throw new Exception("MySQL connect fail, " . $this->mysqli->connect_error); } $this->mysqli->query("CREATE DATABASE IF NOT EXISTS {$this->mysql_database}"); $this->mysqli->select_db($this->mysql_database); $this->checkMysqlError(); $this->mysqli->query("SET NAMES UTF8"); $this->checkMysqlError(); } private function mysqlDropTable() { $table_name = $this->driver->getIndexName(); $this->mysqli->query("DROP TABLE IF EXISTS $table_name"); $this->checkMysqlError(); } private function mysqlCreateTable() { $table_name = $this->driver->getIndexName(); $fields = $this->driver->getIndexFields(); $create_query = "CREATE TABLE $table_name ( id int not null AUTO_INCREMENT, "; foreach ($fields as $field) { $create_query = $create_query .$field->getName()." ".$field->getMysqlType()." null, "; } $create_query = $create_query . " PRIMARY KEY (id) ) DEFAULT CHARSET = 'UTF8'"; $this->logger->d("create query: $create_query"); $this->mysqli->query($create_query); $this->checkMysqlError(); } private function mysqlInsertQueryHead() { if (!$this->mysql_insert_query_head) { $fields = $this->driver->getIndexFields(); $table = $this->driver->getIndexName(); $this->mysql_insert_query_head = "INSERT INTO $table ( "; for ($i = 0; $i < $this->field_count; $i ++) { $field_name = $fields[$i]->getName(); $this->mysql_insert_query_head = $this->mysql_insert_query_head.$field_name; if ($i + 1 < $this->field_count) { $this->mysql_insert_query_head = $this->mysql_insert_query_head.", "; } else { $this->mysql_insert_query_head = $this->mysql_insert_query_head.") "; } } } return $this->mysql_insert_query_head; } private function mysqlInsertValues($values) { // slice and insert values into mysql. // mysql has a length limit for its sql sentence, while a value used for // sphinx indexer is likely to be a long string. thus insert too much values // in one sql sentence may result in a truncated sql sentence, so the values // are sliced into sub arrays to do the insertion. $step = $this->value_insert_step; $fields = $this->driver->getIndexFields(); for ($j = 0; ; $j ++) { $value_set = array_slice($values, $j * $step, $step); $lines = count($value_set); if ($lines == 0) { break; } $insert_query = $this->mysqlInsertQueryHead()." VALUES "; $valid_row_count = 0; foreach ($value_set as $value) { $value_query = "("; $malformed = false; for ($k = 0; $k < $this->field_count; $k ++) { $v = $value[$fields[$k]->getName()]; if (empty($v)) { $value_query = "$value_query ''"; } else if (!is_numeric($v)) { // deal with the possible escape characters $v_real = $this->mysqli->real_escape_string($v); if (!$v_real) { $this->logger->v("mysql insert value, malformed: $v"); $malformed = true; } $value_query = "$value_query '".$v_real."'"; } else { $value_query = "$value_query $v"; } if ($k + 1 < $this->field_count) { $value_query = "$value_query , "; } else { $value_query = "$value_query ) "; } } if ($malformed) { $this->logger->e("mysql insert value fail, malformed. $value_query"); } else { $this->logger->v("mysql insert value: $value_query"); $insert_query = "$insert_query $value_query ,"; $valid_row_count ++; } } if ($valid_row_count > 0) { $insert_query = substr($insert_query, 0, strlen($insert_query) - 1); $this->logger->v("insert query: ".$insert_query); $this->mysqli->query($insert_query); if ($this->mysqli->error) { $this->logger->e("insert error for: $insert_query"); $this->logger->e("insert error: {$this->mysqli->error}"); } } } } }
有了上面的一个 driver 和两个工具类,就可以写一个统一调度的 php 脚本.
统一调度脚本:
该脚本首先加载一个 config.ini 配置文件,所有sphinx目录等配置都是从这个ini文件读取的.然后扫描 drivers 目录,将其中的 *Driver.php 文件加载进来.
然后调用上述两个工具类,完成 索引配置的生成 和 索引的建立.
其中将主动重建索引和由 crontab 发起的重建索引动作当做两个动作对待:
如果是由 crontab 发起的,则记录其重建时间,用以辅助判断下个 crontab 窗口是否要发起重建.
<?php define('DEBUG', true); define('APP_PATH', realpath(dirname(__FILE__)."/../..")); define('SCRIPT_PATH', APP_PATH."/sphinx/script_php"); define('CONFIG_PATH', APP_PATH."/config/config.ini"); define('DRIVER_PATH', SCRIPT_PATH."/drivers/*Driver.php"); define('BASH_TOOL_PATH', APP_PATH."/sphinx/script_bash/sphinx_tool.sh"); require_once SCRIPT_PATH.'/utils/Logger.php'; require_once SCRIPT_PATH.'/utils/SphinxConfCreator.php'; require_once SCRIPT_PATH.'/utils/MysqlTransmitter.php'; require_once SCRIPT_PATH.'/utils/DocumentCreator.php'; foreach (glob(DRIVER_PATH) as $d) { require_once $d; } /** * h: print help * c: rebuild sphinx config file. * a: auto transmit index data to mysql. interval will be checked before transmit. * t: transmit index data to mysql. a index name can be specified, or else all index data will be refreshed. */ $opts = "hcar:"; // ============================================================ // get args $args = getopt($opts); if (isset($args['h'])) { printHelp(); exit(0); } // read config file $ini = parse_ini_file(CONFIG_PATH, true); $pref_path = $ini['builder']['builder_pref_path']; // init logger Logger::init(APP_PATH."/".$ini["builder"]["builder_log_file"].".".date("Y-m"), Logger::LEVEL_DEBUG, Logger::LEVEL_INFO); Logger::ii("firing up !!!"); try { // check mandatory dirs first checkPreCreateDirs(); // load all driver files under driver path $drivers = loadDrivers(DRIVER_PATH); // check rebuild sphinx conf file $conf_file_path = APP_PATH."/".$ini["sphinx"]["conf_file"]; if (isset($args['c']) || !file_exists($conf_file_path)) { logi("rebuild sphinx conf"); rebuildSphinxConf($drivers); } $index_opt = ""; if (isset($args['r'])) { $index_name = $args['r']; if ($index_name == "--all") { logi("transmit data for all indexes"); foreach ($drivers as $name => $driver) { logi("transmit data for index: $name"); transmitDataToMysql($driver); } $index_opt = "--all"; } else { // try refresh the specified index if (isset($drivers[$index_name])) { logi("transmit data for specified index: $index_name"); transmitDataToMysql($drivers[$index_name]); $index_opt = $index_name; } else { logi("transmit data, unknown index: $index_name"); exit(1); } } } else if (isset($args['a'])) { logi("auto transmit data for all indexes"); foreach ($drivers as $name => $driver) { if (isRequireAutoIndex($driver)) { logi("auto transmit data for index: $name"); transmitDataToMysql($driver); updateAutoIndexTime($driver); $index_opt .= "$name "; } else { logi("auto transmit data for index: $name, interval not meet, ignore"); } } } // if $index_opt is not empty, run sphinx indexer to refresh index if (!empty($index_opt)) { runIndexer($index_opt); } exit(0); } catch (Exception $e) { Logger::ee($e->getTraceAsString()); exit(1); } // ====================================================== function printHelp() { echo <<<EOF USAGE: index_tool -a [-c] 重建索引.该用法主要用于 crontab 定时任务.上次更新时间,及 driver file 指定的重建间隔将被比较. -c 参数指定重建 sphinx 索引配置文件. index_tool -r index_name [-c] 重建指定索引.该用法用于手动更新索引,不会检查重建时间间隔. -c 参数指定重建 sphinx 索引配置文件. index_tool -r --all [-c] 重建全部索引.该用法用于手动更新索引,不会检查重建时间间隔. -c 参数指定重建 sphinx 索引配置文件. index_tool -c 重建索引配置文件. DESCRIPTION: 用于根据 driver file, 重建索引 及 重建索引配置文件. -a 参数用于 crontab 定时任务,在该参数下重建索引,会考虑 driver file 内指定的重建索引最小间隔参数. EOF; } function logi($msg) { //$GLOBALS['logger']->i($msg); Logger::ii($msg, "index_tool"); } function checkPreCreateDirs() { $ini = $GLOBALS['ini']; $sphinx_data_path = APP_PATH."/".$ini["sphinx"]["index_data_path"]; if (!file_exists($sphinx_data_path)) { throw new Exception("pre-create dirs not ready !"); die(); } } function loadDrivers($driver_dir) { $drivers = array(); foreach (glob($driver_dir) as $d) { $splits = explode("/", $d); $driver_name = chop($splits[count($splits) - 1], ".php"); $driver = new $driver_name; if ($driver) { $index_name = $driver->getIndexName(); $drivers[$index_name] = $driver; logi("load driver: $index_name -> $d"); } } return $drivers; } function rebuildSphinxConf($drivers) { $ini = $GLOBALS['ini']; $conf_file_path = APP_PATH."/".$ini["sphinx"]["conf_file"]; if (file_exists($conf_file_path)) { // backup conf file $conf_file_bkp_name = basename($conf_file_path) .".". date("YmdHis"); $conf_file_bkp_path = APP_PATH."/".$ini['sphinx']['conf_bkp_path']."/".$conf_file_bkp_name; copy($conf_file_path, $conf_file_bkp_path); } $sphinx_conf_creator = new SphinxConfCreator($drivers); $sphinx_conf_creator ->setMysqlHost($ini["mysql"]["host"], $ini["mysql"]["port"]) ->setMysqlUser($ini["mysql"]["user"], $ini["mysql"]["password"]) ->setMysqlDatabase($ini["mysql"]["database"]) ->setConfFilePath($conf_file_path) ->setConfCharsetDictPath($ini["sphinx"]["charset_dictpath"]) ->setConfIndexDataPath(APP_PATH."/".$ini["sphinx"]["index_data_path"]) ->setConfPidFilePath(APP_PATH."/".$ini["sphinx"]["pid_file"]) ->setConfLogFilePath(APP_PATH."/".$ini["sphinx"]["log_file"]) ->setConfQueryLogFilePath(APP_PATH."/".$ini["sphinx"]["query_log_file"]) ->create(); } function transmitDataToMysql($driver) { $ini = $GLOBALS['ini']; $transmitter = new MysqlTransmitter($driver); $transmitter ->setMysqlHost($ini["mysql"]["host"], $ini["mysql"]["port"]) ->setMysqlUser($ini["mysql"]["user"], $ini["mysql"]["password"]) ->setMysqlDatabase($ini["mysql"]["database"]) ->setValueQueryStep(200) ->setValueInsertStep(50) ->transmit(); } function updateAutoIndexTime($driver) { $pref_path = $GLOBALS['pref_path']."/".$driver->getIndexName()."_auto_index_time"; file_put_contents($pref_path, time() . ""); } function isRequireAutoIndex($driver) { $pref_path = $GLOBALS['pref_path']."/".$driver->getIndexName()."_auto_index_time"; $last_update_time = 0; if (is_readable($pref_path)) { $last_update_time = (int) file_get_contents($pref_path); } return $driver->shouldRefreshIndex($last_update_time); } function runIndexer($index_opt) { $cmd = BASH_TOOL_PATH." $index_opt"; $output = shell_exec($cmd); logi("bash: $cmd\n$output"); }
最后再由一个 bash 脚本负责调用 sphinx 的 searchd 命令建立索引.
这个简单,就是个壳而已,不再贴出.
最最后,贴一下 config.ini 配置文件,以便阅读:
# author: lx # setup.sh will create parent dirs for configs with name ends with _file; # setup.sh will create dirs for configs with name ends with _path. [mysql] host = 127.0.0.1 port = 3306 user = root password = xxxxxxxxx database = sphinx_source [mongo] host = 127.0.0.1 port = 27017 [sphinx] host = 127.0.0.1 port = 9312 searchd = /usr/local/coreseek/bin/searchd indexer = /usr/local/coreseek/bin/indexer charset_dictpath = /usr/local/mmseg3/etc/ conf_file = var/sphinx/index.conf index_data_path = var/sphinx/data pid_file = var/sphinx/searchd.pid log_file = var/sphinx/logs/searchd.log query_log_file = var/sphinx/logs/searchd_query.log conf_bkp_path = var/sphinx/index_backup [builder] php_bin = /usr/local/php/bin/php builder_log_file = var/builder/logs/builder.log builder_pref_path = var/builder/preferences [request] request_log_file = var/request/logs/request.log
最最最后,一个根据上述配置文件建立预设目录的脚本.
规则是,上述配置文件中字段名以 _file 结尾的,则建立其父目录; 以 _path 结尾的,则建立其目录.
#!/bin/bash # author : liuxu # date : 2016-12-23 PROJ_PATH=$(dirname $(git rev-parse --git-dir)) CONF_FILE=$PROJ_PATH/config/config.ini if [ -e $CONF_FILE ]; then paths=$(sed -n "/\w\+_path\s*=.*/"p $CONF_FILE | sed 's/[[:space:]]\+//g' | awk -F "=" '{print $2}') files=$(sed -n "/\w\+_file\s*=.*/"p $CONF_FILE | sed 's/[[:space:]]\+//g' | awk -F "=" '{print $2}') cd $PROJ_PATH for p in ${paths[*]}; do mkdir -vp $PROJ_PATH/$p done for f in ${files[*]}; do d=$(dirname $PROJ_PATH/$f) mkdir -vp $d done cd - else echo "* config.ini not exists." fi
相关文章推荐
- php + MongoDB + Sphinx 实现全文检索 (一)
- [全文检索]用PHP调用Lucene包来实现全文检索
- sphinx全文检索之PHP使用教程
- [转]用PHP调用Lucene包来实现全文检索
- PHP+redis实现超迷你全文检索
- PHP特级课视频教程_第二十五集 Sphinx全文检索_李强强
- sphinx全文检索之PHP使用教程
- 用PHP调用Lucene包来实现全文检索
- PHP+MYSQL实现全文检索及全文检索工具
- 用PHP调用Lucene包来实现全文检索
- sphinx全文检索之PHP使用教程
- [转]用PHP调用Lucene包来实现全文检索
- [转]用PHP调用Lucene包来实现全文检索
- php中调用lucene实现网站的全文检索
- sphinx全文检索之PHP使用教程
- 用PHP调用Lucene包来实现全文检索(转)
- PHP 中文分词及全文检索的实现
- php启用sphinx全文搜索的实现方法
- PHP+MYSQL实现全文检索及全文检索工具
- sphinx全文检索之PHP使用教程