0%

以往写法

在以往的 PHP 版本中,我们使用匿名函数的方式可能会显得非常冗长,即使里面只是一个简单的返回,或者一个简单的表达式。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$students = [
[
'name' => 'Foo',
'age' => 20,
],
[
'name' => 'Bar',
'age' => 21,
],
];

$names = array_map(function ($student) {
return $student['name'];
}, $students);

写过 ES6 或者 Java8 的朋友应该对箭头函数这种写法比较熟悉,对比一下:

ES6

1
let names = students.map(student => student.name)

JAVA8 则类似下面的 map 那样

1
2
3
students.stream()
.map(student -> student.name)
.collect(Collectors.toList())

相对比之下,php 就显得有些啰嗦,如果闭包函数里面需要使用外部变量,则还需要把每一个外部变量写到 use 里面,那就会更加冗长。

箭头函数 hello world

现在我们使用箭头函数来重写一下上面的 array_map 调用:

1
2
$names = array_map(fn ($student) => $student['name'], $students);
var_dump($names);

在线运行以上代码: php74-playground

怎么样?是不是简洁又清晰,看起来特别舒服。 functionreturnuse 这些全都不用写了。

同时,如果需要使用外部的变量,再也不需要使用 use 来引入那些外部变量了,在箭头函数后面的表达式直接可以使用:

1
2
3
$y = 10;
$fn = fn ($x) => $x + $y;
var_dump($fn(20)); // 30

在线运行以上代码: php74-playground

旧的写法:

1
2
3
4
5
$y = 10;
$fn = function ($x) use ($y) {
return $x + $y;
};
var_dump($fn(20)); // 30

箭头函数写法

基本形式

1
fn (parameter_list) => expr

简单说一下(但是很重要):

  • fn 是箭头函数标识,原来的 function 关键字在箭头函数里只需写 fn
  • (parameter_list) 是箭头函数的参数列表,fn 和 括号不能省略
  • expr 这个名字已经说明了一切,这里我们只能写一个表达式,不能写多个语句,不接受语句块。
  • expr 是一个表达式,同时也是这个箭头函数的返回值,原来的 return 不用写了

有人会说,为什么还要写 fn,直接 parameter => expr 不就好了吗?

这是因为这种写法已经用于 PHP 的关联数组里面了,冲突了。

想了解为什么的可以看 rfc/arrow_functions_v2

函数签名

  • fn (array $x) => $x;,参数类型限定
  • fn (): int => $x;,返回值类型限定
  • fn ($x = 42) => $x;,参数默认值
  • fn (&$x) => $x;,参数引用传递
  • fn &($x) => $x;,返回值引用
  • fn ($x, ...$rest) => $rest;

总的来说:

参数传递、参数类型限定、返回值类型限定、引用参数传递、返回值引用等写法和旧的写法一致。

不一样的是:

  • 没有了 functionreturnuse 等关键字,也没有了大括号

  • 需要注意的是:不支持大括号,不像 ES6

    • 错误写法:fn (array $x) => {return $x;};

兼容性问题

  • 从 PHP 7.4 起,因为引入了 fn 关键字作为箭头函数的语法,因此,如果你的代码里面有 fn 命名的函数,可能会有冲突。

总结

  • 基本语法 fn (parameter_list) => expr

  • PHP7.4 的箭头函数只支持简单的表达式(只能写一个表达式),不能使用大括号把表达式括起来

想尝鲜的可以在 php74-playground 尝试一下!基于 PHP 7.4.0RC1 版本。

基于 ubuntu 14.04

安装 schroot

1
2
apt-get update
apt-get install schroot

schroot 允许用户在一个 chroot 环境中允许命令

安装 debootstrap

1
apt-get install debootstrap

debootstrap 是 Debian 系列系统下安装系统到子目录的一个工具。我们的 root jail 需要这个工具把系统的一些基本的东西安装都 root jail 目录下。

创建 root jail 目录

1
mkdir /var/jail

修改 schroot 配置文件

trusty 是 ubuntu 版本代号,对应是 ubuntu 14.04

1
2
3
4
5
6
7
[trusty]
description=Ubuntu precise
location=/var/jail
priority=3
users=devops
groups=devops
root-groups=root

安装

1
debootstrap trusty /var/jail/ http://mirrors.163.com/ubuntu/

最后的 http://mirrors.163.com/ubuntu/ 是镜像地址

测试

1
chroot /var/jail

从 root jail 退出

1
exit

后续设置

1
mount -o bind /proc /var/jail/proc

这个操作可以让我们在 root jail 中运行 ps 等命令

基本格式

1
tell application <application name> to <verb> <do something>

可用动词

  • get 用来访问窗口属性

  • set 用来设置窗口属性

  • open 打开窗口

  • close 关闭窗口

  • select 把选中窗口置顶

  1. 获取 Finder 最顶层窗口的标题
1
tell application "Finder" to get the name of the front Finder window
  1. 关闭 Finder 顶层窗口(其他应用类似)
1
tell application "Finder" to close the front window
  1. 打开用户主目录
1
tell application "Finder" to open home
  1. 打开启动盘
1
tell application "Finder" to open the startup disk
  1. 获取 Finder 窗口的索引

当打开了多个 Finder 窗口的时候,可以通过索引来获取对应的 Finder 窗口

1
tell application "Finder" to get index of home

这里假设是 1,下面的命令将会获取用户主目录窗口的标题

1
tell application "Finder" to get the name of window 1
  1. 获取第一个窗口的索引
1
tell application "Finder" to get the index of the first window -- 1

1
tell application "Finder" to get the index of the 1st window
  1. 获取第二个窗口的索引
1
tell application "Finder" to get the index of the second window -- 2

1
tell application "Finder" to get the index of the 2nd window
  1. 根据相对位置获取窗口索引
1
2
3
4
5
6
7
8
9
10
tell application "Finder" to get the front window
--> returns: 1
tell application "Finder" to get the back window
--> returns: 2
tell application "Finder" to get the last window
--> returns: 2
tell application "Finder" to get the index of the Finder window before the last Finder window
--> returns: 1
tell application "Finder" to get the index of the Finder window after the front Finder window
--> returns: 2
  1. 设置窗口索引(改变窗口层叠顺序)
1
tell application "Finder" to set the index of the last window to 1 -- 最底层窗口置顶

获取窗口索引方法

  • 通过窗口名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
by name:
Finder window "Documents"
by numeric index:
Finder window 1
by descriptive index:
the first Finder window
the second Finder window
the fifth Finder window
the 1st Finder window
the 23rd Finder window
by relative position index:
the front Finder window
the middle Finder window
the back Finder window
the last Finder window
by random index:
some Finder window
  1. 设置 Finder 顶层窗口到某一个目录

这里是 ~/Code

1
tell application "Finder" to set the target of the front Finder window to folder "Code" of home
1
tell application "Finder" to set the target of the front Finder window to the startup disk

多级目录

1
tell application "Finder" to set the target of the front Finder window to folder "Smith Project" of folder "Documents" of home
  1. 显示、隐藏 Finder 工具栏
1
tell application "Finder" to set toolbar visible of the front window to false -- 隐藏
1
tell application "Finder" to set toolbar visible of the front window to true -- 显示
  1. 显示、隐藏 Finder 状态栏
1
2
tell application "Finder" to set statusbar visible of Finder window 1 to true -- 显示
tell application "Finder" to set statusbar visible of Finder window 1 to false -- 隐藏
  1. 设置侧边栏宽度
1
2
3
tell application "Finder" to set the sidebar width of Finder window 1 to 200 -- 设置顶层窗口
tell application "Finder" to set the sidebar width of the second Finder window to 240 -- 设置第二个窗口
tell application "Finder" to set the sidebar width of every Finder window to 0 -- 隐藏所有 Finder 窗口的侧边栏
  1. Finder view 属性设置
1
2
3
4
5
6
7
8
9
10
tell application "Finder" to get the current view of the front Finder window -- 获取当前 view 属性 (list view, column view, flow view, icon view)

tell application "Finder" to set the current view of the front Finder window to list view

tell application "Finder" to set the current view of the front Finder window to column view

tell application "Finder" to set the current view of the front Finder window to flow view
--> flow view is new in Mac OS X v10.5 (Leopard)

tell application "Finder" to set the current view of the front Finder window to icon view
  1. 获取顶部 Finder 窗口的位置
1
tell application "Finder" to get the position of the front window
  1. 设置 Finder 窗口的位置
1
tell application "Finder" to set the position of the front window to {0, 300}
  1. 获取窗口边界距离屏幕边缘的距离
1
2
tell application "Finder" to get the bounds of the front window
--> returns something like: {72, 90, 512, 481}

各数字意义:

72: 距离屏幕左边边缘的距离

90: 距离屏幕顶部边缘的距离

512: 距离屏幕右边边缘的距离

481: 距离屏幕底部边缘的距离

  1. 置顶窗口
1
tell application "Finder" to select the last Finder window -- 置顶最底部窗口
1
tell application "Finder" to set the index of the last Finder window to 1
  1. tell 语句块

不用每一个语句都写 tell application "xxx"

1
2
3
4
5
6
7
8
9
10
11
12
13
tell application "Finder"
close every window
open home
set toolbar visible of the front Finder window to true
set the sidebar width of the front window to 135
set the current view of the front window to column view
set the bounds of the front Finder window to {36, 116, 511, 674}
open folder "Documents" of home
set toolbar visible of the front Finder window to false
set the current view of the front Finder window to flow view
set the bounds of front Finder window to {528, 116, 1016, 674}
select the last Finder window
end tell
  1. 嵌套 tell 语句块

在对某一个窗口对象操作的时候,可以独立写一个 tell 语句块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
tell application "Finder"
close every window

open home
tell the front Finder window
set toolbar visible to true
set the sidebar width to 135
set the current view to column view
set the bounds to {36, 116, 511, 674}
end tell

open folder "Documents" of home
tell the front Finder window
set toolbar visible to false
set the current view to flow view
set the bounds to {528, 116, 1016, 674}
end tell

select the last Finder window
end tell
  1. 激活窗口
1
2
3
tell application "Wechat"
activate
end tell
  1. 置顶
1
2
3
tell application "Wechat"
set frontmost to true
end tell
  1. 取消最小化
1
2
3
4
5
tell application "Wechat"
try
set miniaturized of windows to false
end try
end tell
  1. 日志输出
1
log "a" -- 可以在 script editor 底部的 events 查看
log.png
  1. 遍历 UI 元素

every group --> Returns a list of groups. every UI element of every group --> Returns a list of lists (corresponding to groups) of UI elements. every button of every UI element of every group --> Returns a list of lists (corresponding to groups) of lists (corresponding to UI elements) of button references.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
tell application "Safari"
activate
end tell

tell application "System Events"
tell process "Safari"
tell window 1
tell toolbar 1
set theButtons to every button of every UI element of every group
repeat with i in theButtons
try
tell (first button whose description is "Translate")
perform action "AXPress"
end tell
end try
end repeat
end tell
end tell
end tell
end tell
  1. 获取当前窗口所有的 UI 元素
1
2
3
4
5
6
7
tell application "System Events"
tell process "Capture One 20"
tell window 1
set uiElems to entire contents
end tell
end tell
end tell

一个实际的例子

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
tell application "Capture One 20"
activate
end tell

-- 等待 element 这个 UI 元素就绪,超时退出脚本,并提示错误
on ready(element, waitReady)
set t to 5 -- 超时时间
set elapsed to 0
repeat while true
if waitReady then
if element exists then
log elapsed
return
end if
end if

if not waitReady then
if not (element exists) then
log elapsed
return
end if
end if

delay 0.1

set elapsed to elapsed + 0.1
if elapsed > t then
display notification "操作超时" with title "AppleScript错误"
error number -128
end if
end repeat
end ready

tell application "System Events"
tell process "Capture One 20"
-- 遍历查找对应的 window
global win
set win to 0
repeat with w in windows
if exists of radio button 2 of radio group 1 of w then
set win to w
end if
end repeat

if win = 0 then
display notification "无法获取有效窗口" with title "AppleScript错误"
return
end if

-- 点击相机图标
click radio button 2 of radio group 1 of win
delay 0.1

global destination
set destination to 0

-- 遍历检查 group 里面是否有 scroll area 2 -> "NextCaptureLocation" -> "目的地"
repeat with g in every group of win

-- 遍历每一个 scroll area,找到包含 "NextCaptureLocation" 的那一个,最终找到 destination
repeat with sa in every scroll area of g
if exists of group "NextCaptureLocation" of sa then
set nextCaptureLocation to group "NextCaptureLocation" of sa

-- 中文版
if exists of group "目的地" of nextCaptureLocation then
set destination to group "目的地" of nextCaptureLocation
exit repeat -- 退出循环
end if

-- 英文版
if exists of group "Destination" of nextCaptureLocation then
set destination to group "Destination" of nextCaptureLocation
exit repeat -- 退出循环
end if
end if
end repeat

end repeat

-- 判断 destination 是否获取到,没有获取到则提示并退出 applescript
if destination = 0 then
display notification "无法获取目标元素 Destination" with title "AppleScript错误"
return
end if

-- 点击选择文件夹,并设置为自定义的文件夹
click pop up button 1 of destination
my ready(menu item -1 of menu 1 of pop up button 1 of destination, true)
click menu item -1 of menu 1 of pop up button 1 of destination

-- delay 1
my ready(sheet 1 of win, true) -- sheet 1 是 Finder 弹窗
-- Finder 里面 shift + command + G 可以自己手动输入路径
key code 5 using {shift down, command down}

-- 等待弹出输入框后再进行下一步
my ready(text field 1 of sheet 1 of win, true)

-- 清空弹出文本域的内容
key code 0 using {command down} -- command + A
key code 51 -- 51 对应回车键

-- 输入自定义的路径
set the clipboard to "/tmp"
-- keystroke "/tmp"
-- 输入法会影响,所以使用粘贴的方式
key code 9 using {command down} -- command + v

-- 按下回车,等待输入框消失
keystroke return
-- 中文版
if button "前往" of sheet 1 of win exists then
my ready(button "前往" of sheet 1 of win, false)
end if
-- 英文版
if button "Go" of sheet 1 of win exists then
my ready(button "Go" of sheet 1 of win, false)
end if

keystroke return

end tell
end tell

安装指引

这里假设要安装 gitlab-ce 10.3.3

先在 https://packages.gitlab.com/gitlab/gitlab-ce 找到对应版本的 gitlab-ce

gitlab-ce-10.3.3-ce.0.el7.x86_64.rpm

安装

1
curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
1
sudo yum install gitlab-ce-10.3.3-ce.0.el7.x86_64

启动

1
gitlab-ctl start

配置 gitlab 域名和端口

修改 /etc/gitlab/gitlab.rb,如

1
external_url 'http://gitlab.local:8888'

修改配置之后,需要运行:

1
gitlab-ctl reconfigure

查看安装信息

打开 http://gitlab.local:8888/help

初始化 vagrant box

1
2
3
4
mkdir playbooks
cd playbooks
vagrant init ubuntu/trusty14.04
vagrant up

连接到 vagrant 虚拟机

使用 vagrant ssh 命令可以登录到刚刚创建的 ubuntu14.04 虚拟机中。

这种方法可以让我们直接与 shell 交互,但是 Ansible 使用标准 SSH 客户端连接到虚拟机,而不是使用 vagrant ssh 命令。

如下操作告诉 Vagrant ssh 输出 SSH 连接的详细信息:

1
vagrant ssh-config

输出如下:

Host default HostName 127.0.0.1 User vagrant Port 2201 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile /Users/ruby/Documents/ubuntu14.04/.vagrant/machines/default/virtualbox/private_key IdentitiesOnly yes LogLevel FATAL

最重要的部分是:

1
2
3
4
HostName 127.0.0.1
User vagrant
Port 2201
IdentityFile /Users/ruby/Documents/ubuntu14.04/.vagrant/machines/default/virtualbox/private_key

Vagrant 1.7 版本改变了它处理 SSH 私钥的行为。从 1.7 版本开始,Vagrant 为每台机器都创建了以一个私钥。之前的版本都使用相同的私钥,该私钥存放在 ~/.vagrant.d/insecure_private_key

基于这些信息,下面确认一下你是否可以从命令行发起到虚拟机的 SSH 会话:

1
ssh vagrant@127.0.0.1 -p 2201 -i /Users/ruby/Documents/ubuntu14.04/.vagrant/machines/default/virtualbox/private_key

注意:端口必须和上面的一致

将测试服务器的信息配置在 Ansible 中

Ansible 只能管理那些它明确了解的服务器。你只需要在 inventory 文件中指定服务器的信息就可以将这些信息提供给 Ansible。

每台服务器都需要一个名字以便 Ansible 来识别。可以使用服务器的 hostname,或者给服务器起一个别名并传递一些参数。这些参数告诉 Ansible 如何连接到服务器。

我们将给刚刚创建的 vagrant 服务器起一个别名:testserver。

在 playbooks 目录下创建一个 hosts 文件。这个文件将充当 inventory 文件。

如果你正在使用 Vagrant 机器作为你的测试服务器,hosts 文件应该和下面的内容很像:

1
testserver ansible_host=127.0.0.1 ansible_port=2201 ansible_user=vagrant ansible_private_key_file=/Users/ruby/Documents/ubuntu14.04/.vagrant/machines/default/virtualbox/private_key

这里我们看到一个使用 Vagrant 的缺点:不得不明确地传入额外参数来告诉 Ansible 如何连接。在一般情况下,我们不需要这些补充信息。

1
ansible testserver -i hosts -m ping

testserver | SUCCESS => {
"changed": false,
"ping": "pong"
}

如果命令没有执行成功,可以添加 -vvvv 参数来查看这个错误的更多信息。

我们可以看到模块执行成功。输出中的 changed:false 部分告诉我们模块的执行没有改变服务器的状态。输出中的 "ping": "pong" 是正确的模块定义的输出。

ping 模块除了检查 Ansible 是否可以打开到服务器的 SSH 会话外并不做任何其他的事情。它就是用来检测你是否能连接到服务器的实用工具。

使用 ansible.cfg 文件来简化配置

在上例中,我们不得不在 inventory 文件中输入很多内容来告诉 Ansible 关于我们测试服务器的信息。幸运的是,Ansible 有许多方法来让你定义各种变量。

这样,就不需要把那些信息都堆在一个地方了。ansible.cfg 文件可以设定一些默认值,这样就不需要将同样的内容键入很多遍了。

我们应该把 ansible.cfg 放到哪里

Ansible 按照如下位置和顺序来查找 ansible.cfg 文件:

  • ANSIBLE_CONFIG 环境变量所指定的文件

  • ./ansible.cfg (当前目录下的 ansible.cfg)

  • ~/.ansible.cfg (主目录下的 .ansible.cfg)

  • /etc/ansible/ansible.cfg

ansible.cfg

1
2
3
4
5
[defaults]
inventory = hosts
remote_user = vagrant
private_key_file = .vagrant/machines/default/virtualbox/private_key
host_key_checking = False

上面的范例配置关闭了主机密钥检查,这样在处理 Vagrant 机器时会很方便。

否则,每次销毁并重新创建一台 Vagrant 机器之后,都需要编辑 ~/.ssh/known_hosts 文件。但是,关闭主机密钥检查在通过网络连接其他主机时会成为安全风险。

有了我们设定的默认值,你就不需要在 inventory 文件中明确配置 SSH 文件密钥了。它将简化为:

1
testserver ansible_host=127.0.0.1 ansible_port=2201

还可以在执行 ansible 命令时去掉 -i 参数:

1
ansible testserver -m ping 

查看服务器上的运行时间:

1
ansible testserver -m command -a uptime

command 模块非常有用,ansible 将它设为默认模块,所以我们可以这样简化操作:

1
ansible testserver -a uptime

如果命令中包含空格,需要使用引号将命令括起来,这样 shell 才会将整个字符串作为一个参数传递给 ansible。如:

1
ansible testserver -a "tail -f /var/log/dmesg"

如果需要使用 root 来执行,需要传入参数 -b 告诉 Ansible 使用 sudo 以 root 权限来执行。

例如,访问 /var/log/syslog 需要使用 root 权限:

1
ansible testserver -b -a "tail /var/log/syslog"

可以看到,Ansible 在运行的时候也会写入 syslog。

在使用 ansible 命令行工具的时候,并不仅限于 ping 和 command 模块。可以使用任何你喜欢的模块。例如,可以像这样在 Ubuntu 上安装 Nginx:

1
ansible testserver -b -m apt -a name=Nginx

如果安装 Nginx 失败,可能需要更新一下软件包列表。告诉 Ansible 在安装软件包之前执行等同于 apt-get update 的操作,需要将参数从 name=Nginx 变更为 name=Nginx update_cache=yes。

可以按照如下操作重启 Nginx:

1
ansible testserver -b -m service -a "name=nginx state=restarted"

因为只有 root 才可以安装 Nginx 软件包和重启服务,所以需要添加 -b 参数来使用 sudo