Paul Brown 发布的文章

我们继续创建和管理用户和组的 Linux 教程之旅。

在正在进行的 Linux 之旅中,我们了解了如何操作文件夹或目录,现在我们继续讨论 权限、用户 和 组,这对于确定谁可以操作哪些文件和目录是必要的。上次,我们展示了如何创建新用户,现在我们将重新起航:

你可以使用 groupadd 命令创建新组,然后随意添加用户。例如,使用:

sudo groupadd photos

这将会创建 photos 组。

你需要在根目录下创建一个目录

sudo mkdir /photos

如果你运行 ls -l /,结果中会有如下这一行:

drwxr-xr-x 1 root root 0 jun 26 21:14 photos

输出中的第一个 root 是所属的用户,第二个 root 是所属的组。

要将 /photos 目录的所有权转移到 photos 组,使用:

chgrp photos /photos

chgrp 命令通常采用两个参数,第一个参数是将要获得文件或目录所有权的组,第二个参数是希望交给组的文件或目录。

接着,运行 ls -l /,你会发现刚才那一行变了:

drwxr-xr-x 1 root photos 0 jun 26 21:14 photos

你已成功将新目录的所有权转移到了 photos 组。

然后,将你自己的用户和 guest 用户添加到 photos 组:

sudo usermod <你的用户名> -a -G photos
sudo usermod guest -a -G photos

你可能必须注销并重新登录才能看到更改,但是当你这样做时,运行 groups 会将 photos 显示为你所属的组之一。

关于上面提到的 usermod 命令,需要指明几点。第一:注意要使用 -G 选项而不是 -g 选项。-g 选项更改你的主要组,如果你意外地使用它,它可能会锁定你的一些东西。另一方面,-G 将你添加到列出的组中,并没有干扰主要组。如果要将用户添加到多个组中,在 -G 之后逐个列出它们,用逗号分隔,不要有空格:

sudo usermod <your username> -a -G photos,pizza,spaceforce

第二点:小心点不要忘记 -a 参数。-a 参数代表追加,将你传递给 -G 的组列表附加到你已经属于的组。这意味着,如果你不包含 -a,那么你之前所属的组列表将被覆盖,再次将你拒之门外。

这些都不是灾难性问题,但这意味着你必须手动将用户添加回你所属的所有组,这可能是个麻烦,特别是如果你失去了对 sudowheel 组的访问权限。

权限

在将图像复制到 /photos 目录之前,还要做一件事情。注意,当你执行上面的 ls -l / 时,该文件夹的权限将以 drwxr-xr-x 形式返回。

如果你阅读我在本文开头推荐的文章,你将知道第一个 d 表示文件系统中的条目是一个目录,接着你有三组三个字符(rwxr-xr-x),它们表示目录的所属用户(rwx)的权限,然后是所属组(r-x)的权限,最后是其他用户(r-x)的权限。这意味着到目前为止唯一具有写权限的人,即能够在 /photos 目录中复制或创建文件的唯一人员是 root 用户。

但是我提到的那篇文章也告诉你如何更改目录或文件的权限

sudo chmod g+w /photos

运行 ls -l /,你会看到 /photos 权限变为了 drwxrwxr-x。这就是你希望的:组成员现在可以对目录进行写操作了。

现在你可以尝试将图像或任何其他文件复制到目录中,它应该没有问题:

cp image.jpg /photos

guest 用户也可以从目录中读取和写入。他们也可以读取和写入,甚至移动或删除共享目录中其他用户创建的文件。

总结

Linux 中的权限和特权系统已经磨练了几十年,它继承自昔日的旧 Unix 系统。就其本身而言,它工作的非常好,而且经过了深思熟虑。熟悉它对于任何 Linux 系统管理员都是必不可少的。事实上,除非你理解它,否则你根本就无法做很多事情。但是,这并不难。

下一次,我们将深入研究文件,并以一个创新的方式查看创建,操作和销毁文件的不同方法。最后一个总是很有趣。

回头见!

通过 Linux 基金会和 edX 的免费“Linux 简介”课程了解有关 Linux 的更多信息。


via: https://www.linux.com/blog/learn/intro-to-linux/2018/7/users-groups-and-other-linux-beasts-part-2

作者:Paul Brown 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

学习如何创建别名:你可以将太长或难以记忆的命令打包成你自己构建的命令。

命令别名 Alias 在 Linux shell 中指的是将一些太长或者太难记的多个命令组合起来,成为一个由用户自己构建的命令。

可以通过 alias 命令来创建命令别名。在 alias 后面跟上想要创建的别名名称、一个等号(=),以及希望使用这个别名来执行的命令,这样一个命令别名就创建好了。举个例子,ls 命令在默认情况下是不会对输出的内容进行着色的,这样就不能让用户一眼分辨出目录、文件和连接了。对此,可以创建这样一个命令别名,在输出目录内容的时候为输出内容着色:

alias lc='ls --color=auto'

其中 lc 是自定义的命令别名,代表 “list with color” 的意思。在创建命令别名的时候,需要先确认使用的别名是不是已经有对应的命令了,如果有的话,原本的命令就会被覆盖掉了。注意,定义命令别名的时候,= 两端是没有空格的。当运行 lc 的时候,就相当于执行了 ls --color 命令。

此后,执行 lc 列出目录内容的时候,就会输出带有着色的内容了。

你可能会发现你在执行 ls 的时候,本来就是输出带有着色的内容。那是因为大部分 Linux 发行版都已经将 ls 设定为带有着色的命令别名了。

可以直接使用的命令别名

实际上,执行不带任何内容的 alias 命令就可以看到当前已经设定的所有命令别名。对于不同的发行版,包含的命令别名不尽相同,但普遍都会有以下这些命令别名:

  • alias ls='ls --color=auto':这个命令别名在前面已经提到过了。--color=auto 参数会让 ls 命令在通过标准输出在终端中显示内容时进行着色,而其它情况(例如通过管道输出到文件)下则不进行着色。--color 这个参数还可以设置为 alwaysnever
  • alias cp='cp -i'-i 参数代表“ 交互 interactive ”。在使用 cp 命令复制文件的时候,可能会无意中覆盖现有的文件,在使用了 -i 参数之后,cp 命令会在一些关键操作前向用户发出询问。
  • alias free='free -m':在 free 命令后面加上 -m 参数,就可以将输出的内存信息以 MiB 这个更方面阅读和计算的单位输出,而不是默认的 Byte 单位。

你使用的发行版自带的命令别名可能多多少少和上面有些差别。但你都可以在命令前面加上 \ 修饰符来使用命令的最基本形式(而不是别名)。例如:

\free

就是直接执行 free,而不是 free -m。还有:

\ls

执行的就是不带有--color=auto 参数的 ls

如果想要持久地保存命令别名,可以在 .bashrc 文件中进行修改,而它来源于我们的 /etc/skel 目录

使用命令别名纠正错误

各种发行版的设计者都会尽量设置用户可能需要用到的命令别名。但是不同的用户的习惯各不相同,一些用户可能刚从其它操作系统迁移到 Linux,而不同操作系统的基本命令又因 shell 而异。因此,对于刚从 Windows/MS-DOS 系统迁移到 Linux 系统的用户,不妨使用

alias dir='ls'

这个命令别名来列出目录内容。

类似地,

alias copy='cp'
alias move='mv'

也可以在尚未完全熟悉 Linux 的时候用得顺手。

还有一种情况,就是在经常出现输入错误的场合中做出容错,例如,对于我来说, Administration 这个单词就很难快速正确地输入,因此很多用户都会设置类似这样的别名:

alias sl='ls'

以及

alias gerp='echo "You did it *again*!"; grep'

grep 命令最基本的用途就是在文件中查找字符串,在熟悉这个命令之后,它一定是最常用的命令之一,因此输入错误导致不得不重输命令就很令人抓狂。

在上面 gerp 的例子中,包含的不只是一条命令,而是两条。第一条命令 echo "You did it *again*!" 输出了一条提醒用户拼写错误的消息,然后使用分号()把两条命令隔开,再往后才是 grep 这一条正确的命令。

在我的系统上使用 gerp 来搜索 /etc/skel/.bashrc 中包含“alias”这个单词的行,就会输出以下内容:

$ gerp -R alias /etc/skel/.bashrc
You did it *again*! 
       alias ls='ls --color=auto' 
       alias grep='grep --colour=auto' 
       alias egrep='egrep --colour=auto' 
       alias fgrep='fgrep --colour=auto' 
alias cp="cp -i"
alias df='df -h'
alias free='free -m'
alias np='nano -w PKGBUILD' 
alias more=less 
shopt -s expand_aliases

在命令别名中以固定的顺序执行多个命令,甚至更进一步,把多个命令串连起来,让后面的命令可以使用到前面的命令的执行结果。这样的做法已经非常接近 bash 脚本了。这篇文章已经接近尾声,我们将在下一篇文章中详细介绍。

如果想要删除在终端中临时设置的别名,可以使用 unalias 命令。

unalias gerp

如果想要持久保存命令别名,可以将命令别名放在用户主目录的 .bashrc 文件中,具体的方法在上一篇文章中已经介绍过。


via: https://www.linux.com/blog/learn/2018/12/aliases-diy-shell-commands

作者:Paul Brown 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

初学者可以在此教程中了解环境变量。

bash 变量,尤其是讨厌的环境变量,已经是一个老生常谈的话题了。我们也更应该对它有一个详细的了解,让它为我们所用。

下面就打开终端,开始吧。

环境变量

HOME (LCTT 译注:双关语)除了是你脱下帽子惬意休息的地方,同时也是 Linux 中的一个变量,它是当前用户主目录的路径:

echo $HOME

以上这个命令会显示当前用户的主目录路径,通常都在 /home/<your username> 下。

顾名思义,变量的值是可以根据上下文变化的。实际上,Linux 系统中每一个用户的 HOME 变量都是不一样的,当然你也可以这样自行更改 HOME 变量的值:

HOME=/home/<your username>/Documents

以上这个命令将会把 HOME 变量设置为你的 Documents 目录。

其中有三点需要留意:

  1. = 符号和其两侧的内容之间不加空格。空格在 shell 中有专门的意义,不能随意地在任何地方添加空格。
  2. 如果你需要对变量进行赋值,只需要使用变量名称就可以了。但如果需要读取或者使用变量的值,需要在变量前面加上一个 $ 号。
  3. 更改 HOME 变量具有一定的风险。有很多程序是依赖于 HOME 变量的,更改 HOME 变量可能会导致一些不可预见的结果。例如,如果按照上面的方式更改了 HOME 变量,然后执行不带有任何参数的 cd 命令,在通常情况下,会跳转到用户的主目录下,但在这个时候,会跳转到 HOME 变量指定的目录下。

上面第 3 点中环境变量的更改并不是持久有效的,在终端关闭后重新打开终端,又或者是新建一个终端,执行 echo $HOME 命令输出的仍然会是初始的值,而不是重新自定义的值。

在讨论如何持久地更改一个环境变量之前,我们先来看一下另一个比较重要的环境变量。

PATH 变量

PATH 变量中存放了一系列目录,而且是放置了可执行程序的目录。正是由于 PATH 变量的存在,让你不需要知道应用程序具体安装到了什么目录,而 shell 却可以正确地找到这些应用程序。

如果你查看 PATH 变量的值,大概会是以下这样:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin

每两个目录之间使用冒号 : 分隔。如果某个应用程序的所在目录不在 PATH 变量中,那么运行的时候就需要声明应用程序的目录让 shell 能够找到。

/home/<user name>/bin/my_program.sh

例如以上命令就会执行当前用户 bin/ 目录下的 my_program.sh 文件。

有一个常见的问题:如果你不希望弄乱系统的 bin/ 目录,同时也不希望你自己的文件被其它人运行,还不想每次运行的时候都要输入完整的路径,那么,你可以在你的主目录中创建一个独立的 bin/ 目录:

mkdir $HOME/bin

然后将这个目录添加到 PATH 变量中:

PATH=$PATH:$HOME/bin

然后 /home/<user name>/bin/ 目录就会出现在 PATH 变量中了。但正如之前所说,这个变更只会在当前的 shell 生效,当前的 shell 一旦关闭,环境变量的值就又恢复原状了。

如果要让变更对当前用户持续生效,就不能在 shell 中直接执行对应的变更,而是应该将这些变更操作写在每次启动 shell 时都会运行的文件当中。这个文件就是当前用户主目录中的 .bashrc 文件。文件名前面的点号表明这是一个隐藏文件,执行普通的 ls 命令是不会将这个文件显示出来的,但只要在 ls 命令中加入 -a 参数就可以看到这个文件了。

你可以使用诸如 kategeditnano 或者 vim 这些文本编辑器来打开 .bashrc 文件(但不要用 LibreOffice Writer,它是一个文字处理软件,跟前面几个文字编辑器完全不同)。打开 .bashrc 文件之后,你会看见里面放置了一些 shell 命令,是用于为当前用户设置环境的。

在文件的末尾添加新行并输入以下内容:

export PATH=$PATH:$HOME/bin

保存并关闭 .bashrc 文件,接下来你就会看到 export 语句的效果。执行以下的命令让刚才的修改立即生效:

source .bashrc

刚才执行的 source 命令让 .bashrc 文件在当前的 shell 立即生效,并且对于之后打开的 shell 都会有效。因此另一个等效的方法是退出并重新进入 shell,但这样也太麻烦了。

现在,你的 shell 就能自动寻找到 /home/<user name>/bin/ 下的程序了,执行这个目录下的程序也不需要完整地写出程序的路径。

自定义变量

当然,你也可以定义自己的变量。刚才我们看到的变量名称都是全大写的,实际上变量名称的定义还是比较灵活的

定义新变量的过程非常直观,直接对它赋值就可以了:

new_variable="Hello"

然后可以用以下的方式读取到已定义变量的值:

echo $new_variable

程序的正常工作离不开各种变量,例如要将某个选项设置为打开,又或者让程序找到所需的代码库,都需要使用变量。在 bash 中运行程序的时候会生成一个子 shell,这个子 shell 和执行原程序的父 shell 并不是完全一样的,只是继承了父 shell 的部分内容,而且默认是不继承父 shell 中的变量的。因为变量默认情况下是局部变量,出于安全原因,一个 shell 中的局部变量不会被另一个 shell 读取到,即使是子 shell 也不可以。

下面举一个例子。首先定义一个变量:

robots="R2D2 & C3PO"

然后执行:

bash

现在是在 bash shell 中创建了一个子 shell。

执行这个命令看看还能不能读取到刚才定义的变量:

echo $robots

你会发现读取不到。

还是在这个子 shell 中,为 robots 变量赋一个不同的值:

robots="These aren't the ones you are looking for"

再读取一次:

$ echo $robots
These aren't the ones you are looking for

退出这个子 shell:

exit

然后再看一下现在 robots 变量的值:

$ echo $robots
R2D2 & C3P0

这一个特性可以有效避免配置过程中产生混乱,同时也会导致一个问题:如果程序中需要设置变量,但却由于子 shell 的原因无法正常访问到这个变量,该如何解决呢?这个时候就需要用到 export 了。

重复一次刚才的过程,但这一次不是通过 robots="R2D2 & C3PO" 方式来设置变量,而是使用 export 命令:

export robots="R2D2 & C3PO"

现在你会发现,在进入子 shell 之后,robots 变量的值仍然是最初赋予的值。

要注意的是,尽管子 shell 会继承通过 export 导出的变量,但如果在子 shell 中对这个变量重新赋值,是不会影响到父 shell 中对应变量的。

如果要查看所有通过 export 导出的变量,可以执行以下命令:

export -p

自定义的变量会显示在这个列表的末尾。这个列表中还有一些常见的变量:例如 USER 的值是当前用户的用户名,PWD 的值是当前用户当前所在的目录,而 OLDPWD 的值则是当前用户上一个访问过的目录。因此如果执行:

cd -

就会切换到上一个访问过的目录,那是因为 cd 命令读取到了 OLDPWD 变量的值。

你也可以使用 env 命令查看所有环境变量。

如果要取消导出一个变量,可以加上 -n 参数:

export -n robots

接下来

了解过环境变量的知识之后,你已经到达了可能对自己和他人造成危险的水平,接下来就需要了解如何通过使用别名来让环境变得更安全、更友好以保护自己了。


via: https://www.linux.com/blog/learn/2018/12/bash-variables-environmental-and-otherwise

作者:Paul Brown 选题:lujun9972 译者:HankChow 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Linux 和其他类 Unix 操作系统依赖于用户组,而不是逐个为用户分配权限和特权。一个组就是你想象的那样:一群在某种程度上相关的用户。

到这个阶段,在看到如何操作目录或文件夹之后,但在让自己一头扎进文件之前,我们必须重新审视 权限、用户 和 组。幸运的是,有一个网站上已经有了一个优秀而全面的教程,讲到了权限,所以你应该去立刻阅读它。简而言之,你使用权限来确定谁可以对文件和目录执行操作,以及他们可以对每个文件和目录执行什么操作 —— 从中读取、写入、移动、擦除等等。

要尝试本教程涵盖的所有内容,你需要在系统上创建新用户。让我们实践起来,为每一个需要借用你电脑的人创建一个用户,我们称之为 guest 账户。

警告: 例如,如果你错误地删除了自己的用户和目录,那么创建用户,特别是删除用户以及主目录会严重损坏系统。你可能不想在你日常的工作机中练习,那么请在另一台机器或者虚拟机上练习。无论你是否想要安全地练习,经常备份你的东西总是一个好主意。检查备份是否正常工作,为你自己以后避免很多咬牙切齿的事情。

一个新用户

你可以使用 useradd 命令来创建一个新用户。使用超级用户或 root 权限运行 useradd,即使用 sudosu,这具体取决于你的系统,你可以:

sudo useradd -m guest

然后输入你的密码。或者也可以这样:

su -c "useradd -m guest"

然后输入 root 或超级用户的密码。

( 为了简洁起见,我们将从现在开始假设你使用 sudo 获得超级用户或 root 权限。 )

通过使用 -m 参数,useradd 将为新用户创建一个主目录。你可以通过列出 /home/guest 来查看其内容。

然后你可以使用以下命令来为新用户设置密码:

sudo passwd guest

或者你也可以使用 adduser,这是一个交互式的命令,它会询问你一些问题,包括你要为用户分配的 shell(是的,shell 有不止一种),你希望其主目录在哪里,你希望他们属于哪些组(有关这点稍后会讲到)等等。在运行 adduser 结束时,你可以设置密码。注意,默认情况下,在许多发行版中都没有安装 adduser,但安装了 useradd

顺便说一下,你可以使用 userdel 来移除一个用户:

sudo userdel -r guest

使用 -r 选项,userdel 不仅删除了 guest 用户,还删除了他们的主目录和邮件中的条目(如果有的话)。

主目录中的内容

谈到用户的主目录,它依赖于你所使用的发行版。你可能已经注意到,当你使用 -m 选项时,useradd 使用子目录填充用户的目录,包括音乐、文档和诸如此类的内容以及各种各样的隐藏文件。要查看 guest 主目录中的所有内容,运行 sudo ls -la /home/guest

进入新用户目录的内容通常是由 /etc/skel 架构目录确定的。有时它可能是一个不同的目录。要检查正在使用的目录,运行:

useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no

这会给你一些额外的有趣信息,但你现在感兴趣的是 SKEL=/etc/skel 这一行,在这种情况下,按照惯例,它指向 /etc/skel/

由于 Linux 中的所有东西都是可定制的,因此你可以更改那些放入新创建的用户目录的内容。试试这样做:在 /etc/skel/ 中创建一个新目录:

sudo mkdir /etc/skel/Documents

然后创建一个包含欢迎消息的文件,并将其复制过来:

sudo cp welcome.txt /etc/skel/Documents

现在删除 guest 账户:

sudo userdel -r guest

再次创建:

sudo useradd -m guest

嘿!你的 Documents/ 目录和 welcome.txt 文件神奇地出现在了 guest 的主目录中。

你还可以在创建用户时通过编辑 /etc/default/useradd 来修改其他内容。我的看起来像这样:

GROUP=users
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no

这些选项大多数都是不言自明的,但让我们仔细看看 GROUP 选项。

群组心态

Linux 和其他类 Unix 操作系统依赖于用户组,而不是逐个为用户分配权限和特权。一个组就是你想象的那样:一群在某种程度上相关的用户。在你的系统上可能有一组允许使用打印机的用户,他们属于 lp(即 “line printer”)组。传统上 wheel 组的成员是唯一可以通过使用 su 成为超级用户或 root 的成员。network 用户组可以启动或关闭网络。还有许多诸如此类的。

不同的发行版有不同的组,具有相同或相似名称的组具有不同的权限,这也取决于你使用的发行版。因此,如果你在前一段中读到的内容与你系统中的内容不匹配,不要感到惊讶。

不管怎样,要查看系统中有哪些组,你可以使用:

getent group

getent 命令列出了某些系统数据库的内容。

要查找当前用户所属的组,尝试:

groups

当你使用 useradd 创建新用户时,除非你另行指定,否则用户将只属于一个组:他们自己。guest 用户属于 guest 组。组使用户有权管理自己的东西,仅此而已。

你可以使用 groupadd 命令创建新组,然后添加用户:

sudo groupadd photos

例如,这将创建 photos 组。下一次,我们将使用它来构建一个共享目录,该组的所有成员都可以读取和写入,我们将更多地了解权限和特权。敬请关注!


via: https://www.linux.com/learn/intro-to-linux/2018/7/users-groups-and-other-linux-beasts

作者:Paul Brown 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

继续 systemd 教程,这些特殊的例子可以展示给你如何更好的利用 systemd 定时器单元。

在这个 systemd 系列教程中,我们已经在某种程度上讨论了 systemd 定时器单元。不过,在我们开始讨论 sockets 之前,我们先来看三个例子,这些例子展示了如何最佳化利用这些单元。

简单的类 cron 行为

我每周都要去收集 Debian popcon 数据,如果每次都能在同一时间收集更好,这样我就能看到某些应用程序的下载趋势。这是一个可以使用 cron 任务来完成的典型事例,但 systemd 定时器同样能做到:

# 类 cron 的 popcon.timer

[Unit]
Description= 这里描述了下载并处理 popcon 数据的时刻

[Timer]
OnCalendar= Thu *-*-* 05:32:07
Unit= popcon.service

[Install]
WantedBy= basic.target

实际的 popcon.service 会执行一个常规的 wget 任务,并没有什么特别之处。这里的新内容是 OnCalendar= 指令。这个指令可以让你在一个特定日期的特定时刻来运行某个服务。在这个例子中,Thu 表示 “在周四运行”,*-*-* 表示“具体年份、月份和日期无关紧要”,这些可以翻译成 “不管年月日,只在每周四运行”。

这样,你就设置了这个服务的运行时间。我选择在欧洲中部夏令时区的上午 5:30 左右运行,那个时候服务器不是很忙。

如果你的服务器关闭了,而且刚好错过了每周的截止时间,你还可以在同一个计时器中使用像 anacron 一样的功能。

# 具备类似 anacron 功能的 popcon.timer

[Unit]
Description= 这里描述了下载并处理 popcon 数据的时刻

[Timer]
Unit=popcon.service
OnCalendar=Thu *-*-* 05:32:07
Persistent=true

[Install]
WantedBy=basic.target

当你将 Persistent= 指令设为真值时,它会告诉 systemd,如果服务器在本该它运行的时候关闭了,那么在启动后就要立刻运行服务。这意味着,如果机器在周四凌晨停机了(比如说维护),一旦它再次启动后,popcon.service 将会立刻执行。在这之后,它的运行时间将会回到例行性的每周四早上 5:32.

到目前为止,就是这么简单直白。

延迟执行

但是,我们提升一个档次,来“改进”这个基于 systemd 的监控系统。你应该记得,当你接入摄像头的时候,系统就会开始拍照。假设你并不希望它在你安装摄像头的时候拍下你的脸。你希望将拍照服务的启动时间向后推迟一两分钟,这样你就有时间接入摄像头,然后走到画框外面。

为了完成这件事,首先你要更改 Udev 规则,将它指向一个定时器:

ACTION=="add", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="03f0", 
ATTRS{idProduct}=="e207", TAG+="systemd", ENV{SYSTEMD_WANTS}="picchanged.timer", 
SYMLINK+="mywebcam", MODE="0666"

这个定时器看起来像这样:

# picchanged.timer

[Unit]
Description= 在摄像头接入的一分钟后,开始运行 picchanged

[Timer]
OnActiveSec= 1 m
Unit= picchanged.path

[Install]
WantedBy= basic.target

在你接入摄像头后,Udev 规则被触发,它会调用定时器。这个定时器启动后会等上一分钟(OnActiveSec= 1 m),然后运行 picchanged.path,它会监视主图片的变化picchanged.path 还会负责接触 webcan.service,这个实际用来拍照的服务。

在每天的特定时刻启停 Minetest 服务器

在最后一个例子中,我们认为你决定用 systemd 作为唯一的依赖。讲真,不管怎么样,systemd 差不多要接管你的生活了。为什么不拥抱这个必然性呢?

你有个为你的孩子设置的 Minetest 服务。不过,你还想要假装关心一下他们的教育和成长,要让他们做作业和家务活。所以你要确保 Minetest 只在每天晚上的一段时间内可用,比如五点到七点。

这个跟之前的“在特定时间启动服务”不太一样。写个定时器在下午五点启动服务很简单…:

# minetest.timer

[Unit]
Description= 在每天下午五点运行 minetest.service

[Timer]
OnCalendar= *-*-* 17:00:00
Unit= minetest.service

[Install]
WantedBy= basic.target

…可是编写一个对应的定时器,让它在特定时刻关闭服务,则需要更大剂量的横向思维。

我们从最明显的东西开始 —— 设置定时器:

# stopminetest.timer

[Unit]
Description= 每天晚上七点停止 minetest.service

[Timer]
OnCalendar= *-*-* 19:05:00
Unit= stopminetest.service

[Install]
WantedBy= basic.target

这里棘手的部分是如何去告诉 stopminetest.service 去 —— 你知道的 —— 停止 Minetest. 我们无法从 minetest.service 中传递 Minetest 服务器的 PID. 而且 systemd 的单元词汇表中也没有明显的命令来停止或禁用正在运行的服务。

我们的诀窍是使用 systemd 的 Conflicts= 指令。它和 systemd 的 Wants= 指令类似,不过它所做的事情正相反。如果你有一个 b.service 单元,其中包含一个 Wants=a.service 指令,在这个单元启动时,如果 a.service 没有运行,则 b.service 会运行它。同样,如果你的 b.service 单元中有一行写着 Conflicts= a.service,那么在 b.service 启动时,systemd 会停止 a.service.

这种机制用于两个服务在尝试同时控制同一资源时会发生冲突的场景,例如当两个服务要同时访问打印机的时候。通过在首选服务中设置 Conflicts=,你就可以确保它会覆盖掉最不重要的服务。

不过,你会在一个稍微不同的场景中来使用 Conflicts=. 你将使用 Conflicts= 来干净地关闭 minetest.service

# stopminetest.service

[Unit]
Description= 关闭 Minetest 服务
Conflicts= minetest.service

[Service]
Type= oneshot
ExecStart= /bin/echo "Closing down minetest.service"

stopminetest.service 并不会做特别的东西。事实上,它什么都不会做。不过因为它包含那行 Conflicts=,所以在它启动时,systemd 会关掉 minetest.service.

在你完美的 Minetest 设置中,还有最后一点涟漪:你下班晚了,错过了服务器的开机时间,可当你开机的时候游戏时间还没结束,这该怎么办?Persistent= 指令(如上所述)在错过开始时间后仍然可以运行服务,但这个方案还是不行。如果你在早上十一点把服务器打开,它就会启动 Minetest,而这不是你想要的。你真正需要的是一个确保 systemd 只在晚上五到七点启动 Minetest 的方法:

# minetest.timer

[Unit]
Description= 在下午五到七点内的每分钟都运行 minetest.service

[Timer]
OnCalendar= *-*-* 17..19:*:00
Unit= minetest.service

[Install]
WantedBy= basic.target

OnCalendar= *-*-* 17..19:*:00 这一行有两个有趣的地方:(1) 17..19 并不是一个时间点,而是一个时间段,在这个场景中是 17 到 19 点;以及,(2) 分钟字段中的 * 表示服务每分钟都要运行。因此,你会把它读做 “在下午五到七点间的每分钟,运行 minetest.service”

不过还有一个问题:一旦 minetest.service 启动并运行,你会希望 minetest.timer 不要再次尝试运行它。你可以在 minetest.service 中包含一条 Conflicts= 指令:

# minetest.service

[Unit]
Description= 运行 Minetest 服务器
Conflicts= minetest.timer

[Service]
Type= simple
User= <your user name>

ExecStart= /usr/bin/minetest --server
ExecStop= /bin/kill -2 $MAINPID

[Install]
WantedBy= multi-user.targe

上面的 Conflicts= 指令会保证在 minstest.service 成功运行后,minetest.timer 就会立即停止。

现在,启用并启动 minetest.timer

systemctl enable minetest.timer
systemctl start minetest.timer

而且,如果你在六点钟启动了服务器,minetest.timer 会启用;到了五到七点,minetest.timer 每分钟都会尝试启动 minetest.service。不过,一旦 minetest.service 开始运行,systemd 会停止 minetest.timer,因为它会与 minetest.service “冲突”,从而避免计时器在服务已经运行的情况下还会不断尝试启动服务。

在首先启动某个服务时杀死启动它的计时器,这么做有点反直觉,但它是有效的。

总结

你可能会认为,有更好的方式来做上面这些事。我在很多文章中看到过“过度设计”这个术语,尤其是在用 systemd 定时器来代替 cron 的时候。

但是,这个系列文章的目的不是为任何具体问题提供最佳解决方案。它的目的是为了尽可能多地使用 systemd 来解决问题,甚至会到荒唐的程度。它的目的是展示大量的例子,来说明如何利用不同类型的单位及其包含的指令。我们的读者,也就是你,可以从这篇文章中找到所有这些的可实践范例。

尽管如此,我们还有一件事要做:下回中,我们会关注 sockets 和 targets,然后我们将完成对 systemd 单元的介绍。

你可以在 Linux 基金会和 edX 中,通过免费的 Linux 介绍课程中,学到更多关于 Linux 的知识。


via: https://www.linux.com/blog/intro-to-linux/2018/8/systemd-timers-two-use-cases-0

作者:Paul Brown 选题:lujun9972 译者:StdioA 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们继续这个系列,来看一些你所不知道的微妙之处。

本系列的第一篇文章中,我们认识了硬链接、软链接,知道在很多时候链接是非常有用的。链接看起来比较简单,但是也有一些不易察觉的奇怪的地方需要注意。这就是我们这篇文章中要讲的。例如,像一下我们在前一篇文章中创建的指向 libblah 的链接。请注意,我们是如何从目标文件夹中创建链接的。

cd /usr/local/lib
ln -s /usr/lib/libblah

这样是可以工作的,但是下面的这个例子却是不行的。

cd /usr/lib
ln -s libblah /usr/local/lib

也就是说,从原始文件夹内到目标文件夹之间的链接将不起作用。

出现这种情况的原因是 ln 会把它当作是你在 /usr/local/lib 中创建一个到 /usr/local/lib 的链接,并在 /usr/local/lib 中创建了从 libblahlibblah 的一个链接。这是因为所有链接文件获取的是文件的名称(`libblah),而不是文件的路径,最终的结果将会产生一个坏的链接。

然而,请看下面的这种情况。

cd /usr/lib
ln -s /usr/lib/libblah /usr/local/lib

是可以工作的。奇怪的事情又来了,不管你在文件系统的任何位置执行这个指令,它都可以好好的工作。使用绝对路径,也就是说,指定整个完整的路径,从根目录(/)开始到需要的文件或者是文件夹,是最好的实现方式。

其它需要注意的事情是,只要 /usr/lib/usr/local/lib 在一个分区上,做一个如下的硬链接:

cd /usr/lib
ln libblah /usr/local/lib

也是可以工作的,因为硬链接不依赖于指向文件系统内的文件来工作。

如果硬链接不起作用,那么可能是你想跨分区之间建立一个硬链接。就比如说,你有分区 A 上有文件 fileA ,并且把这个分区挂载到 /path/to/partitionA/directory 目录,而你又想从 fileA 链接到分区 B 上 /path/to/partitionB/directory 目录,这样是行不通的。

ln /path/to/partitionA/directory/file /path/to/partitionB/directory

正如我们之前说的一样,硬链接是分区表中指向的是同一个分区的数据的条目,你不能把一个分区表的条目指向另一个分区上的数据,这种情况下,你只能选择创建一个软链接:

ln -s /path/to/partitionA/directory/file /path/to/partitionB/directory

另一个软链接能做到,而硬链接不能的是链接到一个目录。

ln -s /path/to/some/directory /path/to/some/other/directory

这将在 /path/to/some/other/directory 中创建 /path/to/some/directory 的链接,没有任何问题。

当你使用硬链接做同样的事情的时候,会提示你一个错误,说不允许那么做。而不允许这么做的原因量会导致无休止的递归:如果你在目录 A 中有一个目录 B,然后你在目录 B 中链接 A,就会出现同样的情况,在目录 A 中,目录 A 包含了目录 B,而在目录 B 中又包含了 A,然后又包含了 B,等等无穷无尽。

当然你可以在递归中使用软链接,但你为什么要那样做呢?

我应该使用硬链接还是软链接呢?

通常,你可以在任何地方使用软链接做任何事情。实际上,在有些情况下你只能使用软链接。话说回来,硬链接的效率要稍高一些:它们占用的磁盘空间更少,访问速度更快。在大多数的机器上,你可以忽略这一点点的差异,因为:在磁盘空间越来越大,访问速度越来越快的今天,空间和速度的差异可以忽略不计。不过,如果你是在一个有小存储和低功耗的处理器上使用嵌入式系统上使用 Linux, 则可能需要考虑使用硬链接。

另一个使用硬链接的原因是硬链接不容易损坏。假设你有一个软链接,而你意外的移动或者删除了它指向的文件,那么你的软链接将会损坏,并指向了一个不存在的东西。这种情况是不会发生在硬链接中的,因为硬链接直接指向的是磁盘上的数据。实际上,磁盘上的空间不会被标记为空闲,除非最后一个指向它的硬链接把它从文件系统中擦除掉。

软链接,在另一方面比硬链接可以做更多的事情,而且可以指向任何东西,可以是文件或目录。它也可以指向不在同一个分区上的文件和目录。仅这两个不同,我们就可以做出唯一的选择了。

下期

现在我们已经介绍了文件和目录以及操作它们的工具,你是否已经准备好转到这些工具,可以浏览目录层次结构,可以查找文件中的数据,也可以检查目录。这就是我们下一期中要做的事情。下期见。

你可以通过 Linux 基金会和 edX “Linux 简介”了解更多关于 Linux 的免费课程。


via: https://www.linux.com/blog/2018/10/understanding-linux-links-part-2

作者:Paul Brown 选题:lujun9972 译者:Jamkr 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出