ERROR 1442 (HY000): Can't update table '...' in stored function/trigger because it is already used

Mysqlliuliangsong 发表了文章 • 0 个评论 • 129 次浏览 • 2016-12-22 16:31 • 来自相关话题

一、MySQL错误现象:MySQL执行创建的触发器时,报如下错误:ERROR 1442 (HY000): Can't update table 'tmp' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
 下面为错误触发器的创建过程:1、创建测试表格:drop table if exists tmp;
create table tmp (id int, n1 int, n2 int);
insert tmp values(1, 10, 50);

MariaDB [test]> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.01 sec)2、创建一个MySQL触发器:DELIMITER $
drop trigger if exists tmp_update$
create trigger tmp_update
after update on tmp
for each row
begin
update tmp set n2=n1*5 where id=new.id;
end$
DELIMITER ;3、测试触发效果:mysql> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.00 sec)

mysql> update tmp set n1=2 where id=1;
ERROR 1442 (HY000): Can't update table 'tmp' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. 二、错误原因:  这是由于MySQL触发器,触发事件的表与触发更新的表是用一表。触发更新同一表时,不应该使用update语句更新,应该使用set修改NEW对象更新即可。
 三、解决方案:1、将触发器的创建SQL语句改成如下并执行创建即可:DELIMITER $
drop trigger if exists tmp_update$
create trigger tmp_update
before update on tmp -- 更新之前执行,这样才能在NEW插入到数据库之前,修改NEW.n2
for each row
begin
set new.n2 = new.n1*5; -- 直接修改new.n2
end$
DELIMITER ; 触发器测试效果:aiezu.com> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.00 sec)

aiezu.com> update tmp set n1=12 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

aiezu.com> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 12 | 60 |
+------+------+------+
1 row in set (0.00 sec) 查看全部

一、MySQL错误现象:

MySQL执行创建的触发器时,报如下错误:
ERROR 1442 (HY000): Can't update table 'tmp' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

 下面为错误触发器的创建过程:

1、创建测试表格:

drop table if exists tmp;
create table tmp (id int, n1 int, n2 int);
insert tmp values(1, 10, 50);

MariaDB [test]> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.01 sec)

2、创建一个MySQL触发器:

DELIMITER $
drop trigger if exists tmp_update$
create trigger tmp_update
after update on tmp
for each row
begin
update tmp set n2=n1*5 where id=new.id;
end$
DELIMITER ;

3、测试触发效果:

mysql> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.00 sec)

mysql> update tmp set n1=2 where id=1;
ERROR 1442 (HY000): Can't update table 'tmp' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
 

二、错误原因:

  这是由于MySQL触发器,触发事件的表与触发更新的表是用一表。触发更新同一表时,不应该使用update语句更新,应该使用set修改NEW对象更新即可。
 

三、解决方案:

1、将触发器的创建SQL语句改成如下并执行创建即可:
DELIMITER $
drop trigger if exists tmp_update$
create trigger tmp_update
before update on tmp -- 更新之前执行,这样才能在NEW插入到数据库之前,修改NEW.n2
for each row
begin
set new.n2 = new.n1*5; -- 直接修改new.n2
end$
DELIMITER ;
 触发器测试效果:
aiezu.com> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 10 | 50 |
+------+------+------+
1 row in set (0.00 sec)

aiezu.com> update tmp set n1=12 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

aiezu.com> select * from tmp;
+------+------+------+
| id | n1 | n2 |
+------+------+------+
| 1 | 12 | 60 |
+------+------+------+
1 row in set (0.00 sec)

MySQL触发器用法详解

Mysqlliuliangsong 发表了文章 • 0 个评论 • 295 次浏览 • 2016-12-22 10:53 • 来自相关话题

一、MySQL触发器创建:1、MySQL触发器的创建语法:CREATE [DEFINER = { 'user' | CURRENT_USER }] 
TRIGGER trigger_name
trigger_time trigger_event
ON table_name
FOR EACH ROW
[trigger_order]
trigger_body 2、MySQL创建语法中的关键词解释:字段含义可能的值DEFINER=可选参数,指定创建者,默认为当前登录用户(CURRENT_USER);
该触发器将以此参数指定的用户执行,所以需要考虑权限问题;DEFINER='root@%'
DEFINER=CURRENT_USERtrigger_name触发器名称,最好由表名+触发事件关键词+触发时间关键词组成; trigger_time触发时间,在某个事件之前还是之后;BEFORE、AFTERtrigger_event触发事件,如插入时触发、删除时触发;
  INSERT:插入操作触发器,INSERT、LOAD DATA、REPLACE时触发;
  UPDATE:更新操作触发器,UPDATE操作时触发;
  DELETE:删除操作触发器,DELETE、REPLACE操作时触发;INSERT、UPDATE、DELETEtable_name 触发操作时间的表名; trigger_order可选参数,如果定义了多个具有相同触发事件和触法时间的触发器时(
如:BEFORE UPDATE),默认触发顺序与触发器的创建顺序一致,可以
使用此参数来改变它们触发顺序。mysql 5.7.2起开始支持此参数。
  FOLLOWS:当前创建触发器在现有触发器之后激活;
  PRECEDES:当前创建触发器在现有触发器之前激活;FOLLOWS、PRECEDEStrigger_body触发执行的SQL语句内容,一般以begin开头,end结尾begin .. end 3、触发执行语句内容(trigger_body)中的OLD,NEW:  在trigger_body中,我们可以使用NEW表示将要插入的新行(相当于MS SQL的INSERTED),OLD表示将要删除的旧行(相当于MS SQL的DELETED)。通过OLD,NEW中获取它们的字段内容,方便在触发操作中使用,下面是对应事件是否支持OLD、NEW的对应关系:事件OLDNEWINSERT×√DELETE√×UPDATE√√  由于UPDATE相当于删除旧行(OLD),然后插入新行(NEW),所以UPDATE同时支持OLD、NEW;
 4、MySQL分隔符(DELIMITER):  MySQL默认使用“;”作为分隔符,SQL语句遇到“;”就会提交。而我们的触发器中可能会有多个“;”符,为了防止触发器创建语句过早的提交,我们需要临时修改MySQL分隔符,创建完后,再将分隔符改回来。使用DELIMITER可以修改分隔符,如下:DELIMITER $
... --触发器创建语句;
$ --提交创建语句;
DELIMITER ; 二、MySQL触发器创建进阶:1、MySQL触发器中使用变量:  MySQL触发器中变量变量前面加'@',无需定义,可以直接使用:-- 变量直接赋值
set @num=999;

-- 使用select语句查询出来的数据方式赋值,需要加括号:
set @name =(select name from table);2、MySQL触发器中使用if语做条件判断:-- 简单的if语句:
set sex = if (new.sex=1, '男', '女');

-- 多条件if语句:
if old.type=1 then
update table ...;
elseif old.type=2 then
update table ...;
end if; 三、MySQL查看触发器:  可以使用“show triggers;”查看触发器。由于MySQL创建的触发器保存在“information_schema库中的triggers表中,所以还可以通过查询此表查看触发器:-- 通过information_schema.triggers表查看触发器:
select * from information_schema.triggers;

-- mysql 查看当前数据库的触发器
show triggers;

-- mysql 查看指定数据库"aiezu"的触发器
show triggers from aiezu; 四、MySQL删除触发器:1、可以使用drop trigger删除触发器:drop trigger trigger_name; 2、删除前先判断触发器是否存在:drop trigger if exists trigger_name 五、Msql触发器用法举例:1、MySQL触发器Insert触发更新同一张表:  下面我们有一个表“tmp1”,tmp1表有两个整型字段:n1、n2。我们要通过触发器实现,在tmp插入记录时,自动将n2字段的值设置为n1字段的5倍。
 创建测试表和触发器:-- 创建测试表
drop table if exists tmp1;
create table tmp1 (n1 int, n2 int);

-- 创建触发器
DELIMITER $
drop trigger if exists tmp1_insert$
create trigger tmp1_insert
before insert on tmp1
for each row
begin
set new.n2 = new.n1*5;
end$
DELIMITER ;测试触发更新效果:mysql> insert tmp1(n1) values(18);
Query OK, 1 row affected (0.01 sec)

mysql> insert tmp1(n1) values(99);
Query OK, 1 row affected (0.00 sec)

mysql> select * from tmp1;
+------+------+
| n1 | n2 |
+------+------+
| 18 | 90 |
| 99 | 495 |
+------+------+
2 rows in set (0.00 sec) 2、MySQL触发器Update触发更新另一张表:  下面有有两个表tmp1、tmp2,两个表都有一个相同的字段name。使用触发器实现更新一个表的name时,将另外一个表的name也更新。
 创建测试表和触发器:-- 创建测试表和插入测试数据
drop table if exists tmp1;
drop table if exists tmp2;
create table tmp1 (id int, name varchar(128)) default charset='utf8';
create table tmp2 (fid int, name varchar(128)) default charset='utf8';
insert into tmp1 values(1, '爱E族');
insert into tmp2 values(1, '爱E族');

-- 创建触发器
DELIMITER $
drop trigger if exists tmp1_update$
create trigger tmp1_update
after update on tmp1
for each row
begin
update tmp2 set name=new.name where fid=new.id;
end$
DELIMITER ;测试触发更新效果:mysql> select * from tmp1;
+------+---------+
| id | name |
+------+---------+
| 1 | 爱E族 |
+------+---------+
1 row in set (0.00 sec)

mysql> select * from tmp2;
+------+---------+
| fid | name |
+------+---------+
| 1 | 爱E族 |
+------+---------+
1 row in set (0.00 sec)

mysql> update tmp1 set name='aiezu.com' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from tmp1;
+------+-----------+
| id | name |
+------+-----------+
| 1 | aiezu.com |
+------+-----------+
1 row in set (0.00 sec)

mysql> select * from tmp2;
+------+-----------+
| fid | name |
+------+-----------+
| 1 | aiezu.com |
+------+-----------+
1 row in set (0.00 sec) 查看全部

一、MySQL触发器创建:

1、MySQL触发器的创建语法:

CREATE [DEFINER = { 'user' | CURRENT_USER }] 
TRIGGER trigger_name
trigger_time trigger_event
ON table_name
FOR EACH ROW
[trigger_order]
trigger_body

 

2、MySQL创建语法中的关键词解释:

字段含义可能的值
DEFINER=可选参数,指定创建者,默认为当前登录用户(CURRENT_USER);
该触发器将以此参数指定的用户执行,所以需要考虑权限问题;
DEFINER='root@%'
DEFINER=CURRENT_USER
trigger_name触发器名称,最好由表名+触发事件关键词+触发时间关键词组成; 
trigger_time触发时间,在某个事件之前还是之后;BEFORE、AFTER
trigger_event触发事件,如插入时触发、删除时触发;
  INSERT:插入操作触发器,INSERT、LOAD DATA、REPLACE时触发;
  UPDATE:更新操作触发器,UPDATE操作时触发;
  DELETE:删除操作触发器,DELETE、REPLACE操作时触发;
INSERT、UPDATE、DELETE
table_name 触发操作时间的表名; 
trigger_order可选参数,如果定义了多个具有相同触发事件和触法时间的触发器时(
如:BEFORE UPDATE),默认触发顺序与触发器的创建顺序一致,可以
使用此参数来改变它们触发顺序。mysql 5.7.2起开始支持此参数。
  FOLLOWS:当前创建触发器在现有触发器之后激活;
  PRECEDES:当前创建触发器在现有触发器之前激活;
FOLLOWS、PRECEDES
trigger_body触发执行的SQL语句内容,一般以begin开头,end结尾begin .. end
 

3、触发执行语句内容(trigger_body)中的OLD,NEW:

  在trigger_body中,我们可以使用NEW表示将要插入的新行(相当于MS SQL的INSERTED),OLD表示将要删除的旧行(相当于MS SQL的DELETED)。通过OLD,NEW中获取它们的字段内容,方便在触发操作中使用,下面是对应事件是否支持OLD、NEW的对应关系:
事件OLDNEW
INSERT×
DELETE×
UPDATE
  由于UPDATE相当于删除旧行(OLD),然后插入新行(NEW),所以UPDATE同时支持OLD、NEW;
 

4、MySQL分隔符(DELIMITER):

  MySQL默认使用“;”作为分隔符,SQL语句遇到“;”就会提交。而我们的触发器中可能会有多个“;”符,为了防止触发器创建语句过早的提交,我们需要临时修改MySQL分隔符,创建完后,再将分隔符改回来。使用DELIMITER可以修改分隔符,如下:
DELIMITER $
... --触发器创建语句;
$ --提交创建语句;
DELIMITER ;
 

二、MySQL触发器创建进阶:

1、MySQL触发器中使用变量:

  MySQL触发器中变量变量前面加'@',无需定义,可以直接使用:
-- 变量直接赋值
set @num=999;

-- 使用select语句查询出来的数据方式赋值,需要加括号:
set @name =(select name from table);

2、MySQL触发器中使用if语做条件判断:

-- 简单的if语句:
set sex = if (new.sex=1, '男', '女');

-- 多条件if语句:
if old.type=1 then
update table ...;
elseif old.type=2 then
update table ...;
end if;
 

三、MySQL查看触发器:

  可以使用“show triggers;”查看触发器。由于MySQL创建的触发器保存在“information_schema库中的triggers表中,所以还可以通过查询此表查看触发器:
-- 通过information_schema.triggers表查看触发器:
select * from information_schema.triggers;

-- mysql 查看当前数据库的触发器
show triggers;

-- mysql 查看指定数据库"aiezu"的触发器
show triggers from aiezu;
 

四、MySQL删除触发器:

1、可以使用drop trigger删除触发器:

drop trigger trigger_name;
 

2、删除前先判断触发器是否存在:

drop trigger if exists trigger_name
 

五、Msql触发器用法举例:

1、MySQL触发器Insert触发更新同一张表:

  下面我们有一个表“tmp1”,tmp1表有两个整型字段:n1、n2。我们要通过触发器实现,在tmp插入记录时,自动将n2字段的值设置为n1字段的5倍。
 创建测试表和触发器:
-- 创建测试表
drop table if exists tmp1;
create table tmp1 (n1 int, n2 int);

-- 创建触发器
DELIMITER $
drop trigger if exists tmp1_insert$
create trigger tmp1_insert
before insert on tmp1
for each row
begin
set new.n2 = new.n1*5;
end$
DELIMITER ;
测试触发更新效果:
mysql> insert tmp1(n1) values(18);
Query OK, 1 row affected (0.01 sec)

mysql> insert tmp1(n1) values(99);
Query OK, 1 row affected (0.00 sec)

mysql> select * from tmp1;
+------+------+
| n1 | n2 |
+------+------+
| 18 | 90 |
| 99 | 495 |
+------+------+
2 rows in set (0.00 sec)
 

2、MySQL触发器Update触发更新另一张表:

  下面有有两个表tmp1、tmp2,两个表都有一个相同的字段name。使用触发器实现更新一个表的name时,将另外一个表的name也更新。
 创建测试表和触发器:
-- 创建测试表和插入测试数据
drop table if exists tmp1;
drop table if exists tmp2;
create table tmp1 (id int, name varchar(128)) default charset='utf8';
create table tmp2 (fid int, name varchar(128)) default charset='utf8';
insert into tmp1 values(1, '爱E族');
insert into tmp2 values(1, '爱E族');

-- 创建触发器
DELIMITER $
drop trigger if exists tmp1_update$
create trigger tmp1_update
after update on tmp1
for each row
begin
update tmp2 set name=new.name where fid=new.id;
end$
DELIMITER ;
测试触发更新效果:
mysql> select * from tmp1;
+------+---------+
| id | name |
+------+---------+
| 1 | 爱E族 |
+------+---------+
1 row in set (0.00 sec)

mysql> select * from tmp2;
+------+---------+
| fid | name |
+------+---------+
| 1 | 爱E族 |
+------+---------+
1 row in set (0.00 sec)

mysql> update tmp1 set name='aiezu.com' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from tmp1;
+------+-----------+
| id | name |
+------+-----------+
| 1 | aiezu.com |
+------+-----------+
1 row in set (0.00 sec)

mysql> select * from tmp2;
+------+-----------+
| fid | name |
+------+-----------+
| 1 | aiezu.com |
+------+-----------+
1 row in set (0.00 sec)

Linux git服务端搭建和公钥免密码登录

Linuxliuliangsong 发表了文章 • 0 个评论 • 208 次浏览 • 2016-12-16 15:54 • 来自相关话题

一、实验环境:git服务端:操作系统:Red Hat Enterprise Linux 7.1 64位服务端ip:10.0.0.101git软件:git version 1.8.3.1(yum方式安装)git客户端:操作系统:Windows 10 64位git软件: 二、Linux git服务端安装:1、安装git命令:  为了方便和稳定性,能使用yum安装的我们尽量使用yum安装,没必要去编译安装:yum -y install git 2、添加用户git用于访问数据仓库:  这里我们创建一个"/storage"目录,用来存储数据。添加一个用户git,git的用户目录为“/storage/gitroot”,同时以后此目录也是我们的数据仓库根目录,以后所有的数据仓库存放在此目录下。将创建的git用户的shell设置为 “/usr/bin/git-shell”的目的是禁止git用户通过shell登录,保证安全:mkdir -p /storage
useradd -d /storage/gitroot/ -s /usr/bin/git-shell git
# 设一个密码
passwd git 3、创建第一个数据仓库aiezu.com:  这里我们创建第一个数据仓库“aiezu.com”,用来存放我们正在开发的网站aiezu.com。同时设置权限为归git用户和组所有:cd /storage/gitroot
git init --bare aiezu.com
chown -R git.git /storage/gitroot/
 4、git服务端安装完成:
  按上面配置好后,一个数据仓库已经配置好了,数据仓库的URL为:"git@10.0.0.101:aiezu.com",用户名名为"git",密码为上面创建的“git”用户的密码。
 三、git免密码登录:  上面在Linux配置好了git服务端,但是是需要密码登录的。如果希望实现免密码登录,需要在客户机的用户目录下".ssh"文件夹生成一对"公钥/私钥",并将"公钥"内容添加到git服务端的git用户根目录下的".ssh/authorized_keys"文件中,这里是“/storage/gitroot/.ssh/authorized_keys”文件,这样就可以通过“公钥/私钥”认证,达到免密码登录的目的。
  简单的说"B服务器"的“git”用户目录下“.ssh/authorized_keys”文件中如果有公钥,与"A主机"当前登录用户的根目录下私钥文件“.ssh/id_dsa”是成对关系,那么B就能以“git”用户无需密码登录到A。
 1、git免密码登录windows客户端配置:  windows的git客户端,在安装目录下,一般有"git-bash.exe"文件,可以用来生成"公钥/私钥"对。打开“git-bash.exe”文件,进入bash命令行。运行下面命令,生成"公钥/私钥"对:mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa
ls  直接回车就可以看到在目录下生成了两个文件:id_rsa(私钥)、id_rsa.pub(公钥)。
 2、git免密码登录linux客户端配置:  Linux客户端直接在bash使用"ssh-keygen"命令生成公钥/私钥对即可,如果没有"ssh-keygen"命令,需要安装"openssh"包,运行"yum -y install openssh"即可:mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa
ls  3、git免密码登录服务端配置:  在git服务端,在git用户目录下创建一个“.ssh”目录,并在".ssh"命令下添加一个“authorized_keys”文件。记住,每次使用root账户添加文件后,要将文件的所有者变更为git用户,否则会出现文件权限问题:mkdir /storage/gitroot/.ssh
touch /storage/gitroot/.ssh/authorized_keys
chown -R git.git /storage/gitroot/.ssh/  然后将上面在git客户端生成的公钥"id_rsa.pub"的内容追加到authorized_keys文件中去,authorized_keys文件可以放多台客户机的公钥,一条公钥一行即可。这样,authorized_keys文件中有公钥的客户机,即可免密码登录到服务器了。
 4、免密码登录测试:  按上面方法配置好后, 使用git命令“git clone git@10.0.0.101:aiezu.com aiezu.com”,在客户端成功将git服务端的数据仓库克隆到本地的aiezu.com目录: 查看全部

一、实验环境:

git服务端:

  • 操作系统:Red Hat Enterprise Linux 7.1 64位
  • 服务端ip:10.0.0.101
  • git软件:git version 1.8.3.1(yum方式安装)

git客户端:

  • 操作系统:Windows 10 64位
  • git软件:
 

二、Linux git服务端安装:

1、安装git命令:

  为了方便和稳定性,能使用yum安装的我们尽量使用yum安装,没必要去编译安装:
yum -y install git

 

2、添加用户git用于访问数据仓库:

  这里我们创建一个"/storage"目录,用来存储数据。添加一个用户git,git的用户目录为“/storage/gitroot”,同时以后此目录也是我们的数据仓库根目录,以后所有的数据仓库存放在此目录下。将创建的git用户的shell设置为 “/usr/bin/git-shell”的目的是禁止git用户通过shell登录,保证安全:
mkdir -p /storage
useradd -d /storage/gitroot/ -s /usr/bin/git-shell git
# 设一个密码
passwd git

 

3、创建第一个数据仓库aiezu.com:

  这里我们创建第一个数据仓库“aiezu.com”,用来存放我们正在开发的网站aiezu.com。同时设置权限为归git用户和组所有:
cd /storage/gitroot
git init --bare aiezu.com
chown -R git.git /storage/gitroot/

 4、git服务端安装完成:
  按上面配置好后,一个数据仓库已经配置好了,数据仓库的URL为:"git@10.0.0.101:aiezu.com",用户名名为"git",密码为上面创建的“git”用户的密码。
 

三、git免密码登录:

  上面在Linux配置好了git服务端,但是是需要密码登录的。如果希望实现免密码登录,需要在客户机的用户目录下".ssh"文件夹生成一对"公钥/私钥",并将"公钥"内容添加到git服务端的git用户根目录下的".ssh/authorized_keys"文件中,这里是“/storage/gitroot/.ssh/authorized_keys”文件,这样就可以通过“公钥/私钥”认证,达到免密码登录的目的。
  简单的说"B服务器"的“git”用户目录下“.ssh/authorized_keys”文件中如果有公钥,与"A主机"当前登录用户的根目录下私钥文件“.ssh/id_dsa”是成对关系,那么B就能以“git”用户无需密码登录到A。
 

1、git免密码登录windows客户端配置:

  windows的git客户端,在安装目录下,一般有"git-bash.exe"文件,可以用来生成"公钥/私钥"对。打开“git-bash.exe”文件,进入bash命令行。运行下面命令,生成"公钥/私钥"对:
mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa
ls
  直接回车就可以看到在目录下生成了两个文件:id_rsa(私钥)、id_rsa.pub(公钥)。
 

2、git免密码登录linux客户端配置:

  Linux客户端直接在bash使用"ssh-keygen"命令生成公钥/私钥对即可,如果没有"ssh-keygen"命令,需要安装"openssh"包,运行"yum -y install openssh"即可:
mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa
ls
  

3、git免密码登录服务端配置:

  在git服务端,在git用户目录下创建一个“.ssh”目录,并在".ssh"命令下添加一个“authorized_keys”文件。记住,每次使用root账户添加文件后,要将文件的所有者变更为git用户,否则会出现文件权限问题:
mkdir /storage/gitroot/.ssh
touch /storage/gitroot/.ssh/authorized_keys
chown -R git.git /storage/gitroot/.ssh/
  然后将上面在git客户端生成的公钥"id_rsa.pub"的内容追加到authorized_keys文件中去,authorized_keys文件可以放多台客户机的公钥,一条公钥一行即可。这样,authorized_keys文件中有公钥的客户机,即可免密码登录到服务器了。
 

4、免密码登录测试:

  按上面方法配置好后, 使用git命令“git clone git@10.0.0.101:aiezu.com aiezu.com”,在客户端成功将git服务端的数据仓库克隆到本地的aiezu.com目录:
linux_git_server_nopasswdlogin.png

vim go语言IDE环境Tagbar插件和NERDTree插件安装

Linuxliuliangsong 发表了文章 • 0 个评论 • 481 次浏览 • 2016-12-14 15:49 • 来自相关话题

  Tagbar插件和NERDTree插件是vim搭建go语言IDE环境必不可少的两个插件。下面是vim编辑go语言源码时,启动Tagbar插件和NERDTree插件后的效果图:


 
 一、安装前准备:  在执行本文的vim安装Tagbar和NERDTree前,需要先安装好go语言环境、以及安装好vim的插件管理器Vundle插件和go语言基础插件“vim-go”,这些操作都在另外一篇文章《Vim go语言基础IDE开发环境安装(Vundle/vim-go)》里面介绍,这里就不再做介绍,下面介绍Tagbar和NERDTree插件的安装。
 二、安装vim Tagbar插件:  Tagbar插件能在侧边栏展示当前go源码中的所有定义的常量、变量、函数等,并能从侧边栏快速跳转到它们的定义处 。安装好后可以使用":help Tagbar"命令获得使用帮助,也可以去官网查看相关文档:http://majutsushi.github.io/tagbar/。
 1、安装gotags:  gotags是go语言编写的,直接使用go get命令安装:go get -u github.com/jstemmer/gotags 2、安装ctags:yum -y install ctags 3、安装vim Tagbar插件:  在"~/.vimrc"插件填写处添加下面几行,保存后重新启动vim(这里我们将Tagbar插件的启动命令注册为“F8键”)。再在命令模式运行“:PluginInstall”命令安装插件,直到"Tagbar"插件安装完成:Plugin 'Tagbar'
" 设置tagbar的窗口宽度
let g:tagbar_width=30
" 映射Tagbar的快捷键,按F8自动打开
map <F8> :TagbarToggle<CR> 4、Tagbar插件启动测试:  安装好Tagbar插件后,用vim打开go文件,输入“:Tagbar”或者按"F8"就可以打开Tagbar。可以使用"CTRL+W+方向键"实现窗口切换,可以使用“Ctrl + ]”跳至函数定义处,然后再使用“Ctrl + t”返回。下面是Tagbar的常用的快捷键和命令:命令描述F1关闭/显示帮助信息;q关闭Tagbar窗口;x放大/缩小Tagbar窗口;s项目排序切换;+, <kPlus>, zo展开折叠;-, <kMinus>, zc收起折叠;o, za折叠收起\展开切换;*, <kMultiply>, zR展开所有折叠;=, zM收起所有折叠;回车转到标签定义处,并且光标跳转到标签定义处;p转到标签定义处,但光标停留在Tagbar的窗口;P在预览窗口显示标签;<C-N>跳到下一个上级标签;<C-P>跳到上一个上级标签;<Space>显示标签定义;
 三、vim安装NERDTree插件:  NERDTree插件能以树型结构在侧边栏展示当前工作目录下所有文件夹和源文件,并支持快速打开指定的源文件。安装好后可以使用":help NERDTree"命令获得使用帮助,也可以去官网查看相关文档:https://github.com/scrooloose/nerdtree​。
 1、安装NERDTree插件:  在"~/.vimrc"插件填写处的继续添加下面两行,保存并重新启动vim(这里我们将NERDTree插件的启动命令注册为“F7键”)。然后再在命令模式运行“:PluginInstall”命令安装插件,直到"NERDTree"插件安装完成:Plugin 'scrooloose/nerdtree'
map <F7> :NERDTreeToggle<CR> 2、NERDTree插件启动测试:  安装好Tagbar插件后,用vim打开go文件,输入“:NERDTree”或者按"F7键"就可以启动NERDTree。下面是NERDTree的常用的快捷键和命令: 命令描述 ?显示帮助信息;打



树F7打开或关闭NERDTree,此键可以根据自己需求定义。:NERDTreeToggle同F7:NERDTree打开NERDTree:NERDTreeClose关闭NERDTreeq关闭这个NERDTree窗口;A调整NERDTree窗口大小(最大化、最小化)书
签:Bookmark <name>将目录树保存为指定名字的书签;:NERDTreeFromBookmark <name>打开指定书签名的目录树;:ClearBookmarks <name>删除指定书签;:ClearAllBookmarks删除所有书签;过
滤I大写"i",显示或者隐藏"."号开头的文件和目录;f切换是否使用文件筛选;F切换是否显示文件;B切换是否显示书签表;根

录:NERDTreeCWD将CWD设为目录树根目录;CD将CWD目录作为目录树的根目录;C将当前目录作为目录树的根目录;u将当前根目录的上级目录作为根目录;U同u;cd将选定的目录作为CWD目录;刷
新R刷新当前根目录r刷新当前目录 目
录o打开\关闭文件或目录,打开文件时光标会跳到编辑区域;O递归打开当前目录下的所有目录x关闭父目录;X关闭所有子目录;定
位:NERDTreeFind在目录树中定位到当前打开的文件;P跳转到根目录p跳转到上层目录K到同级第一个节点J到同级最后一个节点k跳转到上一个节点;j跳转到下一个节点;<C-K>跳转到同级上一个节点;<C-J>跳转到同级下一个节点;文
件m显示文件系统管理菜单
(添加、删除、移动、复制、现在属性等操作)o打开\关闭文件或目录,打开文件时光标会跳到编辑区域;go打开目录树光标所在文件,但光标依然保留在目录树中;i在水平分割割的窗口中打开选定文件;gi同i,但光标依然保留在目录树中;s在垂直分割割的窗口中打开选定文件;gs同s,但光标依然保留在目录树中;t在新标签页中打开文件,并使新的标签到focus状态;T在新标签页中打开文件,保留原标签的focus状态; 查看全部
  Tagbar插件和NERDTree插件是vim搭建go语言IDE环境必不可少的两个插件。下面是vim编辑go语言源码时,启动Tagbar插件和NERDTree插件后的效果图:
linux_vim_golang_tagbar_nerdtree.png
 
 

一、安装前准备:

  在执行本文的vim安装Tagbar和NERDTree前,需要先安装好go语言环境、以及安装好vim的插件管理器Vundle插件和go语言基础插件“vim-go”,这些操作都在另外一篇文章《Vim go语言基础IDE开发环境安装(Vundle/vim-go)》里面介绍,这里就不再做介绍,下面介绍Tagbar和NERDTree插件的安装。
 

二、安装vim Tagbar插件:

  Tagbar插件能在侧边栏展示当前go源码中的所有定义的常量、变量、函数等,并能从侧边栏快速跳转到它们的定义处 。安装好后可以使用":help Tagbar"命令获得使用帮助,也可以去官网查看相关文档:http://majutsushi.github.io/tagbar/
 

1、安装gotags:

  gotags是go语言编写的,直接使用go get命令安装:
go get -u github.com/jstemmer/gotags
 

2、安装ctags:

yum -y install ctags
 

3、安装vim Tagbar插件:

  在"~/.vimrc"插件填写处添加下面几行,保存后重新启动vim(这里我们将Tagbar插件的启动命令注册为“F8键”)。再在命令模式运行“:PluginInstall”命令安装插件,直到"Tagbar"插件安装完成:
Plugin 'Tagbar'
" 设置tagbar的窗口宽度
let g:tagbar_width=30
" 映射Tagbar的快捷键,按F8自动打开
map <F8> :TagbarToggle<CR>
 

4、Tagbar插件启动测试:

  安装好Tagbar插件后,用vim打开go文件,输入“:Tagbar”或者按"F8"就可以打开Tagbar。可以使用"CTRL+W+方向键"实现窗口切换,可以使用“Ctrl + ]”跳至函数定义处,然后再使用“Ctrl + t”返回。下面是Tagbar的常用的快捷键和命令:
命令描述
F1关闭/显示帮助信息;
q关闭Tagbar窗口;
x放大/缩小Tagbar窗口;
s项目排序切换;
+, <kPlus>, zo展开折叠;
-, <kMinus>, zc收起折叠;
o, za折叠收起\展开切换;
*, <kMultiply>, zR展开所有折叠;
=, zM收起所有折叠;
回车转到标签定义处,并且光标跳转到标签定义处;
p转到标签定义处,但光标停留在Tagbar的窗口;
P在预览窗口显示标签;
<C-N>跳到下一个上级标签;
<C-P>跳到上一个上级标签;
<Space>显示标签定义;

 

三、vim安装NERDTree插件:

  NERDTree插件能以树型结构在侧边栏展示当前工作目录下所有文件夹和源文件,并支持快速打开指定的源文件。安装好后可以使用":help NERDTree"命令获得使用帮助,也可以去官网查看相关文档:https://github.com/scrooloose/nerdtree​。
 

1、安装NERDTree插件:

  在"~/.vimrc"插件填写处的继续添加下面两行,保存并重新启动vim(这里我们将NERDTree插件的启动命令注册为“F7键”)。然后再在命令模式运行“:PluginInstall”命令安装插件,直到"NERDTree"插件安装完成:
Plugin 'scrooloose/nerdtree'
map <F7> :NERDTreeToggle<CR>
 

2、NERDTree插件启动测试:

  安装好Tagbar插件后,用vim打开go文件,输入“:NERDTree”或者按"F7键"就可以启动NERDTree。下面是NERDTree的常用的快捷键和命令:
 命令描述
 ?显示帮助信息;




F7打开或关闭NERDTree,此键可以根据自己需求定义。
:NERDTreeToggle同F7
:NERDTree打开NERDTree
:NERDTreeClose关闭NERDTree
q关闭这个NERDTree窗口;
A调整NERDTree窗口大小(最大化、最小化)

:Bookmark <name>将目录树保存为指定名字的书签;
:NERDTreeFromBookmark <name>打开指定书签名的目录树;
:ClearBookmarks <name>删除指定书签;
:ClearAllBookmarks删除所有书签;

I大写"i",显示或者隐藏"."号开头的文件和目录;
f切换是否使用文件筛选;
F切换是否显示文件;
B切换是否显示书签表;


:NERDTreeCWD将CWD设为目录树根目录;
CD将CWD目录作为目录树的根目录;
C将当前目录作为目录树的根目录;
u将当前根目录的上级目录作为根目录;
U同u;
cd将选定的目录作为CWD目录;

R刷新当前根目录
r刷新当前目录 

o打开\关闭文件或目录,打开文件时光标会跳到编辑区域;
O递归打开当前目录下的所有目录
x关闭父目录;
X关闭所有子目录;

:NERDTreeFind在目录树中定位到当前打开的文件;
P跳转到根目录
p跳转到上层目录
K到同级第一个节点
J到同级最后一个节点
k跳转到上一个节点;
j跳转到下一个节点;
<C-K>跳转到同级上一个节点;
<C-J>跳转到同级下一个节点;

m显示文件系统管理菜单
(添加、删除、移动、复制、现在属性等操作)
o打开\关闭文件或目录,打开文件时光标会跳到编辑区域;
go打开目录树光标所在文件,但光标依然保留在目录树中;
i在水平分割割的窗口中打开选定文件;
gi同i,但光标依然保留在目录树中;
s在垂直分割割的窗口中打开选定文件;
gs同s,但光标依然保留在目录树中;
t在新标签页中打开文件,并使新的标签到focus状态;
T在新标签页中打开文件,保留原标签的focus状态;

CentOS编译安装Vim让其支持lua

CentOSliuliangsong 发表了文章 • 0 个评论 • 240 次浏览 • 2016-12-14 10:23 • 来自相关话题

  vim有很多插件依赖于lua功能,但是默认系统自带的vim和使用yum安装的vim都不带lua功能,所以我们需要自己编译安装。要查看vim支不支持lua,打开vim,在命令模式输入“:version”即可以看到vim的一些版本和编译信息,其中"+lua"表示支持lua,"-lua"表示不支持lua。


 
 一、卸载自带的vim:  为了避免冲突,首先我们要卸载掉系统自带的vim,使用"yum -y remove vim-*"命令卸载:yum -y remove vim-* 二、安装vim相关软件:1、安装软件编译工具包集:yum -y groupinstall 'Development tools' 2、安装编译vim必须的依赖包:yum -y install ncurses ncurses-devel
yum -y install lua lua-devel 三、编译安装vim:  既然是编译安装,当然是安装最新版的vim,所以我们这次编译安装的是vim8.0。最新版本的vim可以从vim的官网上查找下载地址:http://www.vim.org/
 1、下载并解压vim安装包:cd /usr/local/src
wget ftp://ftp.vim.org/pub/vim/unix/vim-8.0.tar.bz2
tar -xjf vim-8.0.tar.bz2
cd vim80/ 2、编译安装支持lua的vim:./configure --prefix=/usr --with-features=huge --enable-rubyinterp --enable-pythoninterp --enable-luainterp --with-lua-prefix=/usr
make && make install 3、卸载编译安装的vim:  这一步现在不要执行,只是讲编译安装的卸载方法,用来将来的卸载;要卸载编译安装的vim,必须将vim的编译目录“/usr/local/src/vim80/”保留,在编译目录执行卸载操作:[root@aiezu vim80]# cd /usr/local/src/vim80/
[root@aiezu vim80]# make uninstall
[root@aiezu vim80]# make clean 四、安装完成:  安装完成后,使用"vim --version"命令可以看出,已经支持lua啦,效果如下图,开始安装你想要的插件吧。




  查看全部
  vim有很多插件依赖于lua功能,但是默认系统自带的vim和使用yum安装的vim都不带lua功能,所以我们需要自己编译安装。要查看vim支不支持lua,打开vim,在命令模式输入“:version”即可以看到vim的一些版本和编译信息,其中"+lua"表示支持lua,"-lua"表示不支持lua。
linux_vim_makeinstall_enable_lua.png
 
 

一、卸载自带的vim:

  为了避免冲突,首先我们要卸载掉系统自带的vim,使用"yum -y remove vim-*"命令卸载:
yum -y remove vim-*
 

二、安装vim相关软件:

1、安装软件编译工具包集:

yum -y groupinstall 'Development tools'
 

2、安装编译vim必须的依赖包:

yum -y install ncurses ncurses-devel
yum -y install lua lua-devel
 

三、编译安装vim:

  既然是编译安装,当然是安装最新版的vim,所以我们这次编译安装的是vim8.0。最新版本的vim可以从vim的官网上查找下载地址:http://www.vim.org/
 

1、下载并解压vim安装包:

cd /usr/local/src
wget ftp://ftp.vim.org/pub/vim/unix/vim-8.0.tar.bz2
tar -xjf vim-8.0.tar.bz2
cd vim80/
 

2、编译安装支持lua的vim:

./configure --prefix=/usr --with-features=huge --enable-rubyinterp --enable-pythoninterp --enable-luainterp --with-lua-prefix=/usr
make && make install
 

3、卸载编译安装的vim:

  这一步现在不要执行,只是讲编译安装的卸载方法,用来将来的卸载;要卸载编译安装的vim,必须将vim的编译目录“/usr/local/src/vim80/”保留,在编译目录执行卸载操作:
[root@aiezu vim80]# cd /usr/local/src/vim80/
[root@aiezu vim80]# make uninstall
[root@aiezu vim80]# make clean
 

四、安装完成:

  安装完成后,使用"vim --version"命令可以看出,已经支持lua啦,效果如下图,开始安装你想要的插件吧。
linux_vim_makeinstall_enable_lua_02.png


 

Linux vim为Golang(go语言)添加语法高亮

GoLangliuliangsong 发表了文章 • 0 个评论 • 186 次浏览 • 2016-12-08 12:42 • 来自相关话题

  Go语言是谷歌2009发布的一款开源跨平台编程语言,目前vim默认还没有为golang提供语法高亮功能,要实现go语言vim下语法高亮,可以参考下面设置。  一、测试环境:  我的测试环境为RHEL7.1,本文适用与CentOS、Ubuntu、Fedora,同样也适用于其他大多数linux系统: [root@aiezu.com ~]# cat /etc/redhat-release
Derived from Red Hat Enterprise Linux 7.1 (Source)
[root@aiezu.com ~]# uname -a
Linux aiezu.com 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 二、为vim添加go语言用法高亮:  打开https://golang.org/dl/页面,在上面找到并下载“go1.3.3.src.tar.gz ”源码包,然后解压:wget https://storage.googleapis.com/golang/go1.3.3.src.tar.gz
tar -zxvf go1.3.3.src.tar.gz  然后将解压出来的“go/misc/vim/”目录下的syntax、ftplugin、indent、compiler、ftdetect五个文件夹复制到"/usr/share/vim/vim74/"目录下:cp -R go/misc/vim/syntax/ /usr/share/vim/vim74/
cp -R go/misc/vim/ftplugin/ /usr/share/vim/vim74/
cp -R go/misc/vim/indent/ /usr/share/vim/vim74/
cp -R go/misc/vim/compiler/ /usr/share/vim/vim74/
cp -R go/misc/vim/ftdetect/ /usr/share/vim/vim74/ 三、测试:  文件全部复制好后,创建一个后缀为".go"的文件,输入golang代码试试,看vim golang语法高亮是否生效,我的效果如下:



 四、附录:  可以在用户目录下,添加一个".vimrc"文件,在文件添加一些vim初始设置项,来设置是否现在行号、tab字符宽度、文本缩进等。".vimrc"文件每次使用vim命令时都会自动调用。运行"vim ~/.vimrc"命令,然后添加如下内容:syntax on
"set number " 显示行号
"set cursorline " 突出显示当前行
set tabstop=4 " Tab键的宽度

" 统一缩进为4
set softtabstop=4
set shiftwidth=4 查看全部

  Go语言是谷歌2009发布的一款开源跨平台编程语言,目前vim默认还没有为golang提供语法高亮功能,要实现go语言vim下语法高亮,可以参考下面设置。

  

一、测试环境:

  我的测试环境为RHEL7.1,本文适用与CentOS、Ubuntu、Fedora,同样也适用于其他大多数linux系统: 
[root@aiezu.com ~]# cat /etc/redhat-release 
Derived from Red Hat Enterprise Linux 7.1 (Source)
[root@aiezu.com ~]# uname -a
Linux aiezu.com 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
 

二、为vim添加go语言用法高亮:

  打开https://golang.org/dl/页面,在上面找到并下载“go1.3.3.src.tar.gz ”源码包,然后解压:
wget https://storage.googleapis.com/golang/go1.3.3.src.tar.gz 
tar -zxvf go1.3.3.src.tar.gz
  然后将解压出来的“go/misc/vim/”目录下的syntax、ftplugin、indent、compiler、ftdetect五个文件夹复制到"/usr/share/vim/vim74/"目录下:
cp -R go/misc/vim/syntax/ /usr/share/vim/vim74/ 
cp -R go/misc/vim/ftplugin/ /usr/share/vim/vim74/
cp -R go/misc/vim/indent/ /usr/share/vim/vim74/
cp -R go/misc/vim/compiler/ /usr/share/vim/vim74/
cp -R go/misc/vim/ftdetect/ /usr/share/vim/vim74/
 

三、测试:

  文件全部复制好后,创建一个后缀为".go"的文件,输入golang代码试试,看vim golang语法高亮是否生效,我的效果如下:
vim_golang_syntax_highlight.png

 

四、附录:

  可以在用户目录下,添加一个".vimrc"文件,在文件添加一些vim初始设置项,来设置是否现在行号、tab字符宽度、文本缩进等。".vimrc"文件每次使用vim命令时都会自动调用。运行"vim ~/.vimrc"命令,然后添加如下内容:
syntax on
"set number " 显示行号
"set cursorline " 突出显示当前行
set tabstop=4 " Tab键的宽度

" 统一缩进为4
set softtabstop=4
set shiftwidth=4

PHP php://input、php://output用法解析

PHPliuliangsong 发表了文章 • 0 个评论 • 244 次浏览 • 2016-12-05 10:28 • 来自相关话题

一、php://output输出流用法:  php://output是php语言中一个只写的数据流,向“php://input”写入的数据将像 print() 和 echo() 一样的方式 写入到输出缓冲区;“php://input”支持CLI(command-line interface,命令行界面)模式和Http模式;
   1、CLI模式通过php://output向终端输出内容:test.php文件:<?php
$output = fopen("php://output", "w");
fwrite($output, "爱E族");
fclose($output);输出:[root@aiezu.com ~]# php test.php
爱E族 2、Http模式通过php://output导出csv文件:  注意,以UTF8编码导出CSV文件,如果文件头未添加BOM头,使用Excel打开会出现乱码。
test.php页面代码:<?php
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="test.csv"');
$output = fopen('php://output','w') or die("Can't open php://output");
//UTF8 csv文件头前需添加BOM,不然会是乱码
fwrite($output, chr(0xEF).chr(0xBB).chr(0xBF));
// 输出标题行
fputcsv($output, array('站点名', '域名', '行业'));
//数据内容
$rows = array(
array('天猫', 'http://tmall.com', '电子商务')
,array('爱E族', 'http://aiezu.com', '互联网技术')
,array('腾讯', 'http://qq.com', '社交网络')
);
foreach($rows as $row) {
fputcsv($output, $row);
}
fclose($output) or die("Can't close php://output");网页打开效果如下:



   二、php://input输入流用法:  php://input是php语言中一个只读的数据流;通过"php://input",可以读取从Http客户端以POST方式提交、请求头“Content-Type”值非"multipart/form-data​"的所有数据;"php://input"一般用来读取POST上来,除已被处理以外的剩余数据。
 1、PHP使用"php://input"接收XML数据:http test.php页面代码:<?php
/**
* xml文档转为数组元素
* @param obj $xmlobject XML文档对象
* @return array
*/
function xmlToArray($xmlobject) {
$data = array();
foreach ((array)$xmlobject as $key => $value) {
$data[$key] = !is_string($value) ? xmlToArray($value) : $value;
}
return $data;
}

if ( strtolower($_SERVER['CONTENT_TYPE']) == 'application/xml' && $content = file_get_contents("php://input") ) {
$xml = simplexml_load_string($content);//转换post数据为simplexml对象
print_r(xmlToArray($xml));
}待提交xml.xml文件内容:<?xml version='1.0' encoding='UTF-8'?>
<root>
<site>
<name>爱E族</name>
<domain>aiezu.com</domain>
</site>
<site>
<name>天猫</name>
<domain>tmall.com</domain>
</site>
</root>通过linux curl命令提交xml.xml:[root@aiezu.com ~]# curl -H "Content-Type: application/xml" --data-binary @xml.xml http://aiezu.com/test.php
Array
(
[site] => Array
(
[0] => Array
(
[name] => 爱E族
[domain] => aiezu.com
)
[1] => Array
(
[name] => 天猫
[domain] => tmall.com
)
)
)  2、PHP使用"php://input"接收JSON数据:  PHP使用"php://input"接收JSON数据,与接收XML数据十分类似,这里不再做介绍,要查看实例请参考:Linux curl命令get/post提交数据、json和文件全攻略 页面的第“六”节。
 3、PHP使用"php://input"接收文件内容:  下面通过代码演示PHP使用"php://input"接收一个png文件,这里只是用于演示,实际运用中还是建议使用Http请求头"Content-Type"值为"multipart/form-data"的表单方式POST。
接收页面"test.php"代码:<?php
if ( preg_match("#^image/(png|jpe?g|gif)$#i", $_SERVER['CONTENT_TYPE'], $match) && $binary = file_get_contents("php://input") ) {
$file = sprintf("/tmp/pic.%s", strtolower($match[1]));
file_put_contents($file, $binary);
echo sprintf("文件大小: %s\n", filesize($file));
echo sprintf("修改时间: %s\n", date("Y-m-d H:i:s", filemtime($file)));
}提交测试:[root@aiezu.com ~]# curl -H "Content-Type: image/png" --data-binary @logo.png http://aiezu.com/test.php
文件大小: 3706
修改时间: 2016-12-05 13:29:08
 三、附录:Linux curl命令详解PHP 输入、输出流合集 查看全部

一、php://output输出流用法:

  php://output是php语言中一个只写的数据流,向“php://input”写入的数据将像 print() 和 echo() 一样的方式 写入到输出缓冲区;“php://input”支持CLI(command-line interface,命令行界面)模式和Http模式;
   

1、CLI模式通过php://output向终端输出内容:

test.php文件:
<?php
$output = fopen("php://output", "w");
fwrite($output, "爱E族");
fclose($output);
输出:
[root@aiezu.com ~]# php test.php 
爱E族
 

2、Http模式通过php://output导出csv文件:

  注意,以UTF8编码导出CSV文件,如果文件头未添加BOM头,使用Excel打开会出现乱码。
test.php页面代码:
<?php
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename="test.csv"');
$output = fopen('php://output','w') or die("Can't open php://output");
//UTF8 csv文件头前需添加BOM,不然会是乱码
fwrite($output, chr(0xEF).chr(0xBB).chr(0xBF));
// 输出标题行
fputcsv($output, array('站点名', '域名', '行业'));
//数据内容
$rows = array(
array('天猫', 'http://tmall.com', '电子商务')
,array('爱E族', 'http://aiezu.com', '互联网技术')
,array('腾讯', 'http://qq.com', '社交网络')
);
foreach($rows as $row) {
fputcsv($output, $row);
}
fclose($output) or die("Can't close php://output");
网页打开效果如下:
php_output流到处CSV文件.png

   

二、php://input输入流用法:

  php://input是php语言中一个只读的数据流;通过"php://input",可以读取从Http客户端以POST方式提交、请求头“Content-Type”值非"multipart/form-data​"的所有数据;"php://input"一般用来读取POST上来,除已被处理以外的剩余数据。
 

1、PHP使用"php://input"接收XML数据:

http test.php页面代码:
<?php
/**
* xml文档转为数组元素
* @param obj $xmlobject XML文档对象
* @return array
*/
function xmlToArray($xmlobject) {
$data = array();
foreach ((array)$xmlobject as $key => $value) {
$data[$key] = !is_string($value) ? xmlToArray($value) : $value;
}
return $data;
}

if ( strtolower($_SERVER['CONTENT_TYPE']) == 'application/xml' && $content = file_get_contents("php://input") ) {
$xml = simplexml_load_string($content);//转换post数据为simplexml对象
print_r(xmlToArray($xml));
}
待提交xml.xml文件内容:
<?xml version='1.0' encoding='UTF-8'?>
<root>
<site>
<name>爱E族</name>
<domain>aiezu.com</domain>
</site>
<site>
<name>天猫</name>
<domain>tmall.com</domain>
</site>
</root>
通过linux curl命令提交xml.xml:
[root@aiezu.com ~]# curl -H "Content-Type: application/xml" --data-binary @xml.xml http://aiezu.com/test.php
Array
(
[site] => Array
(
[0] => Array
(
[name] => 爱E族
[domain] => aiezu.com
)
[1] => Array
(
[name] => 天猫
[domain] => tmall.com
)
)
)
  

2、PHP使用"php://input"接收JSON数据:

  PHP使用"php://input"接收JSON数据,与接收XML数据十分类似,这里不再做介绍,要查看实例请参考:Linux curl命令get/post提交数据、json和文件全攻略 页面的第“”节。
 

3、PHP使用"php://input"接收文件内容:

  下面通过代码演示PHP使用"php://input"接收一个png文件,这里只是用于演示,实际运用中还是建议使用Http请求头"Content-Type"值为"multipart/form-data"的表单方式POST。
接收页面"test.php"代码:
<?php
if ( preg_match("#^image/(png|jpe?g|gif)$#i", $_SERVER['CONTENT_TYPE'], $match) && $binary = file_get_contents("php://input") ) {
$file = sprintf("/tmp/pic.%s", strtolower($match[1]));
file_put_contents($file, $binary);
echo sprintf("文件大小: %s\n", filesize($file));
echo sprintf("修改时间: %s\n", date("Y-m-d H:i:s", filemtime($file)));
}
提交测试:
[root@aiezu.com ~]# curl -H "Content-Type: image/png" --data-binary @logo.png http://aiezu.com/test.php
文件大小: 3706
修改时间: 2016-12-05 13:29:08

 三、附录:

PHP命令行输入输出流STDIN/STDOUT/STDERR用法

PHPliuliangsong 发表了文章 • 0 个评论 • 269 次浏览 • 2016-12-04 21:27 • 来自相关话题

一、PHP STDIN、STDOUT、STDERR简介:  STDIN、STDOUT、STDERR命令输入输出流,用于向控制台(linux shell终端、windows cmd终端)输入、输出内容,它们默认是已经打开的,可以直接对他们进行读写操作;

  它们只能在CLI(command-line interface,命令行界面)模式中使用,在Http模式时,它们是未定义的。而它们的打开副本php://stdin、php://stdout、php://stderr 也无法输出内容到http浏览器,经测试:写入php://stderr的内容将会输入到默认站点的错误日志中,其它两种无任何效果。

STDIN/STDOUT/STDERR简介:原始流流打开副本描述STDINphp://stdin标准输入(standard input),只读,用于从控制台输入内容;STDOUTphp://stdout标准输出(standard output),只写,用于向控制台输出正常信息;STDERRphp://stderr错误输出(standard error),只写,用于向控制台输出错误信息;  官方推荐使用常量 STDIN、 STDOUT 和 STDERR 来代替它们手动打开的副本封装器“php://stdin”、 “php://stdout” 和 “php://stderr”。下面我们代码演示它们的用法。
  二、PHP STDIN用法:  PHP语言中"STDIN"用于从控制台读取内容,遇到此常量或者通过fopen()函数打开“php://stdin”,脚本将会等待用户输入内容,直到用户按下回车键提交。
测试脚本test.php内容:<?php
echo "请输出站点名称: ";
$name = fgets(STDIN);
echo sprintf("输入的站点名称为: %s\n", $name);

$stdin = fopen('php://stdin', 'r');
echo "请输出域名: ";
$domain = fread($stdin, 128); //最多读取128个字符
echo sprintf("输入的域名为: %s\n", $domain);
fclose($stdin);运行结果:[root@aiezu.com ~]# php test.php
请输出站点名称: 爱E族
输入的站点名称为: 爱E族

请输出域名: aiezu.com
输入的域名为: aiezu.com 三、PHP STDOUT用法:  PHP语言中"STDOUT"用于向控制台输出标准信息;向此常量、或者向fopen()函数打开的"php://stdout"写入的内容将直接输出到控制台的标准输出;标准输出的内容可以用过">"或者"1>"重定向到指定地方,比如文件。
测试脚本test.php内容:<?php
fwrite(STDOUT, "我是通过STDOUT写入;\n");

$stdout = fopen("php://stdout", "w");
fwrite($stdout, "我是通过php://stdout写入;");
fclose($stdout);运行结果:[root@aiezu.com ~]# php test.php 1> test.out
[root@aiezu.com ~]# cat test.out
我是通过STDOUT写入;
我是通过php://stdout写入; 四、PHP STDERR用法:  PHP语言中"STDERR"用于向控制台输出错误信息;向此常量、或者向fopen()函数打开的"php://stderr"写入的内容将直接输出到控制台的错误输出;错误输出的内容可以用过"2>"重定向到指定地方,比如文件;也可以使用"2>&1"将错误输出定向到标准输出,与标准输出合并。
测试脚本test.php内容:<?php
fwrite(STDERR, "STDERR写入的错误输出;\n");

fwrite(STDOUT, "STDOUT写入的正常输出;\n");

$stderr = fopen("php://stdout", "w");
fwrite($stderr, "php://stderr写入的错误输出;\n");
fclose($stderr);正常输出和错误输出分别定向到不同文件:[root@aiezu.com ~]# php test.php 1>test.ok 2>test.err
[root@aiezu.com ~]# cat test.ok
STDOUT写入的正常输出;
php://stderr写入的错误输出;
[root@aiezu tmp]# cat test.err
STDERR写入的错误输出;正常输出和错误输出合并定向到统一文件:[root@aiezu.com ~]# php test.php 1>AllInOne 2>&1
[root@aiezu.com ~]# cat AllInOne
STDERR写入的错误输出;
STDOUT写入的正常输出;
php://stderr写入的错误输出;   四、附录:PHP 输入、输出流合集 查看全部

一、PHP STDIN、STDOUT、STDERR简介:

  STDINSTDOUTSTDERR命令输入输出流,用于向控制台(linux shell终端、windows cmd终端)输入、输出内容,它们默认是已经打开的,可以直接对他们进行读写操作;

  它们只能在CLI(command-line interface,命令行界面)模式中使用,在Http模式时,它们是未定义的。而它们的打开副本php://stdinphp://stdoutphp://stderr 也无法输出内容到http浏览器,经测试:写入php://stderr的内容将会输入到默认站点的错误日志中,其它两种无任何效果。

STDIN/STDOUT/STDERR简介:
原始流流打开副本描述
STDINphp://stdin标准输入(standard input),只读,用于从控制台输入内容;
STDOUTphp://stdout标准输出(standard output),只写,用于向控制台输出正常信息
STDERRphp://stderr错误输出(standard error),只写,用于向控制台输出错误信息
  官方推荐使用常量 STDIN、 STDOUT 和 STDERR 来代替它们手动打开的副本封装器“php://stdin”、 “php://stdout” 和 “php://stderr”。下面我们代码演示它们的用法。
  

二、PHP STDIN用法:

  PHP语言中"STDIN"用于从控制台读取内容,遇到此常量或者通过fopen()函数打开“php://stdin”,脚本将会等待用户输入内容,直到用户按下回车键提交。
测试脚本test.php内容:
<?php
echo "请输出站点名称: ";
$name = fgets(STDIN);
echo sprintf("输入的站点名称为: %s\n", $name);

$stdin = fopen('php://stdin', 'r');
echo "请输出域名: ";
$domain = fread($stdin, 128); //最多读取128个字符
echo sprintf("输入的域名为: %s\n", $domain);
fclose($stdin);
运行结果:
[root@aiezu.com ~]# php test.php 
请输出站点名称: 爱E族
输入的站点名称为: 爱E族

请输出域名: aiezu.com
输入的域名为: aiezu.com
 

三、PHP STDOUT用法:

  PHP语言中"STDOUT"用于向控制台输出标准信息;向此常量、或者向fopen()函数打开的"php://stdout"写入的内容将直接输出到控制台的标准输出;标准输出的内容可以用过">"或者"1>"重定向到指定地方,比如文件。
测试脚本test.php内容:
<?php
fwrite(STDOUT, "我是通过STDOUT写入;\n");

$stdout = fopen("php://stdout", "w");
fwrite($stdout, "我是通过php://stdout写入;");
fclose($stdout);
运行结果:
[root@aiezu.com ~]# php test.php 1> test.out
[root@aiezu.com ~]# cat test.out
我是通过STDOUT写入;
我是通过php://stdout写入;
 

四、PHP STDERR用法:

  PHP语言中"STDERR"用于向控制台输出错误信息;向此常量、或者向fopen()函数打开的"php://stderr"写入的内容将直接输出到控制台的错误输出;错误输出的内容可以用过"2>"重定向到指定地方,比如文件;也可以使用"2>&1"将错误输出定向到标准输出,与标准输出合并。
测试脚本test.php内容:
<?php
fwrite(STDERR, "STDERR写入的错误输出;\n");

fwrite(STDOUT, "STDOUT写入的正常输出;\n");

$stderr = fopen("php://stdout", "w");
fwrite($stderr, "php://stderr写入的错误输出;\n");
fclose($stderr);
正常输出和错误输出分别定向到不同文件:
[root@aiezu.com ~]# php test.php 1>test.ok 2>test.err
[root@aiezu.com ~]# cat test.ok
STDOUT写入的正常输出;
php://stderr写入的错误输出;
[root@aiezu tmp]# cat test.err
STDERR写入的错误输出;
正常输出和错误输出合并定向到统一文件:
[root@aiezu.com ~]# php test.php 1>AllInOne 2>&1
[root@aiezu.com ~]# cat AllInOne
STDERR写入的错误输出;
STDOUT写入的正常输出;
php://stderr写入的错误输出;
  

 四、附录:

一次由于磁盘空间满导致的MySQL故障

Mysqlliuliangsong 发表了文章 • 0 个评论 • 175 次浏览 • 2016-12-03 11:45 • 来自相关话题

一、故障描述:
  今天早上起来,发现收到了邮件报警,提示网站500错误。测试服务器上的网站,凡是没使用数据库的页面能正常打开、而使用到数据库的要么500错误,要么一直在加载中。
 
二、追根溯源:
1、“show processlist”:
  看到这种现象,第一个想到的可能是不良SQL语句导致的锁表,才会导致网站页面一直加载中;连接到数据库,发现“use dbname”语句不能用,也就是不能切换数据库;直接运行“show processlist”查看正在运行的SQL语句,发现有好几百条SQL语句未执行完正常退出,选了两条时间最长的:Id User Host db Command Time State Info
4507153 root localhost:35745 ad Execute 1568 query end INSERT INTO `sessions` (`id`, `modified`, `data`, `lifetime`) VALUES (?, ?, ?, ?)
4507157 root localhost:35752 ad Query 1566 Opening tables DESCRIBE `sessions`  可以看出,就是简单的插入操作和显示表结果操作,所以不使用由于SQL语句不良导致的锁表;想“DESCRIBE `sessions`”复原错误现场试试,但是由于切换不了数据库,只能暂时放弃。所以这里找不出问题所在,决定看看MySQL日志看看。

2、mysqld日志:
  打开MySQL日志(日志文件的路径在“/etc/my.cnf”中指定,默认为“/var/log/mysqld.log”),终于在日志文件底部发现了那么几行错误:161203 10:30:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:30:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs
161203 10:40:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:40:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs
161203 10:50:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:50:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs  从上面错误提示可以看出,写“sessions”表索引文件时发现磁盘空间不足;
 
3、清理磁盘空间:
  使用“df -h”命令查看当前磁盘空间的使用情况,发现我的“/storage”挂载点的磁盘空间使用率确实达到了“100%”:[root@aiezu.com ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 20G 11G 7.7G 59% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
/dev/xvdb1 189G 189G 0G 100% /storage  当然,如果即使使用“df -h”命令查看磁盘空间使用率不是“100%”,还得使用“df -i”命令看看磁盘的“inode”使用率,如果“inode”用完,也会导致无法创建文件:[root@aiezu.com ~]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/xvda1 1310720 640191 670529 49% /
tmpfs 490142 1 490141 1% /dev/shm
/dev/xvdb1 26214408 1451980 24762428 6% /storage  发现磁盘空间不足后,删除到一些不重要的上传文件腾出了一半的磁盘空间:[root@aiezu.com ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 20G 11G 7.7G 59% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
/dev/xvdb1 189G 98G 91G 51% /storage 
三、前因后果:
  重启数据库后,发现其他网站正常了,而有一个原来加载很慢的网站,现在刷新直接报500错误了;查看此网站的错误日志,发现依然是提示“sessions”表有错误。
  再次连接到MySQL,切换到站点的数据库,执行“select * from sessions limit 1”语句,提示如下错误:mysql> use ad;
Database changed
mysql> select * from sessions limit 1;
ERROR 145 (HY000): Table './ad/sessions' is marked as crashed and should be repaired  看来是由于磁盘空间满的时候导致表文件损坏,经网上搜索,使用下面命令成功将表修复,问题全部得到解决:[root@aiezu.com ~]# myisamchk -c -r /storage/aiezu.com/mysql/ad/sessions.MYI
- recovering (with sort) MyISAM-table '/storage/aiezu.com/mysql/ad/sessions.MYI'
Data records: 8
- Fixing index 1
- Fixing index 2 
四、总结:
  这次的问题非常严重,导致了好几个站点不能正常访问,极大的影响了用户和搜索引擎对网站的信任;主要是由于未预计到用户的图片上传量,未及时扩充磁盘空间,也没有做磁盘空间监控,确实应该深刻检讨。 查看全部
一、故障描述:
  今天早上起来,发现收到了邮件报警,提示网站500错误。测试服务器上的网站,凡是没使用数据库的页面能正常打开、而使用到数据库的要么500错误,要么一直在加载中。
 
二、追根溯源:
1、“show processlist”:
  看到这种现象,第一个想到的可能是不良SQL语句导致的锁表,才会导致网站页面一直加载中;连接到数据库,发现“use dbname”语句不能用,也就是不能切换数据库;直接运行“show processlist”查看正在运行的SQL语句,发现有好几百条SQL语句未执行完正常退出,选了两条时间最长的:
Id	User	Host	db	Command	Time	State	Info
4507153 root localhost:35745 ad Execute 1568 query end INSERT INTO `sessions` (`id`, `modified`, `data`, `lifetime`) VALUES (?, ?, ?, ?)
4507157 root localhost:35752 ad Query 1566 Opening tables DESCRIBE `sessions`
  可以看出,就是简单的插入操作和显示表结果操作,所以不使用由于SQL语句不良导致的锁表;想“DESCRIBE `sessions`”复原错误现场试试,但是由于切换不了数据库,只能暂时放弃。所以这里找不出问题所在,决定看看MySQL日志看看。

2、mysqld日志:
  打开MySQL日志(日志文件的路径在“/etc/my.cnf”中指定,默认为“/var/log/mysqld.log”),终于在日志文件底部发现了那么几行错误:
161203 10:30:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:30:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs
161203 10:40:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:40:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs
161203 10:50:39 [Warning] Disk is full writing '/storage/aiezu.com/mysql/ad/sessions.MYI' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space)
161203 10:50:39 [Warning] Retry in 60 secs. Message reprinted in 600 secs
  从上面错误提示可以看出,写“sessions”表索引文件时发现磁盘空间不足;
 
3、清理磁盘空间:
  使用“df -h”命令查看当前磁盘空间的使用情况,发现我的“/storage”挂载点的磁盘空间使用率确实达到了“100%”:
[root@aiezu.com ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 20G 11G 7.7G 59% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
/dev/xvdb1 189G 189G 0G 100% /storage
  当然,如果即使使用“df -h”命令查看磁盘空间使用率不是“100%”,还得使用“df -i”命令看看磁盘的“inode”使用率,如果“inode”用完,也会导致无法创建文件:
[root@aiezu.com ~]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/xvda1 1310720 640191 670529 49% /
tmpfs 490142 1 490141 1% /dev/shm
/dev/xvdb1 26214408 1451980 24762428 6% /storage
  发现磁盘空间不足后,删除到一些不重要的上传文件腾出了一半的磁盘空间:
[root@aiezu.com ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 20G 11G 7.7G 59% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
/dev/xvdb1 189G 98G 91G 51% /storage
 
三、前因后果:
  重启数据库后,发现其他网站正常了,而有一个原来加载很慢的网站,现在刷新直接报500错误了;查看此网站的错误日志,发现依然是提示“sessions”表有错误。
  再次连接到MySQL,切换到站点的数据库,执行“select * from sessions limit 1”语句,提示如下错误:
mysql> use ad;
Database changed
mysql> select * from sessions limit 1;
ERROR 145 (HY000): Table './ad/sessions' is marked as crashed and should be repaired
  看来是由于磁盘空间满的时候导致表文件损坏,经网上搜索,使用下面命令成功将表修复,问题全部得到解决:
[root@aiezu.com ~]# myisamchk -c -r /storage/aiezu.com/mysql/ad/sessions.MYI 
- recovering (with sort) MyISAM-table '/storage/aiezu.com/mysql/ad/sessions.MYI'
Data records: 8
- Fixing index 1
- Fixing index 2
 
四、总结:
  这次的问题非常严重,导致了好几个站点不能正常访问,极大的影响了用户和搜索引擎对网站的信任;主要是由于未预计到用户的图片上传量,未及时扩充磁盘空间,也没有做磁盘空间监控,确实应该深刻检讨。

MySQL的selinux配置

Mysqlliuliangsong 发表了文章 • 0 个评论 • 184 次浏览 • 2016-12-01 16:53 • 来自相关话题

一、selinux的概念:
  selinux是Security Enhanced Linux (安全强化 Linux)的简称,selinux涉及到主体(subject,一般指进程)、操作(operation)、对象(object,又称资源,如:文件、网络端口),selinux用于控制“主体”能以何种“操作”方式访问什么“对象”。
  selinux环境中,所有的“主体(进程)”被赋予一个“domain”标签,所有的“对象”也被赋予一个“type”类型标签,“domain”标定的“主体”能访问哪些“type”标定的“对象”,由selinux的安全策略定义。
 
二、selinux环境:
selinux有三种状态,分别是:Enforcing:强制模式,selinux正严格按照策略运行中;Permissive:宽容模式,selinux正在运行中,但是只记录日志,不执行实际限制;Disabled:禁用模式,selinux环境未被开启;
 
  我们可以使用“getenforce”命令查看selinux是否在强制模式中运行:[root@aiezu.com ~]# getenforce
Disabled  如果状态为“Disabled”,我们必须在"/etc/sysconfig/selinux"文件中,将“SELINUX=disabled”改为“SELINUX=enforcing”,并重启服务器后才能开启;
  如果状态为“Permissive”,可以直接使用“setenforce 1”命令临时改为强制状态:[root@aiezu.com ~]# getenforce
Permissive
[root@aiezu.com ~]# setenforce 1
[root@aiezu.com ~]# getenforce
Enforcing  
三、mysql服务中的selinux定义:
1、mysql进程主体的domain标签:
  在selinux环境中,mysqld的“domain”标签为“mysqld_t”、mysqld在受限的“mysqld_t”域中仅能访问指定的资源;

2、selinux环境中mysql能访问的资源标签:标签名用途mysqld_db_t 这种文件类型用于标记MySQL数据库数据文件;
在RHEL/CentOS中数据文件的默认位置是“/var/lib/mysql”;
如果修改了MySQL数据文件的位置,新的位置必须使用这种类型标签;mysqld_etc_t这种文件类型用于标记MySQL的配置文件;
默认位置为:
  主配置文件:“/etc/my.cnf”;
  其他配置文件:“/etc/my.cnf.d/”目录下的文件;mysqld_exec_t 这种文件类型用于标记MySQL主进程文件;
默认为:“/usr/libexec/mysqld”或者“/usr/sbin/mysqld”;
可以通过“whereis mysqld”命令查找;mysqld_initrc_exec_t 这种文件类型用于标记MySQL的初始化脚本;
默认为:“/etc/rc.d/init.d/mysqld”;
“service mysqld start”中“mysqld”用的就是此脚本;mysqld_log_t 这种文件类型用于标记日志文件;mysqld_var_run_t 这种文件类型用于标记MySQL运行时产生的文件;
默认在“/var/run/mysqld”目录中,主要包括:
  /var/run/mysqld/mysqld.pid
  /var/run/mysqld/mysqld.sock 
3、selinux中mysq的布尔变量:变量名含义allow_user_mysql_connect 当这个变量值为“1”时允许用户连接数据库;exim_can_connect_db 当这个变量值为“1”时允许exim邮件程序访问数据库服务器;ftpd_connect_db 当这个变量值为“1”时允许ftpd进程访问数据库服务器;httpd_can_network_connect_db当这个变量值为“1”时允许httpd进程访问数据库服务器; 
四、配置演示:
  下面演示在selinux环境中,由于修改了mysql的数据存放路径,而导致的selinux权限错误、及其修正方法;在测试前,我们得先得确认我们的linux系统处于“selinux”环境,请参考第二步。
  1、首先,我们在修改mysql路径前确定mysql是能正常运行的,并停止mysql服务:[root@aiezu.com ~]# service mysql stop
Shutting down MySQL.. SUCCESS!
[root@aiezu ~]# service mysql start
Starting MySQL. SUCCESS!
[root@aiezu.com ~]# lsof -i:3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 5166 mysql 10u IPv6 22791 0t0 TCP *:mysql (LISTEN)
[root@aiezu.com ~]# service mysql stop
Shutting down MySQL.. SUCCESS!
  2、创建新的mysql数据存放路径,并使用“mysql_install_db”初始化目录;我这里只是测试,原数据库的数据不需要保留,如果需要保留,需要想将原数据库备份,修改路径完成正常运行后再恢复:[root@aiezu.com ~]# mkdir -p /storage/db
[root@aiezu.com ~]# mysql_install_db --datadir=/storage/db/
[root@aiezu.com ~]# chown -R mysql:mysql /storage/db/
  3、在mysql的配置文件"/etc/my.cnf"中,在"[mysqld]"组下,将"datadir="的值改成新的路径;修改后我的配置文件内容如下:[mysqld]
datadir=/storage/db/
socket=/storage/db/mysql.sock
pid-file=/storage/db/aiezu.com.pid

[mysqld_safe]
log-error=/var/log/mysqld.log

[client]
socket=/storage/db/mysql.sock  注意“[mysqld]”组和“[client]”组下的“socket=”后面的值要一致,否则运行mysql连接数据的时候会提示如下错误:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
   4、尝试启动MySQL,观察提示的错误(由于我们上面没有为新的mysql目录设置selinux权限,这里一定会报错的):[root@aiezu.com ~]# service mysql start
Starting MySQL. ERROR! The server quit without updating PID file (/storage/db/aiezu.com.pid).  在mysql的日志文件中能看到更详细的错误信息,可以在“my.cnf”中找到日志文件的路径,我这里的路径为“/var/log/mysqld.log”:161201 23:26:55 mysqld_safe Starting mysqld daemon with databases from /storage/db/
2016-12-01 23:26:56 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2016-12-01 23:26:56 0 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 0 [Note] /usr/sbin/mysqld (mysqld 5.6.34) starting as process 6215 ...
2016-12-01 23:26:56 6215 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 6215 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 6215 [Note] Plugin 'FEDERATED' is disabled.
^G/usr/sbin/mysqld: Can't find file: './mysql/plugin.frm' (errno: 13 - Permission denied)
2016-12-01 23:26:56 6215 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
...
2016-12-01 23:26:56 6215 [ERROR] InnoDB: os_file_get_status() failed on './ibdata1'. Can't determine file permissions
2016-12-01 23:26:56 6215 [ERROR] InnoDB: The system tablespace must be writable!
...  可以看到日志文件中出现了“Permission denied”字样,这就是由于selinux权限引起的(上面我们设置了文件系统权限,所以不是文件系统权限引起的);
  5、使用chcon命令为mysql数据目录设置selinux权限,并启动mysql,发现启动成功:[root@aiezu.com ~]# chcon -R -t mysqld_db_t /storage/db/
[root@aiezu.com ~]# service mysql start
Starting MySQL. SUCCESS!
[root@aiezu.com ~]# lsof -i:3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 6590 mysql 10u IPv6 24127 0t0 TCP *:mysql (LISTEN)
[root@aiezu.com ~]# ps -eZ|grep mysqld
unconfined_u:system_r:mysqld_safe_t:s0 6437 ? 00:00:00 mysqld_safe
unconfined_u:system_r:mysqld_t:s0 6590 ? 00:00:00 mysqldchcon命令的用法请参考:http://aiezu.com/article/linux_chcon_command.html 查看全部
一、selinux的概念:
  selinux是Security Enhanced Linux (安全强化 Linux)的简称,selinux涉及到主体(subject,一般指进程)、操作(operation)、对象(object,又称资源,如:文件、网络端口),selinux用于控制“主体”能以何种“操作”方式访问什么“对象”。
  selinux环境中,所有的“主体(进程)”被赋予一个“domain”标签,所有的“对象”也被赋予一个“type”类型标签,“domain”标定的“主体”能访问哪些“type”标定的“对象”,由selinux的安全策略定义。
 
二、selinux环境:
selinux有三种状态,分别是:
  • Enforcing:强制模式,selinux正严格按照策略运行中;
  • Permissive:宽容模式,selinux正在运行中,但是只记录日志,不执行实际限制;
  • Disabled:禁用模式,selinux环境未被开启;

 
  我们可以使用“getenforce”命令查看selinux是否在强制模式中运行:
[root@aiezu.com ~]# getenforce 
Disabled
  如果状态为“Disabled”,我们必须在"/etc/sysconfig/selinux"文件中,将“SELINUX=disabled”改为“SELINUX=enforcing”,并重启服务器后才能开启;
  如果状态为“Permissive”,可以直接使用“setenforce 1”命令临时改为强制状态:
[root@aiezu.com ~]# getenforce 
Permissive
[root@aiezu.com ~]# setenforce 1
[root@aiezu.com ~]# getenforce
Enforcing
  
三、mysql服务中的selinux定义:
1、mysql进程主体的domain标签:
  在selinux环境中,mysqld的“domain”标签为“mysqld_t”、mysqld在受限的“mysqld_t”域中仅能访问指定的资源;

2、selinux环境中mysql能访问的资源标签:
标签名用途
mysqld_db_t 这种文件类型用于标记MySQL数据库数据文件;
在RHEL/CentOS中数据文件的默认位置是“/var/lib/mysql”;
如果修改了MySQL数据文件的位置,新的位置必须使用这种类型标签;
mysqld_etc_t这种文件类型用于标记MySQL的配置文件;
默认位置为:
  主配置文件:“/etc/my.cnf”;
  其他配置文件:“/etc/my.cnf.d/”目录下的文件;
mysqld_exec_t 这种文件类型用于标记MySQL主进程文件;
默认为:“/usr/libexec/mysqld”或者“/usr/sbin/mysqld”;
可以通过“whereis mysqld”命令查找;
mysqld_initrc_exec_t 这种文件类型用于标记MySQL的初始化脚本;
默认为:“/etc/rc.d/init.d/mysqld”;
“service mysqld start”中“mysqld”用的就是此脚本;
mysqld_log_t 这种文件类型用于标记日志文件;
mysqld_var_run_t 这种文件类型用于标记MySQL运行时产生的文件;
默认在“/var/run/mysqld”目录中,主要包括:
  /var/run/mysqld/mysqld.pid
  /var/run/mysqld/mysqld.sock
 
3、selinux中mysq的布尔变量:
变量名含义
allow_user_mysql_connect 当这个变量值为“1”时允许用户连接数据库;
exim_can_connect_db 当这个变量值为“1”时允许exim邮件程序访问数据库服务器;
ftpd_connect_db 当这个变量值为“1”时允许ftpd进程访问数据库服务器;
httpd_can_network_connect_db当这个变量值为“1”时允许httpd进程访问数据库服务器;
 
四、配置演示:
  下面演示在selinux环境中,由于修改了mysql的数据存放路径,而导致的selinux权限错误、及其修正方法;在测试前,我们得先得确认我们的linux系统处于“selinux”环境,请参考第二步。
  1、首先,我们在修改mysql路径前确定mysql是能正常运行的,并停止mysql服务:
[root@aiezu.com ~]# service mysql stop
Shutting down MySQL.. SUCCESS!
[root@aiezu ~]# service mysql start
Starting MySQL. SUCCESS!
[root@aiezu.com ~]# lsof -i:3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 5166 mysql 10u IPv6 22791 0t0 TCP *:mysql (LISTEN)
[root@aiezu.com ~]# service mysql stop
Shutting down MySQL.. SUCCESS!

  2、创建新的mysql数据存放路径,并使用“mysql_install_db”初始化目录;我这里只是测试,原数据库的数据不需要保留,如果需要保留,需要想将原数据库备份,修改路径完成正常运行后再恢复:
[root@aiezu.com ~]# mkdir -p /storage/db
[root@aiezu.com ~]# mysql_install_db --datadir=/storage/db/
[root@aiezu.com ~]# chown -R mysql:mysql /storage/db/

  3、在mysql的配置文件"/etc/my.cnf"中,在"[mysqld]"组下,将"datadir="的值改成新的路径;修改后我的配置文件内容如下:
[mysqld]
datadir=/storage/db/
socket=/storage/db/mysql.sock
pid-file=/storage/db/aiezu.com.pid

[mysqld_safe]
log-error=/var/log/mysqld.log

[client]
socket=/storage/db/mysql.sock
  注意“[mysqld]”组和“[client]”组下的“socket=”后面的值要一致,否则运行mysql连接数据的时候会提示如下错误:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

   4、尝试启动MySQL,观察提示的错误(由于我们上面没有为新的mysql目录设置selinux权限,这里一定会报错的):
[root@aiezu.com ~]# service mysql start
Starting MySQL. ERROR! The server quit without updating PID file (/storage/db/aiezu.com.pid).
  在mysql的日志文件中能看到更详细的错误信息,可以在“my.cnf”中找到日志文件的路径,我这里的路径为“/var/log/mysqld.log”:
161201 23:26:55 mysqld_safe Starting mysqld daemon with databases from /storage/db/
2016-12-01 23:26:56 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2016-12-01 23:26:56 0 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 0 [Note] /usr/sbin/mysqld (mysqld 5.6.34) starting as process 6215 ...
2016-12-01 23:26:56 6215 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 6215 [Warning] Can't create test file /storage/db/aiezu.lower-test
2016-12-01 23:26:56 6215 [Note] Plugin 'FEDERATED' is disabled.
^G/usr/sbin/mysqld: Can't find file: './mysql/plugin.frm' (errno: 13 - Permission denied)
2016-12-01 23:26:56 6215 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
...
2016-12-01 23:26:56 6215 [ERROR] InnoDB: os_file_get_status() failed on './ibdata1'. Can't determine file permissions
2016-12-01 23:26:56 6215 [ERROR] InnoDB: The system tablespace must be writable!
...
  可以看到日志文件中出现了“Permission denied”字样,这就是由于selinux权限引起的(上面我们设置了文件系统权限,所以不是文件系统权限引起的);
  5、使用chcon命令为mysql数据目录设置selinux权限,并启动mysql,发现启动成功:
[root@aiezu.com ~]# chcon -R -t mysqld_db_t /storage/db/
[root@aiezu.com ~]# service mysql start
Starting MySQL. SUCCESS!
[root@aiezu.com ~]# lsof -i:3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 6590 mysql 10u IPv6 24127 0t0 TCP *:mysql (LISTEN)
[root@aiezu.com ~]# ps -eZ|grep mysqld
unconfined_u:system_r:mysqld_safe_t:s0 6437 ? 00:00:00 mysqld_safe
unconfined_u:system_r:mysqld_t:s0 6590 ? 00:00:00 mysqld
chcon命令的用法请参考:http://aiezu.com/article/linux_chcon_command.html