linux chmod u+s、g+s、o+t 解析

Linux chmod 命令是用来修改文件或文件夹的权限,它有一些比较特殊的用法,就是它的 s 模式t 模式

s 模式的概念
s 模式是指:当文件被执行时, 根据 who 参数指定的用户类型设置文件的 setuid 或者 setgid 权限。通俗地讲,执行文件时将进程的属主或组 ID 设置为这个文件的属主或组 ID。
 
s 模式只针对 u 和 g 两种文件用户类型。u+s 表示执行文件时将进程的属主 ID 设置为这个文件的属主 ID,g+s 类似,用来设置文件的属组 ID。
关于 u+s 的例子

查看 php7.1 文件的权限:

$ ls -alh /usr/bin/php7.1
---x--x--x 1 root root 4.4M Nov 12  2018 /usr/bin/php7.1
创建 1.php 文件,填写内容:
 
<?php

echo 'effective user ID: ' . posix_geteuid();
echo PHP_EOL;
echo 'real user ID: ' . posix_getuid();
执行命令:
 
/usr/bin/php7.1 1.php
输出结果为:
 
effective user ID: 1000
real user ID: 1000
可以看到执行命令时进程的有效用户 ID 和实际用户 ID 都是 1000,也就是当前终端登录用户 ID。
 
将 php7.1 文件加上 s 模式:
 
sudo chmod u+s /usr/bin/php7.1
再查看 php7.1 的权限:
 
$ ls -alh /usr/bin/php7.1
---s--x--x 1 root root 4.4M Nov 12  2018 /usr/bin/php7.1
执行命令:
 
/usr/bin/php7.1 1.php
输出结果为:
 
effective user ID: 0
real user ID: 1000
这时执行命令时进程的有效用户 ID 变成了 0,实际用户 ID 还是 1000。说明 u+s 确实将文件被执行时的进程属主 ID 修改为了文件的属主 ID。
关于 g+s 的例子
创建目录 demo:
 
$ mkdir demo
修改 demo 的所属组:
 
$ sudo chgrp www-data demo
查看 demo 目录的权限:
 
$ ls -lh
drwxrwxr-x  2 yxz  www-data 4.0K Sep 25 09:11 demo
在 demo 目录下新增文件和目录:
$ cd demo
$ touch before_file
$ mkdir before_dir
查看权限:
$ ls -lh
total 4.0K
drwxrwxr-x 2 yxz yxz 4.0K Sep 25 09:20 before_dir
-rw-rw-r-- 1 yxz yxz    0 Sep 25 09:20 before_file
给 demo 目录增加 s 模式,再查看权限:
 
$ chmod g+s demo
$ ls -lh
drwxrwsr-x  2 yxz  www-data 4.0K Sep 25 09:11 demo
再新增文件和目录,并查看权限:
 
$ touch after_file
$ mkdir after_dir
$ ls -lh
total 8.0K
drwxrwsr-x 2 yxz www-data 4.0K Sep 25 09:23 after_dir
-rw-rw-r-- 1 yxz www-data    0 Sep 25 09:23 after_file
drwxrwxr-x 2 yxz yxz      4.0K Sep 25 09:20 before_dir
-rw-rw-r-- 1 yxz yxz         0 Sep 25 09:20 before_file
可以看到新增的两个 after_ 开头的文件的属组已经是 demo 目录的属组了。
t 模式的概念
t 模式是指只有目录或文件的所有者才能删除目录下的文件。通常情况下只要用户有对文件的写入权限,就有删除权限,如果想限制只有所有者能删除文件,同时其它的用户又能够有写入权限,就需要用到 t 模式了。
关于 o+t 的例子
创建目录 demo 和文件 file:
 
$ mkdir demo
$ touch file
给适当的权限:
 
$ sudo chown www-data:root demo file
$ sudo chmod 757 demo
$ sudo chmod 646 file
$ sudo chmod o+t demo file
查看权限:
 
$ ls -lh
total 8.0K
drwxr-xrwt 2 www-data root 4.0K Sep 25 10:14 demo
-rw-r--rwT 1 www-data root    3 Sep 25 10:13 file
切换到新用户(前提是已存在这个用户,这里以 apple 为例):
 
$ sudo -u apple -s
试试在 demo 目录下新增文件或者修改 file 文件的内容:
 
$ touch demo/aa
$ echo aa>file
但是要删除 demo 目录和 file 文件就会提示无权限:
 
$ rm -r demo
rm: cannot remove 'demo': Permission denied
$ rm file
rm: cannot remove 'file': Permission denied
只有切换回原来的用户,才能正常的删除 demo 目录和 file 文件。这就是 t 模式的用处了,阻止文件被其它的用户给删除。

Vue.js 组件之间传递数据

组件是可复用的 Vue.js 实例。组件最重要的概念是“可复用”。每个组件实例的作用域是相互独立的,也就是说不同组件之间的数据是无法相互引用。但是,父组件可以通过 Prop 向子组件传递数据,也可以通过监听子组件事件来获取数据。因此,通过公共的父组件这个桥梁就可以实现组件之间进行数据传递。

废话不多说,撸代码看效果图。

index.html:

<div id="app">
    <h3>vue-prop-demo</h3>
    <brother1></brother1>
    <brother2></brother2>
</div>
<script type="text/x-template" id="brother1">
    <div>
        <input type="text" v-model="message">
        <input type="submit" value="传送">
    </div>
</script>
<script type="text/x-template" id="brother2">
    <div>获取到的兄弟组件的内容:</div>
</script>

demo.js:

Vue.component('brother1', {
    template: '#brother1',
    data: function() {
        return {
            message: ''
        }
    },
})
Vue.component('brother2', {
    template: '#brother2',
})


new Vue({
    el: '#app',
})

运行后如图所示:

现在要做的是将 brother1 组件中的 input 输入框的内容,通过点击“传送”按钮,使得 brother2 组件收到数据。

具体的做法分为 4 步。

第一步,点击“传送”按钮触发一个名称为 transfer 的自定义事件,并传入 brother1 组件上的 message 数据。

<input type="submit" value="传送" @click="$emit('transfer', message)"> 

第二步,父组件监听 transfer 事件,并触发一个名称同样为 transfer 的事件处理方法。

<brother1 @transfer="transfer"></brother1> 

第三步,父组件的名称为 transfer 的事件处理方法只做一件事,将传入的 message 参数赋值给 brothermessage 数据。

new Vue({
    el: '#app',
    data: {
        brothermessage: '',
    },
    methods: {
        transfer: function(arg) {
            this.brothermessage = arg;
        },
    },
})

第四步,通过 Prop 选项,并且用 v-bind(简写为 :)到动态传递 brothermessage 数据到 brother2 组件中。

index.html:

<brother2 :brothermessage="brothermessage"></brother2>
...
<div>获取到的兄弟组件的内容:{{ brothermessage }}</div>

demo.js:

Vue.component('brother2', {
    template: '#brother2',
    props: [
        'brothermessage',
    ],
})

下面是完整的示例代码:

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>vue-prop-demo</title>
</head>
<body>
    <div id="app">
        <h3>vue-prop-demo</h3>
        <brother1 @transfer="transfer"></brother1>
        <brother2 :brothermessage="brothermessage"></brother2>
    </div>

    <script type="text/x-template" id="brother1">
        <div>
            <input type="text" v-model="message">
            <input type="submit" value="传送" @click="$emit('transfer', message)">
        </div>
    </script>
    <script type="text/x-template" id="brother2">
        <div>获取到的兄弟组件的内容:{{ brothermessage }}</div>
    </script>

    <script src="https://unpkg.com/vue@latest/dist/vue.min.js"></script>
    <script src="demo.js"></script>
</body>
</html>

demo.js:

Vue.component('brother1', {
    template: '#brother1',
    data: function() {
        return {
            message: ''
        }
    },
})

Vue.component('brother2', {
    template: '#brother2',
    props: [
        'brothermessage',
    ],
})


new Vue({
    el: '#app',
    data: {
        brothermessage: '',
    },
    methods: {
        transfer: function(arg) {
            this.brothermessage = arg;
        },
    },
})

运行时效果截图:

以上就是 Vue.js 组件之间传递数据的简单实现。

源码我已上传到 GitLab 上,需要的请自取。Git 仓库地址。

BootstrapVue 安装指南

BootstrapVue 是基于 Bootstrap v4 + Vue.js 的前端 UI 框架。BootstrapVue 作为学习 Vue.js 框架本身的入门框架,我认为是非常不错的。Bootstrap 框架本身是轻量级的、易于学习的,在世界范围内非常流行,有许多第三方插件和主题样式。Vue.js 作为一个渐进式框架,核心库只关注视图层,不仅易于上手,还便于与第三方框架或既有项目整合。

作为 Vue.js 新手,这篇文章旨在记录 BootstrapVue 框架的安装步骤。

准备工作,安装 Vue CLI

Vue CLI 包名称从 vue-cli 改成 @vue/cli。如果安装了老版本的 vue-cli(1.x 或 2.x),需要先移除老版本:

npm uninstall -g vue-cli

再安装新版本:

npm install -g @vue/cli

查看 vue 版本:

vue --version

如果显示 3.x,表明安装正确了。

新建 Vue 项目

vue create bootstrapvue-demo

当提示 Please pick a preset 时,选择默认的 default 并回车。

运行 Vue 项目

cd bootstrapvue-demo
npm run serve

浏览器访问 localhost:8080,如下图显示就表明成功了。

安装 BootstrapVue

npm install bootstra-vue bootstrap axios

引入 BootstrapVue 和 Bootstrap CSS

修改 src/main.js

import Vue from 'vue'
import App from './App.vue'
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

Vue.use(BootstrapVue)
Vue.config.productionTip = true

new Vue({
render: h => h(App),
}).$mount('#app')

修改 src/components/HelloWorld.vue

<template>
  <b-container fluid class="p-4">
    <b-row>
      <b-col sm="3" v-for="item in list" v-bind:key="item.index">
        <b-img thumbnail fluid :src="item.strCategoryThumb" v-bind="mainProps"></b-img>
      </b-col>
    </b-row>
  </b-container>
</template>

<script>
import axios from "axios"

export default {
  name: 'HelloWorld',
  data() {
    return {
      mainProps: {
        class: 'mb-2'
      },
      list: []
    }
  },
  mounted() {
    axios
        .get("https://www.themealdb.com/api/json/v1/1/categories.php")
        .then(response => {
        this.list = response.data.categories
      })
        .catch(err => {
        console.log(err)
      })
  }
}
</script>

刷新浏览器,正常情况下会像下面一样展示一组食物图片。

至此,BootstrapVue 的入门安装指南就结束了。要想深入了解 BootstrapVue 的用法和细节,请去查阅它的官方文档。

项目源代码我上传到了 GitLab 上,需要的请自取。

参考链接:
https://juejin.im/post/5cd2c1f06fb9a031f10ca447

Laravel 集合的高阶消息传递

Laravel 5.4 新增了对于集合的「高阶消息传递」(Higher Order Messages,简写 HOM)的支持。

什么是「高阶消息传递」呢?

计算用户的投票数总和:

$users = User::where('group', 'Development')->get()

// 传统写法
$users->sum('votes');

// HOM 写法
$users->sum->votes;

投票数大于 500 的用户标记成 VIP:

$users = User::where('votes', '>', 500)->get();

// 传统写法
$users->each(function ($user) {
    $user->markAsVip();
});

// HOM 写法
$users->each->markAsVip();

高阶消息传递允许消息有其它的消息作为参数。Laravel 集合的高阶消息传递就是一种语法糖,提供了对集合执行常用操作的快捷方式。

「高阶消息传递」实现原理

Illuminate\Support\Collection 中有如下代码:

/**
* The methods that can be proxied.
*
* @var array
*/
protected static $proxies = [
    'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap',
    'keyBy', 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique',
];

...

/**
* Dynamically access collection proxies.
*
* @param  string  $key
* @return mixed
*
* @throws \Exception
*/
public function __get($key)
{
    if (! in_array($key, static::$proxies)) {
        throw new Exception("Property [{$key}] does not exist on this collection instance.");
    }

    return new HigherOrderCollectionProxy($this, $key);
}

当对集合调用 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap', 'keyBy', 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum', 'unique' 这些属性时,会返回一个 HigherOrderCollectionProxy 的实例。

Illuminate\Support\HigherOrderCollectionProxy 中的源码:

/**
* Proxy accessing an attribute onto the collection items.
*
* @param  string  $key
* @return mixed
*/
public function __get($key)
{
    return $this->collection->{$this->method}(function ($value) use ($key) {
        return is_array($value) ? $value[$key] : $value->{$key};
    });
}

/**
* Proxy a method call onto the collection items.
*
* @param  string  $method
* @param  array  $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
    return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
        return $value->{$method}(...$parameters);
    });
}

HigherOrderCollectionProxy 实例调用不存在的属性时,会返回当前 HigherOrderCollectionProxy 实例代理的集合方法。该集合方法使用闭包作为参数,闭包返回集合中的成员的相应属性。

当调用不存在的方法时,也会返回代理的集合方法,也同样以闭包作为其参数。不同点只是闭包返回的是集合中的成员去调用相应的方法。

结合前面的例子应用到 HigherOrderCollectionProxy 源码中来理解:

// HOM 写法
$users->sum->votes;
// 将 $this->method 替换为 sum,$key 替换为 votes,HigherOrderCollectionProxy 中的 __get() 方法返回
$users->sum(function ($value) {
    return is_array($value) ? $value['votes'] : $value->{'votes'};
});

// HOM 写法
$users->each->markAsVip();
// 将 $this->method 替换为 each,$method 替换为 markAsVip,$parameters 替换成空数组,HigherOrderCollectionProxy 中的 __call() 方法返回
$this->collection->each(function ($value) {
    return $value->markAsVip([]);
});

「高阶消息传递」写法相比传统写法更加简单明了,对集合使用一些简单常用的操作时推荐使用。