微擎安装教程

mikel阅读(2615)

微信公众平台自助引擎,简称微擎,微擎是一款免费开源的微信公众平台管理系统。

来源: 微擎官方网站 – 微擎是一款免费开源的微信公众平台管理系统

微擎是一款免费开源的微信公众号管理系统,基于目前最流行的WEB2.0架构(php+mySQL),支持在线升级和安装模块及模板,拥有良好的开发框架、成熟稳定的技术解决方案、活跃的第三方开发者及开发团队,依托微擎开放的生态系统,提供丰富的扩展功能。


宝宝还是不会 服务器环境要求:

IIS/Apache/Nginx + PHP>=5.3 + MySQL>=5
下载微擎:

下载离线安装包:http://www.we7.cc/download/WeEngine-Laster-Offline.zip(文件体积较大,上传比较耗时)

下载在线安装包:http://www.we7.cc/download/WeEngine-Laster-Online.zip推荐!文件体积较小,简洁方便,执行后可自动从云服务上下载完整源码)
安装微擎:

安装时请解压下载文件并上传到站点根目录,然后访问http://你的域名/install.php进行安装。
注册云服务:

1,安装微擎后,登录微擎进入后台界面,点击右上角系统菜单:

2,然后你会看到云服务相关的菜单,点击注册站点:

3,进入注册站点之后,填写你的网站名称、论坛账号密码,提交注册就可以了。

宝宝想自己装


如果您的服务器是纯净版的系统,尚未安装环境,可以使用我们为您提供的【微擎+环境一键安装包】

微擎合作方:WDCP是WDlinux Control Panel的简称,是一套通过WEB控制和管理服务器的Linux服务器管理系统以及虚拟主机管理系统。

Linux系统(支持CentOS 6.X/7.X、RedHat 6.X、Ubuntu 12.04):

ssh登录服务器,执行如下操作即可,需root用户身份安装

wget http://dl.wdlinux.cn/files/lanmp_wee_v3.tar.gz

tar zxvf lanmp_wee_v3.tar.gz

sh lanmp.sh
安装说明:

1,安装完后,登录WDCP后台,创建一个站点

2,绑定相应的域名,指定目录到/www/web/we7

3,创建成功后即可直接访问微擎(免安装)。默认帐号:admin 密码:we7.cc


微擎合作方:UPUPW绿色服务器平台是目前Windows下最具特色的免费服务器PHP套件。UPUPW PHP套件简化了PHP环境搭建步骤。适用于VPS,云主机,服务器架设网站,也适用于本地开发和测试PHP程序。绿色,安全,稳定,高速!

Windows系统(支持windows全系列):

下载地址:http://pan.baidu.com/s/1kUYpYQj
安装步骤:

1、解压UPUPW_MicroEngine文件夹到任意不含空格的非中文路径下

2、打开UPUPW面板输入s1回车

3、打开浏览器输入127.0.0.1即可访问htdocs目录下的微擎网站默认帐号:admin 密码:we7.cc

注意事项:

1、运行前请确保电脑上已安装.NET Framework 2.0或3.5支持库

2、服务器部署请先把域名解析到服务器IP,然后外网可以通过域名访问微擎

详细说明:http://php.upupw.net/customize/20/5650-1.html


如果您对服务器运维不够了解,又不懂如何安装微擎,我们向您进行以下推荐

微擎合作方:应用引擎 BAE,自动为您完成运行环境配置、应用部署、均衡负载、资源监控、日志收集等各项工作,大大简化部署运维工作。

安装步骤:

1、购买BAE

2、模板类型选择【微信模板】,模板场景选择【微擎】

3、完善其他信息,购买完成

4、输入访问地址,按照提示安装微擎

详细说明:http://bbs.we7.cc/thread-16412-1-1.html

HTTPS的学习总结 - AscenZ - 博客园

mikel阅读(1584)

来源: HTTPS的学习总结 – AscenZ – 博客园

简述

HTTPS对比HTTP就多了一个安全层SSL/TLS,具体就是验证服务端的证书和对内容进行加密。

先来看看HTTP和HTTPS的区别
我用AFN访问http下的httpbin.org/image/png
然后用Charles抓一下包,可以看到传输的图片
http的数据
然后访问HTTPS下的https://httpbin.org/image/png
再抓包,看到数据是乱码,这就是加密过后的数据
https的数据

关于加密算法

1)对称加密:密钥只有一个,加密解密为同一个密码,且加解密速度快,典型的对称加密算法有DES、AES等;

2)非对称加密:非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如 果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是 两个不同的密钥,所以这种算法叫作非对称加密算法。

过程就像我们用github的时候也是这样,我们电脑这里生成私钥和公钥,公钥上传到github,私钥添加到我们电脑的ssh里,这样github给我们传输数据就是用我们上传的公钥来加密,我们获得数据后会用私钥去解密。

什么是SSL/TLS

TLS是 Transport Layer Security的缩写,传输层安全性协议,SSL是Secure Sockets Layer的缩写,安全sokects层协议。SSL/TLS有很多好处,强大的验证,算法灵活,容易部署和使用。缺点是增加处理器的负担,但是消耗的性 能很小,对比安全性来说可以忽略不计。

通信过程

通信过程有四次握手。
1、客户端发送请求,服务器返回公钥给客户端;
2、客户端生成对称加密秘钥,用公钥对其进行加密后,返回给服务器;
3、服务器收到后,利用私钥解开得到对称加密秘钥,保存;
4、之后的交互都使用对称加密后的数据进行交互。

HTTPS通信的优点
1)客户端产生的密钥只有客户端和服务器端能得到;

2)加密的数据只有客户端和服务器端才能得到明文;

3)客户端到服务端的通信是安全的。

如何获得证书

1.向CA申请证书

电子商务认证授权机构(CA, Certificate Authority),也称为电子商务认证中心,是负责发放和管理数字证书的权威机构。
这里就不细说了。

2.自制证书

还有一种方式就是自制证书,自制证书的证书是用OpenSSL生成的。OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并且已经在github上开源。

OpenSSL的各种指令
OpenSSL

自制证书的命令是

1
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days XXX

刚开始输入命令就会自动生成key.pem
输入命令后会让你输入密码、国家、省市、组织(公司)、名字等信息
输入完成便会生成证书cert.pem

关于命令的一些选项:

  • req 是证书请求和生成的程序
  • -x509 一种证书标准
  • -newkey arg,arg是参数,例如rsa:2048是指生成2048位的rsa key
  • -keyout filename 输出的根证书文件名
  • -out filename 输出的标准证书文件吗
  • -day n 用X.509标准的话要指定验证多少天,默认30
  • *

预览cert.pem可以看到刚才输入的信息
预览pem

pem可以转换成cer格式,可以用命令
1
openssl x509 -in <你的服务器证书>.pem -outform der -out server.cer

也可以双击 cert.pem,这样是导入到钥匙串,打开钥匙串便可导出cer格式的证书。

以上 部分自制证书资料参考的是stackoverflow的这个问题

关于Security框架

看了一下苹果的官方文档,Security框架是C语言写的,提供了一些管理标识码,证书,数字签名,信任等的API。
这里介绍一下几个常用的对象。SecIdentityRef 代码一个标识码对象,struct类型,包含一个SecKeyRef类型和一个SecCertificateRef类型。SecKeyRef就是一个非对称的key对象。SecCertificateRef是一个遵循X.509标准的证书对象。如果这两个对象没有存储到keychain中,则会把它们转换成SecKeychainItemRef对象还会使Keychain Services的函数返回错误。

要生成p12证书,这让我想起配置推送证书的时候,导出证书的时候便是把cer格式的证书转换成p12格式的证书。

iOS实现HTTPS传输

因为项目都是用AFN,所以就大概说下AFN的实现方法
如果是CA认证的证书,则直接用AFN请求便可。

1
2
3
4
5
6
7
8
9
10
11
AFSecurityPolicy * securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
//allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
//如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
// 置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是 www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个 还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = YES;

 

如果是自制证书,则客户端需要导入服务端的公钥,把公钥拖进Xocde里,这里要用到把证书从pem转换成p12格式,参见上面的方法
2.修改验证方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
- (void)testClientCertificate {
  SecIdentityRef identity = NULL;
  SecTrustRef trust = NULL;
  NSString *p12 = [[NSBundle mainBundle] pathForResource:@"testClient" ofType:@"p12"];
  NSData *PKCS12Data = [NSData dataWithContentsOfFile:p12];
  [[self class] extractIdentity:&identity andTrust:&trust fromPKCS12Data:PKCS12Data];
  NSDictionary *dic = @{@"request" : @{
                            @"orderNo" : @"1409282102222110030643",
                            @"type" : @(2)
                            }
                        };
  _signString = nil;
  NSData *postData = [NSJSONSerialization dataWithJSONObject:dic
                                                     options:NSJSONWritingPrettyPrinted
                                                       error:nil];
  NSString *sign = [self signWithSignKey:@"test" params:dic];
  NSMutableData *body = [postData mutableCopy];
  NSLog(@"%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
  url = [NSString stringWithFormat:url, sign];
  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
  manager.requestSerializer = [AFJSONRequestSerializer serializer];
  manager.responseSerializer = [AFJSONResponseSerializer serializer];
  [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
  [manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                            @"text/plain"]];
  manager.securityPolicy = [self customSecurityPolicy];
  [manager POST:url parameters:dic success:^(AFHTTPRequestOperation *operation, id responSEObject) {
    NSLog(@"JSON: %@", responSEObject);
  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
  }];
}
// 下面这段代码是处理SSL安全性问题的:
/**** SSL Pinning ****/
- (AFSecurityPolicy*)customSecurityPolicy {
  NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"testClient" ofType:@"cer"];
  NSData *certData = [NSData dataWithContentsOfFile:cerPath];
  AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
  [securityPolicy setAllowInvalidCertificates:YES];
  [securityPolicy setPinnedCertificates:@[certData]];
  [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
  /**** SSL Pinning ****/
  return securityPolicy;
}

 

这段代码来自参考资料3,写的很好,没必要再说一次了

总结

在现在网络越来越发达的情况下,安全性越来越重要。不多说,https是趋势。

参考资料:
1.聊聊 iOS 中的网络加密 / 滕先洪

2.iOS安全系列之一:HTTPS / Jaminzzhang

3.iOS访问HTTPS SSL和TLS双向加密 / 标哥的技术博客

4.苹果相关官方文档

jQuery+ASP.NET MVC基于CORS实现带cookie的跨域ajax请求 - dudu - 博客园

mikel阅读(1533)

来源: jQuery+ASP.NET MVC基于CORS实现带cookie的跨域ajax请求 – dudu – 博客园

这是今天遇到的一个实际问题,在这篇随笔中记录一下解决方法。

ASP.NET Web API提供了CORS支持,但ASP.NET MVC默认不支持,需要自己动手实现。可以写一个用于实现CORS的ActionFilterAttribute,我们就是这么实现的:

复制代码
public class AllowCorsAttribute : ActionFilterAttribute
{
    private string[] _domains;

    public AllowCorsAttribute(string domain)
    {
        _domains = new string[] { domain };
    }

    public AllowCorsAttribute(string[] domains)
    {
        _domains = domains;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var context = filterContext.RequestContext.HttpContext;
        var host = context.Request.UrlReferrer?.Host;
        if (host != null && _domains.Contains(host))
        {
            context.Response.AddHeader("Access-Control-Allow-Origin", $"http://{host}");
            context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
        }
        base.OnActionExecuting(filterContext);
    }
复制代码

在需要支持CORS的Controller Action上加上[AllowCors(“www.cnblogs.com”)]标记。

JQuery ajax的请求代码如下:

复制代码
$.ajax({
    url: '...',
    type: 'get',
    xhrFields: {
        withCredentials: true
    },
    dataType: 'application/json; charset=utf-8',
    success: function (data) {
        //...
    }
});
复制代码

【遇到的问题】

1)一开始在ajax代码中没加”withCredentials: true”,发现ajax请求中没带上cookies。

2)加了”withCredentials: true”后,服务端响应出错:

XMLHttpRequest cannot load '{url}'. Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'http://www.cnblogs.com' is therefore not allowed access.

后来在服务端添加了如下的响应头解决了问题:

context.Response.AddHeader("Access-Control-Allow-Credentials", "true");

Unsupported major.minor version 51.0问题及解决方法

mikel阅读(1000)

jdk1.6工程中使用外部jar包中类出现:Unsupported major.minor version 51.0

 

问题原因:外部jar包使用jdk1.7(jdk7)编译,而使用此jar包的工程jdk版本为jdk1.6(jdk6),算是版本不支持。

 

解决方法:将此jar包重新用jdk1.6(jdk6)编译级别编译。

 

PS:jdk1.7下switch语句支持字符串即switch(“字符串”){},而jdk1.6下switch语句不支持字符串匹配。

使用PHP发送邮件的两种方法 - 苗雨顺的专栏 - 博客频道 - CSDN.NET

mikel阅读(1333)

来源: 使用PHP发送邮件的两种方法 – 苗雨顺的专栏 – 博客频道 – CSDN.NET

今天研究了一下使用PHP来发送电子邮件,总结了一下,有这么两种方法:

 

一、使用PHP内置的mail()函数

看了一下手册,就直接开始写代码了,如下

 

  1. <?php
  2. $to = “test@163.com”//收件人
  3. $subject = “Test”//主题
  4. $message = “This is a test mail!”//正文
  5. mail($to,$subject,$message);

结果就直接报错,如下:

Warning: mail() [function.mail]: Failed to connect to mailserver at “localhost” port 25, verify your “SMTP” and “smtp_port” setting in php.ini or use ini_set() inD:/www/Zend/email/email.php on line 10

 

看来本地需要有SMTP服务器,那就使用别人的试试吧,又改了下代码:

  1. <?php
  2. $to = “test@163.com”;//收件人
  3. $subject = “Test”;//邮件主题
  4. $message = “This is a test mail!”;//邮件正文
  5. ini_set(‘SMTP’,‘smtp.163.com’);//发件SMTP服务器
  6. ini_set(‘smtp_port’,25);//发件SMTP服务器端口
  7. ini_set(‘sendmail_from’,“admin@163.com”);//发件人邮箱
  8. mail($to,$subject,$message);

结果还是错误:

Warning: mail() [function.mail]: SMTP server response: 553 authentication is required,smtp2,DNGowKD7v5BTDo9NnplVBA–.1171S2 1301220947 inD:/www/Zend/email/email.php on line 9

 

看来是需要验证信息,怎么写验证信息呢?在哪里配置呢?上网找了半天也没找出个所以然,最后看了别人一些技术文章后得出结论(由于对SMTP邮件什 么的不是非常了解,所以也不知道这个结论是否是正确的):使用mail()函数发送邮件就必须要有一台无需SMTP验证就可以发信的邮件服务器。但现在的 SMTP邮件服务器基本上都是需要验证的,所以要想使用它发邮件就只能自己在本地搭一个不需要验证的SMTP服务器。这就比较麻烦了,我是不想整,有兴趣 的同学可以自己试试搭一个,用windows自带的IIS就可以,或者从网上下载其他的SMTP服务器软件,我就不多说。

 

结论:使用mail()函数发送邮件,就必须要有一台不需要验证的SMTP服务器。

这样的话配置工作会多一点,但是使用的时候就比较省事了,几行代码就可以。

 

 

二、使用封装SMTP协议的邮件类

 

这种方法就比较常见了,尤其对于广大自己没有服务器,从网上购买虚拟主机的同学,

第一种方法不现实,所以还是自己使用SMTP协议来发送邮件吧。

不过要完成这项工作的话,就需要你对SMTP协议有一定的了解,喜欢事必躬亲的同学可以自己动手写一个,喜欢拿来主义的同学就可以从网上下载了,有很多。

不过我比较推荐使用PEAR扩展中的Mail类,功能强大:可以支持纯文本、HTML格式的邮件;各字段都可设置编码,正确配置不会出现中文乱码情况;可以支持附件等等。

在服务器可以使用 pear install Mail 命令快速安装,没有足够服务器权限的同学也可以直接下载类的PHP源码包含进来就可以了。

注:Mail类依赖  Net/SMTP.php  和 Mail/mime.php ,要一块下载,使用时一块包含进来。

详细安装方法可以在官网查看,http://pear.php.net/package/Mail

下面我举例说明一下在Mail类发送邮件的方法吧,网上其他SMTP邮件类使用方法一块也类似,可以参考:

  1. <?php
  2. // Pear Mail 扩展
  3. require_once(‘Mail.php’);
  4. require_once(‘Mail/mime.php’);
  5. require_once(‘Net/SMTP.php’);
  6. $smtpinfo = array();
  7. $smtpinfo[“host”] = “smtp.163.com”;//SMTP服务器
  8. $smtpinfo[“port”] = “25”//SMTP服务器端口
  9. $smtpinfo[“username”] = “username@163.com”//发件人邮箱
  10. $smtpinfo[“password”] = “password”;//发件人邮箱密码
  11. $smtpinfo[“timeout”] = 10;//网络超时时间,秒
  12. $smtpinfo[“auth”] = true;//登录验证
  13. //$smtpinfo[“Debug”] = true;//调试模式
  14. // 收件人列表
  15. $mailAddr = array(‘receiver@163.com’);
  16. // 发件人显示信息
  17. $from = “Name <username@163.com>”;
  18. // 收件人显示信息
  19. $to = implode(‘,’,$mailAddr);
  20. // 邮件标题
  21. $subject = “这是一封测试邮件”;
  22. // 邮件正文
  23. $content = “<h3>随便写点什么</h3>”;
  24. // 邮件正文类型,格式和编码
  25. $contentType = “text/html; charset=utf-8”;
  26. //换行符号 Linux: \n  Windows: \r\n
  27. $crlf = “\n”;
  28. $mime = new Mail_mime($crlf);
  29. $mime->setHTMLBody($content);
  30. $param[‘text_charset’] = ‘utf-8’;
  31. $param[‘html_charset’] = ‘utf-8’;
  32. $param[‘head_charset’] = ‘utf-8’;
  33. $body = $mime->get($param);
  34. $headers = array();
  35. $headers[“From”] = $from;
  36. $headers[“To”] = $to;
  37. $headers[“Subject”] = $subject;
  38. $headers[“Content-Type”] = $contentType;
  39. $headers = $mime->headers($headers);
  40. $smtp =& Mail::factory(“smtp”$smtpinfo);
  41. $mail = $smtp->send($mailAddr$headers$body);
  42. $smtp->disconnect();
  43. if (PEAR::isError($mail)) {
  44.     //发送失败
  45.     echo ‘Email sending failed: ‘ . $mail->getMessage().“\n”;
  46. }
  47. else{
  48.     //发送成功
  49.     echo “success!\n”;
  50. }

如果从网上找的SMTP类都是高度封装的,所以使用起来比上面会更简单,但使用方法都是比较相似的。

 

结论:这种方式发送邮件无需装任何软件,只需要包含进来一个PHP类,然后多写几行配置代码,就可以了。并且网上有很多示例的代码,很多时候只要复制过来然后修改个别的几个参数就可以用了,所以会很方便,推荐使用此方法。

内网穿透神器ngrok - 牦牛 - 博客园

mikel阅读(1509)

来源: 内网穿透神器ngrok – 牦牛 – 博客园

相信做Web开发的同学们,经常会遇到需要将本地部署的Web应用能够让公网环境直接访问到的情况,例如微信应用调试、支付宝接口调试等。这个时候,一个叫ngrok的神器可能会帮到你,它提供了一个能够在公网安全访问内网Web主机的工具,能捕获所有HTTP请求的内容,也支持TCP端口映射,支持Linux、Windows、Mac OS X 等平台。

我们现在就来小试下牛刀,感受下 ngrok 的好玩之处。

一、ngrok 下载

到官网下载ngrok小工具:https://ngrok.com/,工具体积很小,现在已经升级到 2.X 的版本,只支持64位操作系统,并被作者用于商业化。1.X版本的免费自定义固定二级域名功能已经开始收费,这块后面会说明。

二、ngrok 使用

以windows版本为例,把下载的压缩包解压到本机硬盘,用 DOS命令切换到 ngrok.exe 所在的目录:

 

好了,接下来如何使用呢?我们来通过演示几个简单样例来说明:

输入 ngrok http 8001,回车,出现如下信息:

意 思是 ,随机分配一个公网可以访问的二级域名http://49412880.ngrok.io/ 转发到我们本机的8001端口 ,这也就意味着,现在访问 http://49412880.ngrok.io/ 就如同访问内网的http://127.0.0.1:8001。

同时通过 ngrok提供的管理界面(http://127.0.0.1:4040)可以清晰的看到当前有哪些连接、请求的URL等, 是不是很方便?但是上面分配 的域名是临时且随机的,一旦本机重启或者ngrok重启后,这个域名就变化了。如何把一个固定的域名映射到本机呢? V1.X的版本是可以免费支持将一个固定的二级域名指向本机的,不过作者已经把 V2.X的版本商业化,所以固定域名的转发现在需要收费了,费用倒是不贵,有兴趣的同学可以去官网看看。

 

当我们的机器绑定了多个IP时,通过指定IP来转发映射:ngrok http 192.168.1.101:8006

也可以转发同一局域网内其它的主机和端口:ngrok http 192.168.4.6:8008

TCP端口转发:ngrok tcp 22

       ngrok tcp 3389  

这样我们可以在公网SSH到内网Linux机器,远程桌面到内网Windows机器了,当然外网端口也是临时随机的。如下图,我们已经在公网环境下远程到了内网windows机器上

怎么样,有意思吧。

三、ngrok小问题

不过需要说明一点,ngrok的访问可能会不太稳定,原因你们都懂的,指不定哪天就被墙了。

微信扫码支付+Asp.Net MVC - stoneniqiu - 博客园

mikel阅读(1276)

来源: 微信扫码支付+Asp.Net MVC – stoneniqiu – 博客园

这里的扫码支付指的是PC网站上面使用微信支付,也就是官方的模式二,网站ASP.NET MVC,整理如下。

一、准备工作

使用的微信API中的统一下单方法,关键的参数是‘公众账号ID(appid)’,‘商户号(mch_id)’和’商户支付密钥(KEY)‘,所以首先要有一个审核过的公众号,并开通支付功能,然后申请商户,通过审核后得到商户号,也就是商户平台的登录名。商户支付密钥是用来签名的,确保url不被篡改。进入商户平台后在API安全中设置,是一个32位的字符串。

有这三个参数后,还有一点要注意的是交易起始时间和交易结束时间的间隔应该在五分钟以上2小时以内。不然获取支付url的时候回报错。

二、生成支付二维码

有了上面的参数,接下来就是下载SDK: .net SDK及示例 

可惜官方的这个示例一开始并不能运行正确。把相关dll引用MVC目录下。并创建一个WxPayAPI文件夹把相关类复制过来。

  

然后将WxPayConfig中的相关参数设置成自己的参数,再修改GetPayUrl方法,

复制代码
 public string GetPayUrl(Order order,string ip)
        {
            if (order == null)
            {
                throw new ArgumentNullException("order");
            }
           
            var product = order.OrderItems.First();
            WxPayData data = new WxPayData();
            data.SetValue("appid", WxPayConfig.APPID);
            data.SetValue("mch_id", WxPayConfig.MCHID);
            // data.SetValue("device_info", "iphone4s");
            data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());
            data.SetValue("body", product.AttributeDescription);//商品描述
            data.SetValue("detail", product.AttributeDescription);//商品描述
            data.SetValue("attach", "北京分店");//附加数据
            data.SetValue("out_trade_no", order.TradeNumber);//随机字符串
           // data.SetValue("total_fee", Convert.ToInt32(order.OrderTotal * 100));//总金额
            data.SetValue("total_fee", 1);//总金额
            data.SetValue("spbill_create_ip",ip);//总金额
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
            data.SetValue("time_expire", DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss"));//交易结束时间
            data.SetValue("goods_tag", "智能婴儿床");//商品标记
            data.SetValue("notify_url", "http://www.xxxx.com/Checkout/ResultNotify");//通知地址
            data.SetValue("trade_type", "NATIVE");//交易类型
            data.SetValue("product_id", product.ProductId);//商品ID  
            data.SetValue("sign", data.MakeSign());//签名
            Logger.Info("获得签名" + data.GetValue("sign"));

            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
            Logger.Info(result.ToJson());
            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接
            Logger.Info("pay url:" + url);
            return url;
        }
复制代码

TradeNumber是调用WxPayApi.GenerateOutTradeNo() 方法生成的,notify_url是用户支付之后微信通知的地址。金额的单位是分,只能传int型或string型,decimal需要转换一下。获取 url成功后,在负责支付的控制器中创建一个payment方法。用于显示二维码:

复制代码
 public ActionResult Payment(string guid)
        {
            if(string.IsNullOrEmpty(guid)) 
                throw new ArgumentException("guide");
            var order = _orderService.GetOrderByGuid(new Guid(guid));var user = _workContext.CurrentUser;
            NativePay nativePay = new NativePay();
            string url2 = nativePay.GetPayUrl(order, user.LastIpAddress);
            ViewBag.QRCode = "/Checkout/MakeQRCode?data=" + HttpUtility.UrlEncode(url2);
            ViewBag.Order = order;
          
            return View();
        }
复制代码

这里只是返回了一个url,在页面上:

<img src="@ViewBag.QRCode" class="qrcode"  />

后台用的qrCodeEncoder生成二维码。

复制代码
  public FileResult MakeQRCode(string data)
        {
            if (string.IsNullOrEmpty(data)) 
                throw new ArgumentException("data");

            //初始化二维码生成工具
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
            qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
            qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
            qrCodeEncoder.QRCodeVersion = 0;
            qrCodeEncoder.QRCodeScale = 4;

            //将字符串生成二维码图片
            Bitmap image = qrCodeEncoder.Encode(data, Encoding.Default);

            //保存为PNG到内存流  
            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Jpeg);

            return File(ms.ToArray(), "image/jpeg");
        }
复制代码

成功之后得到支付页面:

扫码后跳出支付页面:

三、回调

用户支付之后,微信会给之前预留的接口(接口不能带参数)发消息, 网站在收到消息后进行验证和确认,确定之后再给微信发一个消息。详细参数和文档请看官方API

这里还是把demo中的方法稍作改动放到了控制器里面:

复制代码
  public ActionResult ResultNotify()
        {
            //接收从微信后台POST过来的数据
            Stream s = Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();

            Logger.Info(this.GetType()+ "Receive data from WeChat : " + builder);
            //转换数据格式并验证签名
            WxPayData data = new WxPayData();
            try
            {
                data.FromXml(builder.ToString());
            }
            catch (WxPayException ex)
            {
                //若签名错误,则立即返回结果给微信支付后台
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", ex.Message);
                Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }
            Logger.Info(this.GetType()+ "Check sign success");


            ProcessNotify(data);

            return View();
        }
        public void ProcessNotify(WxPayData data)
        {
            WxPayData notifyData = data;

            //检查支付结果中transaction_id是否存在
            if (!notifyData.IsSet("transaction_id"))
            {
                //若transaction_id不存在,则立即返回结果给微信支付后台
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "支付结果中微信订单号不存在");
                Logger.Error(this.GetType()+"The Pay result is error : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }

            string transaction_id = notifyData.GetValue("transaction_id").ToString();

            //查询订单,判断订单真实性
            if (!QueryOrder(transaction_id))
            {
                //若订单查询失败,则立即返回结果给微信支付后台
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "订单查询失败");
                Logger.Error(this.GetType()+"Order query failure : " + res.ToXml());
                Response.Write(res.ToXml());
                Response.End();
            }
            //查询订单成功
            else
            {
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "SUCCESS");
                res.SetValue("return_msg", "OK");
                Logger.Info(this.GetType()+"order query success : " + res.ToXml());
                SetPaymentResult(data.GetValue("out_trade_no").ToString(), PaymentStatus.Paid);
                Response.Write(res.ToXml());
                Response.End();
            }
        }
复制代码

收到确认后,我们要更新订单的状态:

复制代码
  public void SetPaymentResult(string tradeno, PaymentStatus status)
        {
            Logger.Info("订单号:"+tradeno);
            var order = _orderService.GetOrderByTradeNumber(tradeno);
            if (order != null)
            {
                order.PaymentStatus = status;
                if (status == PaymentStatus.Paid)
                {
                    order.PaidDate = DateTime.Now;
                }
                _orderService.UpdateOrder(order);
                Logger.Info("订单:"+tradeno+"成功更新状态为"+status);
            }
        }
复制代码

然后在页面上检测订单的状态,确定成功后,跳转页面。

在商户平台的后台,我们可以查询到:

 

小结:主要过程就是这样,因为不能本地调试,打日志调试比较耗时,希望对你有帮助。接下来研究下退款(需要证书)。

 

互联网金融爬虫怎么写-第一课 p2p网贷爬虫(XPath入门) - 游牧民族 - 博客园

mikel阅读(1038)

来源: 互联网金融爬虫怎么写-第一课 p2p网贷爬虫(XPath入门) – 游牧民族 – 博客园

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关教程:

手把手教你写电商爬虫-第一课 找个软柿子捏捏

手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫

手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

手把手教你写电商爬虫-第五课 京东商品评论爬虫 一起来对付反爬虫

 

 

工具要求:教程中主要使用到了 1、神箭手云爬虫 框架  这个是爬虫的基础,2、Chrome浏览器和Chrome的插件XpathHelper 这个用来测试Xpath写的是否正确

基础知识:本教程中主要用到了一些基础的js和xpath语法,如果对这两种语言不熟悉,可以提前先学习下,都很简单

 

之前写了一个电商爬虫系列的文章,简单的给大家展示了一下爬虫从入门到进阶的路 径,但是作为一个永远走在时代前沿的科技工作者,我们从来都不能停止 在已有的成果上,所以带上你的chrome,拿起你的xpathhelper,打开你的神箭手,让我们再次踏上征战金融数据之旅吧。(上个系列相对难一 些,建议如果是初学者,先看这个系列的教程)

 

 

金融数据实在是价值大,维度多,来源广。我们到底从哪里入手呢?想来想去,就从前一段时间风云变幻的p2p网贷开始吧。

同样,我们教程的一致风格就是先找个软柿子,上来不能用力过猛,逐渐培养自己的信心,等真正敌人来的时候,才不至于怯场害怕。

我们先去搜索一下p2p网站,随便找几个对比一下,选中了这个沪商财富

 

 

看着这样的收益率,心动了有木有,钱包坐不住了有木有,对余额宝投出鄙夷的目光了有木有

 

好了,闲话不说,这个系列课程吸取上个系列课程里进度太快的教训,给大家多讲一些 基础的知识,这一课就结合这个实例,重点讲讲xpath的编写和用 法。首先,大体来讲,XPath是一个相对简单的语言,甚至都不一定能称得上是一个语言,主要用处是用来标记XML的元素路径。由于html也是一种 xml,因此通常来说,在html中抽取某个元素是通过XPath来做的。XPath本身和Css有着很大的相似性,一般来说如果之前对Css有一定的了 解的话,XPath上手还是很简单的。具体的情况我在下面的课程中一边写,一边解释。

首先先确定列表页:

http://www.hushangcaifu.com/invest/main.html

http://www.hushangcaifu.com/invest/index2.html

http://www.hushangcaifu.com/invest/index3.html

基本上可以看到列表页除了第一页以外都有规律可寻,不过看到这个效果,通常我们最好精益求精一下,看下第一页是否也可以符合规律呢?

打开http://www.hushangcaifu.com/invest/index1.html 果然也是第一页,好了,很完美,总结成正则表达式:

http://www\\.hushangcaifu\\.com/invest/index\\d+\\.html

 

再看下详情页:

http://www.hushangcaifu.com/invest/a3939.html

http://www.hushangcaifu.com/invest/a3936.html

哈哈,小菜一碟,直接化解成正则:

http://www\\.hushangcaifu\\.com/invest/a\\d{4}\\.html

 

好了,最后最重要的就是提取页面元素了。我们打开详情页:

http://www.hushangcaifu.com/invest/a3870.html

 

一般来说,我们在我们想要提取的元素上右击,点击审查元素,得到如下结果:

首先看到yanh1147这个元素有没有整个网页唯一的class,id或者其他 属性,可以看到,在这个页面中没有,那么我们就往上找,上一级的p 标签也没有,咱们再往上找,在上一级是一个<div class=”product-content-top-left-top”>,终于有class了,让我们祈祷这个class是唯一的 吧,ctrl+f打开搜索框,输入product-content-top-left-top,可以看到,找到了1 of 1,这个代表一共一个,这个是第一个,这就是我们希望的结果,好了,只用找到这一级既可,我们来构造整个的xpath,一般来说xpath我们并不会从最 顶层的html开始写,因为没有必要,因此我们要使用//,这个表示不知中间有多少的层级。接着我们直接把刚刚找到的这个div写上去,得到这个表达式:

//div[contains(@class,"product-content-top-left-top")]

对于class属性,我们通常会使用contains这样一个函数,防止一个元素有多个class的情况,另外因为class是一个属性,因此class前面需要加上@代表选择到该元素的一个属性。

现在我们已经选择到了我们要选择的元素的父元素的父元素,只要我们继续往下走两层既可。

//div[contains(@class,"product-content-top-left-top")]/p/span

由于我们要选择元素里的文字信息,而不是整个元素,我们需要指定是这个元素的文字:

//div[contains(@class,"product-content-top-left-top")]/p/span/text()

 

好了,这样我们就确定了我们爬取的借款用户的名称,我们打开xpathhelper验证一下有没有写错:

 

完美的结果。不过大家有的时候也需要注意,因为有的网页不代表你在一个内容页测试成功,在其他内容页也能成功,最好多测几个页面才是保险的。好了,其他的抽取项就不一一演示了,直接上最后的代码

 

复制代码
var configs = {
    domains: ["www.hushangcaifu.com"],
    scanUrls: ["http://www.hushangcaifu.com/invest/index1.html"],
    contentUrlRegexes: ["http://www\\.hushangcaifu\\.com/invest/a\\d{4}\\.html"],
    helperUrlRegexes: ["http://www\\.hushangcaifu\\.com/invest/index\\d+\\.html"],
    fields: [
        {
            name: "title",
            selector: "//div[contains(@class,'product-content-top-left-top')]/h3/text()",
            required: true 
        },
        {
            name: "user_name",
            selector: "//div[contains(@class,'product-content-top-left-top')]/p/span/text()"
        },
        {
            name: "total_money",
            selector: "//div[contains(@class,'product-content-top-left-middle')]/div[1]/h4/text()"
        },
        {
            name: "project_time",
            selector: "//div[contains(@class,'product-content-top-left-middle')]/div[2]/h4/text()"
        },
        {
            name: "annual_return",
            selector: "//div[contains(@class,'product-content-top-left-middle')]/div[3]/h4/text()"
        },
        {
            name: "return_method",
            selector: "//div[contains(@class,'product-content-top-left-middle')]/div[4]/h4/text()"
        }
        
    ]
};

var crawler = new Crawler(configs);
crawler.start();
复制代码

将代码粘贴到神箭手平台上既可运行。好了,看下运行结果:

对爬虫感兴趣的童鞋可以加qq群讨论:342953471。

mysql 1067错误的解决方法

mikel阅读(1198)

解决方法如下:
1、先删除mySQL服务
控制面板->管理工具->服务,先停止mySQL服务
开始->运行->输入cmd->sc delete mysql
服务删除
2、修改my.ini
如果没将其创建(以下设置可以参考http://hi.baidu.com/chuyanwu/blog/item/98142a2e7d448d564ec2262c.html一般这个设置都不会错误)
[mysqld]
# set basedir to your installation path
basedir=c:/mysql (mysql所在目录)
# set datadir to the *** of your data directory
datadir=c:/mysql/data (mysql所在目录/data)
[WinMySQLAdmin]
Server=c:/mysql/bin/mysqld-nt.exe
3、启动服务
附:启动服务bat:(放在mysql根目录下)

复制代码 代码如下:
@echo off
if not exist mySQLServer5\data\%computername%.pid goto startsvr
net stop MySQL
mySQLServer5\bin\mysqld-nt.exe –remove MySQL
:startsvr
echo MySQL服务正在启动…..
mySQLServer5\bin\mysqld-nt.exe –install MySQL –defaults-file=”%cd%\my.ini”
net start MySQL
pause

MySQL安装与备份
一、下载MySQL
http://www.mysql.org/downloads
二、安装过程
1、解压缩mysql-noinstall-5.0.51b-win32.zip到一个目录。
假定MYSQL_HOME=C:\mysql-5.0.51b-win32
2、编写mysql的运行配置文件my.ini
my.ini
—————————–
[mysqld]
# 设置mysql的安装目录
basedir=$MYSQL_HOME
# 设置mysql数据库的数据的存放目录,必须是data,或者是\\xxx\data
datadir=$MYSQL_HOME\data
# 设置mysql服务器的字符集
default-character-set=utf8
[client]
# 设置mysql客户端的字符集
default-character-set=gbk
—————————–
3、安装mysql服务
从MS-DOS窗口进入目录C:\mysql-5.0.51b-win32\bin,
(把my.ini放到C:\mysql-5.0.51b-win32\目录下!)
运行如下命令:
mysqld –install MySQL5 –defaults-file=C:\mysql-5.0.51b-win32\my.ini(服务安装成功!但是出现发生系统错误 1067)
mysqld-nt.exe -install(服务安装成功!并成功启动)
4、启动mysql数据库
还在上面的命令窗口里面,输入命令:net start MySQL5
这样就启动了mysql服务。
5、删除服务
执行mysqld –remove MySQL5即可
-------------------------
MySQL备份常常有以下几种方法
1、使用phpmyadmin,不过用这个备份,数据的大小要小于两兆,多了就不好恢复了,对于数据特别小的备份,还是挺管用的!
2、 使用程序软件自带的后台备份工具备份,这样备份出来的数据一般是没有大小限制的,备份出来后的还原是需要将该程序在新空间安装一次,然后登陆后台,在后台 用其自带的还原工具还原一下就实现了新的数据的导入,比如,象discuz、phpwind、bo-blog……都可以这样备份!
3、使用其他SQL工具软件备份:比较好用的有 帝国备份王 优点,数据库可以大小不受限制,不过程序被zend了,并且备份程序文件很多。
另一个软件为 faisunsql 优点,数据库可以大小不受限制,备份程序就一个文件,导出,导入方便,直接操作数据库。(强烈推荐使用)

linux find命令格式及find命令详解

mikel阅读(980)

1、find命令的一般形式为;

find pathname -options [-print -exec -ok …]

2、find命令的参数;

pathname: find命令所查找的目录路径。例如用.来表示当前目录,用/来表示系统根目录。

-print: find命令将匹配的文件输出到标准输出。

-exec: find命令对匹配的文件执行该参数所给出的shell命令。相应命令的形式为’command’ { } ;,注意{ }和;之间的空格。

-ok: 和-exec的作用相同,只不过以一种更为安全的模式来执行该参数所给出的shell命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行。

3、find命令选项

-name

按照文件名查找文件。

-perm

按照文件权限来查找文件。

-prune

使用这一选项可以使find命令不在当前指定的目录中查找,如果同时使用-depth选项,那么-prune将被find命令忽略。

-user

按照文件属主来查找文件。

-group

按照文件所属的组来查找文件。

-mtime -n +n

按照文件的更改时间来查找文件, – n表示文件更改时间距现在n天以内,+ n表示文件更改时间距现在n天以前。find命令还有-atime和-ctime 选项,但它们都和-m time选项。

-nogroup

查找无有效所属组的文件,即该文件所属的组在/etc/groups中不存在。

-nouser

查找无有效属主的文件,即该文件的属主在/etc/passwd中不存在。

-newer file1 ! file2

查找更改时间比文件file1新但比文件file2旧的文件。

-type

查找某一类型的文件,诸如:

b – 块设备文件。

d – 目录。

c – 字符设备文件。

p – 管道文件。

l – 符号链接文件。

f – 普通文件。

-size n: 查找文件长度为n块的文件,带有c时表示文件长度以字节计。-depth:在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。

-fstype:查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件/etc/fstab中找到,该配置文件中包含了本系统中有关文件系统的信息。

-mount:在查找文件时不跨越文件系统mount点。

-follow:如果find命令遇到符号链接文件,就跟踪至链接所指向的文件。

-cpio:对匹配的文件使用cpio命令,将这些文件备份到磁带设备中。

另外,下面三个的区别:

-amin n

查找系统中最后N分钟访问的文件

-atime n

查找系统中最后n*24小时访问的文件

-cmin n

查找系统中最后N分钟被改变文件状态的文件

-ctime n

查找系统中最后n*24小时被改变文件状态的文件

-mmin n

查找系统中最后N分钟被改变文件数据的文件

-mtime n

查找系统中最后n*24小时被改变文件数据的文件

4、使用exec或ok来执行shell命令

使用find命令时,只要把想要的操作写在一个文件里,就可以用exec来配合find命令查找,很方便的

在有些操作系统中只允许-exec选项执行诸如l s或ls -l这样的命令。大多数用户使用这一选项是为了查找旧文件并删除它们。建议在真正执行rm命令删除文件之前,最好先用ls命令看一下,确认它们是所要删除的文件。

exec选项后面跟随着所要执行的命令或脚本,然后是一对儿{ },一个空格和一个,最后是一个分号。为了使用exec选项,必须要同时使用print选项。如果验证一下find命令,会发现该命令只输出从当前路径起的相对路径及文件名。

例如:为了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在find命令的-exec选项中

# find . -type f -exec ls -l { } ;

-rw-r–r– 1 root root 34928 2003-02-25 ./conf/httpd.conf

-rw-r–r– 1 root root 12959 2003-02-25 ./conf/magic

-rw-r–r– 1 root root 180 2003-02-25 ./conf.d/README

上面的例子中,find命令匹配到了当前目录下的所有普通文件,并在-exec选项中使用ls -l命令将它们列出。

在/logs目录中查找更改时间在5日以前的文件并删除它们:

$ find logs -type f -mtime +5 -exec rm { } ;

记住:在shell中用任何方式删除文件之前,应当先查看相应的文件,一定要小心!当使用诸如mv或rm命令时,可以使用-exec选项的安全模式。它将在对每个匹配到的文件进行操作之前提示你。

在下面的例子中, find命令在当前目录中查找所有文件名以.LOG结尾、更改时间在5日以上的文件,并删除它们,只不过在删除之前先给出提示。

$ find . -name “*.conf” -mtime +5 -ok rm { } ;

< rm … ./conf/httpd.conf > ? n

按y键删除文件,按n键不删除。

任何形式的命令都可以在-exec选项中使用。

在下面的例子中我们使用grep命令。find命令首先匹配所有文件名为“ passwd*”的文件,例如passwd、passwd.old、passwd.bak,然后执行grep命令看看在这些文件中是否存在一个sam用户。

# find /etc -name “passwd*” -exec grep “sam” { } ;

sam:x:501:501::/usr/sam:/bin/bash

find命令的例子;

1、查找当前用户主目录下的所有文件:

下面两种方法都可以使用

$ find $HOME -print

$ find ~ -print

2、让当前目录中文件属主具有读、写权限,并且文件所属组的用户和其他用户具有读权限的文件;

$ find . -type f -perm 644 -exec ls -l { } ;

3、为了查找系统中所有文件长度为0的普通文件,并列出它们的完整路径;

$ find / -type f -size 0 -exec ls -l { } ;

4、查找/var/logs目录中更改时间在7日以前的普通文件,并在删除之前询问它们;

$ find /var/logs -type f -mtime +7 -ok rm { } ;

5、为了查找系统中所有属于root组的文件;

$find . -group root -exec ls -l { } ;

-rw-r–r– 1 root root 595 10月 31 01:09 ./fie1

6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的admin.log文件。

该命令只检查三位数字,所以相应文件的后缀不要超过999。先建几个admin.log*的文件 ,才能使用下面这个命令

$ find . -name “admin.log[0-9][0-9][0-9]” -atime -7 -ok

rm { } ;

< rm … ./admin.log001 > ? n

< rm … ./admin.log002 > ? n

< rm … ./admin.log042 > ? n

< rm … ./admin.log942 > ? n

7、为了查找当前文件系统中的所有目录并排序;

$ find . -type d | sort

8、为了查找系统中所有的rmt磁带设备;

$ find /dev/rmt -print

xargs

xargs – build and execute command lines from standard input

在使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现 溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。

find命令把匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。

在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;

而使用xargs命令则只有一个进程。另外,在使用xargs命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。

来看看xargs命令是如何同find命令一起使用的,并给出一些例子。

下面的例子查找系统中的每一个普通文件,然后使用xargs命令来测试它们分别属于哪类文件

#find . -type f -print | xargs file

./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text

./.kde/Autostart/.directory: ISO-8859 text

……

在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到/tmp/core.log 文件中:

$ find / -name “core” -print | xargs echo “” >/tmp/core.log

上面这个执行太慢,我改成在当前目录下查找

#find . -name “file*” -print | xargs echo “” > /temp/core.log

# cat /temp/core.log

./file6

在当前目录下查找所有用户具有读、写和执行权限的文件,并收回相应的写权限:

# ls -l

drwxrwxrwx 2 sam adm 4096 10月 30 20:14 file6

-rwxrwxrwx 2 sam adm 0 10月 31 01:01 http3.conf

-rwxrwxrwx 2 sam adm 0 10月 31 01:01 httpd.conf

# find . -perm -7 -print | xargs chmod o-w

# ls -l

drwxrwxr-x 2 sam adm 4096 10月 30 20:14 file6

-rwxrwxr-x 2 sam adm 0 10月 31 01:01 http3.conf

-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf

用grep命令在所有的普通文件中搜索hostname这个词:

# find . -type f -print | xargs grep “hostname”

./httpd1.conf:# different IP addresses or hostnames and have them handled by the

./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames

on your

用grep命令在当前目录下的所有普通文件中搜索hostnames这个词:

# find . -name * -type f -print | xargs grep “hostnames”

./httpd1.conf:# different IP addresses or hostnames and have them handled by the

./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames

on your

注意,在上面的例子中, 用来取消find命令中的*在shell中的特殊含义。

find命令配合使用exec和xargs可以使用户对所匹配到的文件执行几乎所有的命令。

find命令的参数

下面是find命令一些常用参数的例子,有用到的时候查查就行了,像上面前几个贴子,都用到了其中的的一些参数,也可以用man或查看论坛里其它贴子有find命令手册

1、使用name选项

文件名选项是find命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用。

可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来。

不管当前路径是什么,如果想要在自己的根目录$HOME中查找文件名符合*.txt的文件,使用~作为 ‘pathname’参数,波浪号~代表了你的$HOME目录。

$ find ~ -name “*.txt” -print

想要在当前目录及子目录中查找所有的‘ *.txt’文件,可以用:

$ find . -name “*.txt” -print

想要的当前目录及子目录中查找文件名以一个大写字母开头的文件,可以用:

$ find . -name “[A-Z]*” -print

想要在/etc目录中查找文件名以host开头的文件,可以用:

$ find /etc -name “host*” -print

想要查找$HOME目录中的文件,可以用:

$ find ~ -name “*” -print 或find . -print

要想让系统高负荷运行,就从根目录开始查找所有的文件。

$ find / -name “*” -print

如果想在当前目录查找文件名以两个小写字母开头,跟着是两个数字,最后是.txt的文件,下面的命令就能够返回名为ax37.txt的文件:

$find . -name “[a-z][a-z][0–9][0–9].txt” -print

2、用perm选项

按照文件权限模式用-perm选项,按文件权限模式来查找文件的话。最好使用八进制的权限表示法。

如在当前目录下查找文件权限位为755的文件,即文件属主可以读、写、执行,其他用户可以读、执行的文件,可以用:

$ find . -perm 755 -print

还有一种表达方法:在八进制数字前面要加一个横杠-,表示都匹配,如-007就相当于777,-006相当于666

# ls -l

-rwxrwxr-x 2 sam adm 0 10月 31 01:01 http3.conf

-rw-rw-rw- 1 sam adm 34890 10月 31 00:57 httpd1.conf

-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf

drw-rw-rw- 2 gem group 4096 10月 26 19:48 sam

-rw-rw-rw- 1 root root 2792 10月 31 20:19 temp

# find . -perm 006

# find . -perm -006

./sam

./httpd1.conf

./temp

-perm mode:文件许可正好符合mode

-perm +mode:文件许可部分符合mode

-perm -mode: 文件许可完全符合mode

3、忽略某个目录

如果在查找文件时希望忽略某个目录,因为你知道那个目录中没有你所要查找的文件,那么可以使用-prune选项来指出需要忽略的目录。在使用-prune选项时要当心,因为如果你同时使用了-depth选项,那么-prune选项就会被find命令忽略。

如果希望在/apps目录下查找文件,但不希望在/apps/bin目录下查找,可以用:

$ find /apps -path “/apps/bin” -prune -o -print

4、使用find查找文件的时候怎么避开某个文件目录

比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件

find /usr/sam -path “/usr/sam/dir1” -prune -o -print

find [-path ..] [expression] 在路径列表的后面的是表达式

-path “/usr/sam” -prune -o -print 是 -path “/usr/sam” -a -prune -o

-print 的简写表达式按顺序求值, -a 和 -o 都是短路求值,与 shell 的 && 和 || 类似如果 -path “/usr/sam” 为真,则求值 -prune , -prune 返回真,与逻辑表达式为真;否则不求值 -prune,与逻辑表达式为假。如果 -path “/usr/sam” -a -prune 为假,则求值 -print ,-print返回真,或逻辑表达式为真;否则不求值 -print,或逻辑表达式为真。

这个表达式组合特例可以用伪码写为

if -path “/usr/sam” then

-prune

else

-print

避开多个文件夹

find /usr/sam ( -path /usr/sam/dir1 -o -path /usr/sam/file1 ) -prune -o -print

圆括号表示表达式的结合。

表示引用,即指示 shell 不对后面的字符作特殊解释,而留给 find 命令去解释其意义。

查找某一确定文件,-name等选项加在-o 之后

#find /usr/sam (-path /usr/sam/dir1 -o -path /usr/sam/file1 ) -prune -o -name “temp” -print

5、使用user和nouser选项

按文件属主查找文件,如在$HOME目录中查找文件属主为sam的文件,可以用:

$ find ~ -user sam -print

在/etc目录下查找文件属主为uucp的文件:

$ find /etc -user uucp -print

为了查找属主帐户已经被删除的文件,可以使用-nouser选项。这样就能够找到那些属主在/etc/passwd文件中没有有效帐户的文件。在使用-nouser选项时,不必给出用户名; find命令能够为你完成相应的工作。

例如,希望在/home目录下查找所有的这类文件,可以用:

$ find /home -nouser -print

6、使用group和nogroup选项

就像user和nouser选项一样,针对文件所属于的用户组, find命令也具有同样的选项,为了在/apps目录下查找属于gem用户组的文件,可以用:

$ find /apps -group gem -print

要查找没有有效所属用户组的所有文件,可以使用nogroup选项。下面的find命令从文件系统的根目录处查找这样的文件

$ find / -nogroup-print

7、按照更改时间或访问时间等查找文件

如果希望按照更改时间来查找文件,可以使用mtime,atime或ctime选项。如果系统突然没有可用空间了,很有可能某一个文件的长度在此期间增长迅速,这时就可以用mtime选项来查找这样的文件。

用减号-来限定更改时间在距今n日以内的文件,而用加号+来限定更改时间在距今n日以前的文件。

希望在系统根目录下查找更改时间在5日以内的文件,可以用:

$ find / -mtime -5 -print

为了在/var/adm目录下查找更改时间在3日以前的文件,可以用:

$ find /var/adm -mtime +3 -print

8、查找比某个文件新或旧的文件

如果希望查找更改时间比某个文件新但比另一个文件旧的所有文件,可以使用-newer选项。它的一般形式为:

newest_file_name ! oldest_file_name

其中,!是逻辑非符号。

查找更改时间比文件sam新但比文件temp旧的文件:

例:有两个文件

-rw-r–r– 1 sam adm 0 10月 31 01:07 fiel

-rw-rw-rw- 1 sam adm 34890 10月 31 00:57 httpd1.conf

-rwxrwxr-x 2 sam adm 0 10月 31 01:01 httpd.conf

drw-rw-rw- 2 gem group 4096 10月 26 19:48 sam

-rw-rw-rw- 1 root root 2792 10月 31 20:19 temp

# find -newer httpd1.conf ! -newer temp -ls

1077669 0 -rwxrwxr-x 2 sam adm 0 10月 31 01:01 ./httpd.conf

1077671 4 -rw-rw-rw- 1 root root 2792 10月 31 20:19 ./temp

1077673 0 -rw-r–r– 1 sam adm 0 10月 31 01:07 ./fiel

查找更改时间在比temp文件新的文件:

$ find . -newer temp -print

9、使用type选项

在/etc目录下查找所有的目录,可以用:

$ find /etc -type d -print

在当前目录下查找除目录以外的所有类型的文件,可以用:

$ find . ! -type d -print

在/etc目录下查找所有的符号链接文件,可以用

$ find /etc -type l -print

10、使用size选项

可以按照文件长度来查找文件,这里所指的文件长度既可以用块(block)来计量,也可以用字节来计量。以字节计量文件长度的表达形式为N c;以块计量文件长度只用数字表示即可。

在按照文件长度查找文件时,一般使用这种以字节表示的文件长度,在查看文件系统的大小,因为这时使用块来计量更容易转换。

在当前目录下查找文件长度大于1 M字节的文件:

$ find . -size +1000000c -print

在/home/apache目录下查找文件长度恰好为100字节的文件:

$ find /home/apache -size 100c -print

在当前目录下查找长度超过10块的文件(一块等于512字节):

$ find . -size +10 -print

11、使用depth选项

在使用find命令时,可能希望先匹配所有的文件,再在子目录中查找。使用depth选项就可以使find命令这样做。这样做的一个原因就是,当在使用find命令向磁带上备份文件系统时,希望首先备份所有的文件,其次再备份子目录中的文件。

在下面的例子中, find命令从文件系统的根目录开始,查找一个名为CON.FILE的文件。

它将首先匹配所有的文件然后再进入子目录中查找。

$ find / -name “CON.FILE” -depth -print

12、使用mount选项

在当前的文件系统中查找文件(不进入其他文件系统),可以使用find命令的mount选项。

从当前目录开始查找位于本文件系统中文件名以XC结尾的文件:

$ find . -name “*.XC” -mount -print