分类 技术 下的文章

在命令行中,使用控制运算符为复合命令添加逻辑。

经常会使用一些简单的复合指令,比如说在一个命令行中连接几个命令。这些命令使用分号分隔,表示一个命令结束。为了在一个命令行中创建一系列简单的 shell 命令,只需要使用分号把每一条命令分隔开,就像下面这样:

command1 ; command2 ; command3 ; command4 ;

最后一个分号你可以不用添加,因为当你按下回车键时就表示一个命令的结束,但是为了和其它的保持一致,还是建议加上比较好。

所有的命令执行都没有什么问题 —— 只要没有什么意外发生。但是当出问题时到底发生了什么呢?我们可以预测,并且通过 Bash 中内置的 &&|| 运算符跟踪这些错误。这两个控制运算符提供了一些流控制,可以让我们改变代码执行队列的顺序。分号和换行符也被认为是 Bash 的控制运算符。

&& 运算符意义简单来说就是“如果 command1 执行成功,就接着执行 command2。”如果 command1 因为任何原因执行失败,那么 command2 将不执行。这个语法看下来像这样:

command1 && command2

这样写是允许的,因为每一个命令都会返回一个值(RC)给 shell 来表示这个命令在执行的过程中是否执行成功或者失败。通常,返回值是 0 表示成功,而一个正数值表示不同种类的错误。有一些系统管理工具仅仅返回一个 1 来表示所有的错误,但是也有很多工具使用其它的正数的返回值来表示各种类型错误。

我们可以很容易的使用脚本来检查 shell 变量 $?,可以通过命令列表中的下一个命令,或者可以直接使用系统管理工具检查。我们一起来看这些返回值。运行一个简单的命令然后立即检查它的返回值,这个返回值始终是属于最后一个运行的命令。

[student@studentvm1 ~]$ ll ; echo "RC = $?"
total 284
-rw-rw-r--  1 student student   130 Sep 15 16:21 ascii-program.sh
drwxrwxr-x  2 student student  4096 Nov 10 11:09 bin
<snip>
drwxr-xr-x. 2 student student  4096 Aug 18 10:21 Videos
RC = 0
[student@studentvm1 ~]$

这个返回值是 0,表示这个命令执行成功了。现在尝试使用同样的命令在一些我们没有权限的目录上。

[student@studentvm1 ~]$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 ~]$

这个返回值的含义可以在 ls 命令的 man 页面 中找到。

现在我们来试试 && 这个控制运算符,因为它也可能会被用在一个命令行程序中。我们将从一个简单的示例开始:创建一个新目录,如果创建成功就在这个目录中创建一个文件。

我们需要一个目录可以创建其它的目录。首先,在你的家目录中创建一个临时的目录用来做测试。

[student@studentvm1 ~]$ cd ; mkdir testdir

~/testdir 中新建一个目录,这也应该是一个空目录,因为是你刚刚创建的,然后创建一个新的空文件在这个新目录中。下面的命令可以做这些事情。

[student@studentvm1 ~]$ mkdir ~/testdir/testdir2 && touch ~/testdir/testdir2/testfile1
[student@studentvm1 ~]$ ll ~/testdir/testdir2/
total 0
-rw-rw-r-- 1 student student 0 Nov 12 14:13 testfile1
[student@studentvm1 ~]$

我们看到一切都运行得很好,因为 testdir 目录是访问且可写的。然后我们改变 testdir 目录的权限,让用户 student 不再具有访问的权限。操作如下:

[student@studentvm1 ~]$ chmod 076 testdir ; ll | grep testdir
d---rwxrw-. 3 student student  4096 Nov 12 14:13 testdir
[student@studentvm1 ~]$

在长列表(ll)命令后面使用 grep 命令来列出 testdir 目录。你可以看到用户 student 不再有 testdir 目录的访问权限。现在我们像之前一样运行同样的命令,但是在 testdir 目录中创建的是一个不同的目录。

[student@studentvm1 ~]$ mkdir ~/testdir/testdir3 && touch ~/testdir/testdir3/testfile1
mkdir: cannot create directory ‘/home/student/testdir/testdir3’: Permission denied
[student@studentvm1 ~]$

尽管我们也同样得到了一个错误的消息,但 && 控制运算符阻止了 touch 命令的运行,因为在创建 testdir3 目录的时候发生了错误。通过这种复合的流控制可以阻止一些错误的发生使事情变乱。但是这样看起来变得稍微复杂了一些。

|| 控制运算符允许添加另一个命令,这个命令在初始程序语句返回值大于 0 时执行。

[student@studentvm1 ~]$ mkdir ~/testdir/testdir3 && touch ~/testdir/testdir3/testfile1 || echo "An error occurred while creating the directory."
mkdir: cannot create directory ‘/home/student/testdir/testdir3’: Permission denied
An error occurred while creating the directory.
[student@studentvm1 ~]$

当我们使用 &&|| 控制运算符时,使用流控制的复合命令的语法格式通常是下面这样的形式。

preceding commands ; command1 && command2 || command3 ; following commands

使用控制运算符的复合命令可以在其它命令之前或者之后,这些和控制运算符流控制有关系,但是不受控制运算符流控制的影响。如果不考虑复合命令的流控制中发生的任何事情那么所有的命令都将执行。

当程序出问题时,这些流控制运算符使得在命令中处理出错和通知我们变得更有效率。我直接在命令行中使用它们,也在脚本中使用。

你可以以 root 用户的身份来删除这个目录和它里面的内容。

[root@studentvm1 ~]# rm -rf /home/student/testdir

你是怎样使用 Bash 控制运算符的呢?在评论区中告诉我们。


via: https://opensource.com/article/18/11/control-operators-bash-shell

作者:David Both 选题:lujun9972 译者:Jamskr 校对:wxy

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

对于许多数据科学家来说,数据操作从始至终就是 Pandas 或 Tidyverse。从理论上讲,这样做没有任何问题。毕竟,这就是这些工具存在的原因。然而,对于像分隔符转换这样的简单任务,这些工具是大材小用了。

立志掌握命令行应该在每个开发人员的学习清单上,特别是数据科学家。学习 shell 的来龙去脉将无可否认地提高你的生产力。除此之外,命令行还是计算领域的一个重要历史课程。例如,awk —— 一种数据驱动的脚本语言。1977 年,在 Brain Kernighan(即传奇的 K&R 书中 K)的帮助下,awk 首次出现。今天,大约五十年过去了,awk 仍然活跃在每年新出版的书里面。因此,可以安全地假设对命令行魔法的付出不会很快贬值。

我们将涵盖什么

  • ICONV
  • HEAD
  • TR
  • WC
  • SPLIT
  • SORT & UNIQ
  • CUT
  • PASTE
  • JOIN
  • GREP
  • SED
  • AWK

ICONV

文件编码可能会很棘手。现在大部分文件都是 UTF-8 编码的。要了解 UTF-8 背后的一些魔力,请查看这个出色的视频。尽管如此,有时我们收到的文件不是这种编码。这可能引起对改变编码模式的一些胡乱尝试。这里,iconv 是一个拯救者。iconv 是一个简单的程序,它将获取采用一种编码的文本并输出采用另一种编码的文本。

# Converting -f (from) latin1 (ISO-8859-1)
# -t (to) standard UTF_8

iconv -f ISO-8859-1 -t UTF-8 < input.txt > output.txt

实用选项:

  • iconv -l 列出所有已知编码
  • iconv -c 默默丢弃无法转换的字符

HEAD

如果你是一个 Pandas 重度用户,那么会很熟悉 head。通常在处理新数据时,我们想做的第一件事就是了解其内容。这就得启动 Pandas,读取数据然后调用 df.head() —— 要说这有点费劲。没有任何选项的 head 将打印出文件的前 10 行。head 的真正力量在于干净利落的测试操作。例如,如果我们想将文件的分隔符从逗号更改为管道。一个快速测试将是:head mydata.csv | sed 's/,/|/g'

# Prints out first 10 lines
head filename.csv

# Print first 3 lines
head -n 3 filename.csv

实用选项:

  • head -n 打印特定行数
  • head -c 打印特定字节数

TR

tr 类似于翻译。这个功能强大的实用程序是文件基础清理的主力。理想的用例是替换文件中的分隔符。

# Converting a tab delimited file into commas
cat tab_delimited.txt | tr "\t" "," comma_delimited.csv

tr 另一个功能是你可以用内建 [:class:] 变量(POSIX 字符类)发挥威力。这些包括了:

  • [:alnum:] 所有字母和数字
  • [:alpha:] 所有字母
  • [:blank:] 所有水平空白
  • [:cntrl:] 所有控制字符
  • [:digit:] 所有数字
  • [:graph:] 所有可打印字符,但不包括空格
  • [:lower:] 所有小写字母
  • [:print:] 所有可打印字符,包括空格
  • [:punct:] 所有标点符号
  • [:space:] 所有水平或垂直空白
  • [:upper:] 所有大写字母
  • [:xdigit:] 所有 16 进制数字

你可以将这些连接在一起以组成强大的程序。以下是一个基本的字数统计程序,可用于检查 README 是否被滥用。

cat README.md | tr "[:punct:][:space:]" "\n" | tr "[:upper:]" "[:lower:]" | grep . | sort | uniq -c | sort -nr

另一个使用基本正则表达式的例子:

# Converting all upper case letters to lower case
cat filename.csv | tr '[A-Z]' '[a-z]'

实用选项:

  • tr -d 删除字符
  • tr -s 压缩字符
  • \b 退格
  • \f 换页
  • \v 垂直制表符
  • \NNN 八进制字符

WC

单词计数。它的价值主要来自其 -l 选项,它会给你提供行数。

# Will return number of lines in CSV
wc -l gigantic_comma.csv

这个工具可以方便地确认各种命令的输出。所以,如果我们在转换文件中的分隔符之后运行 wc -l,我们会期待总行数是一样的,如果不一致,我们就知道有地方出错了。

实用选项:

  • wc -c 打印字节数
  • wc -m 打印字符数
  • wc -L 打印最长行的长度
  • wc -w 打印单词数量

SPLIT

文件大小的范围可以很广。对于有的任务,拆分文件或许是有好处的,所以使用 split 吧。split 的基本语法是:

# We will split our CSV into new_filename every 500 lines
split -l 500 filename.csv new_filename_
# filename.csv
# ls output
# new_filename_aaa
# new_filename_aab
# new_filename_aa

它有两个奇怪的地方是命名约定和缺少文件扩展名。后缀约定可以通过 -d 标志变为数字。要添加文件扩展名,你需要运行以下 find 命令。它将通过附加 .csv 扩展名来更改当前目录中所有文件的名称,所以小心了。

find . -type f -exec mv '{}' '{}'.csv \;
# ls output
# filename.csv.csv
# new_filename_aaa.csv
# new_filename_aab.csv
# new_filename_aac.csv

实用选项:

  • split -b N 按特定字节大小分割
  • split -a N 生成长度为 N 的后缀
  • split -x 使用十六进制后缀

SORT & UNIQ

上面两个命令很明显:它们的作用就是字面意思。这两者结合起来可以提供最强大的冲击 (例如,唯一单词的数量)。这是由于 uniq 只作用于重复的相邻行。这也是在输出前进行 sort 的原因。一个有趣的事情是 sort -u 会达到和典型的 sort file.txt | uniq 模式一样的结果。

sort 对数据科学家来说确实具有潜在的有用能力:能够根据特定列对整个 CSV 进行排序。

# Sorting a CSV file by the second column alphabetically
sort -t"," -k2,2 filename.csv

# Numerically
sort -t"," -k2n,2 filename.csv

# Reverse order
sort -t"," -k2nr,2 filename.csv

这里的 -t 选项将逗号指定为分隔符,通常假设分隔符是空格或制表符。此外,-k 选项是为了确定我们的键。这里的语法是 -km,nm 作为开始列,n 作为结束列。

实用选项:

  • sort -f 忽略大小写
  • sort -r 反向排序
  • sort -R 乱序
  • uniq -c 统计出现次数
  • uniq -d 只打印重复行

CUT

cut 用于删除列。作为演示,如果我们只想删除第一和第三列。

cut -d, -f 1,3 filename.csv

要选择除了第一行外的所有行。

cut -d, -f 2- filename.csv

结合其他命令,将 cut 用作过滤器。

# Print first 10 lines of column 1 and 3, where "some_string_value" is present
head filename.csv | grep "some_string_value" | cut -d, -f 1,3

查出第二列中唯一值的数量。

cat filename.csv | cut -d, -f 2 | sort | uniq | wc -l

# Count occurences of unique values, limiting to first 10 results
cat filename.csv | cut -d, -f 2 | sort | uniq -c | head

PASTE

paste 是一个带有趣味性功能的特定命令。如果你有两个需要合并的文件,并且它们已经排序好了,paste 帮你解决了接下来的步骤。

# names.txt
adam
john
zach

# jobs.txt
lawyer
youtuber
developer

# Join the two into a CSV
paste -d ',' names.txt jobs.txt > person_data.txt

# Output
adam,lawyer
john,youtuber
zach,developer

更多 SQL 式变种,见下文。

JOIN

join 是一个简单的、 准切向的 quasi-tangential SQL。最大的区别是 join 将返回所有列以及只能在一个字段上匹配。默认情况下,join 将尝试使用第一列作为匹配键。为了获得不同结果,必须使用以下语法:

# Join the first file (-1) by the second column
# and the second file (-2) by the first
join -t "," -1 2 -2 1 first_file.txt second_file.txt

标准的 join 是内连接。然而,外连接通过 -a 选项也是可行的。另一个值得一提的技巧是 -q 标志,如果发现有缺失的字段,可用于替换值。

# Outer join, replace blanks with NULL in columns 1 and 2
# -o which fields to substitute - 0 is key, 1.1 is first column, etc...
join -t"," -1 2 -a 1 -a2 -e ' NULL' -o '0,1.1,2.2' first_file.txt second_file.txt

它不是最用户友好的命令,而是绝望时刻的绝望措施。

实用选项:

  • join -a 打印不可配对的行
  • join -e 替换丢失的输入字段
  • join -j 相当于 -1 FIELD -2 FIELD

GREP

grep 用正则表达式全局搜索并且打印 Global search for a Regular Expression and Print ,可能是最有名的命令,并且名副其实。grep 很强大,特别适合在大型代码库中查找。在数据科学的王国里,它充当其他命令的提炼机制。虽然它的标准用途也很有价值。

# Recursively search and list all files in directory containing 'word'

grep -lr 'word' .

# List number of files containing word

grep -lr 'word' . | wc -l

计算包含单词或模式的总行数。

grep -c 'some_value' filename.csv

# Same thing, but in all files in current directory by file name

grep -c 'some_value' *

对多个值使用“或”运算符: \|

grep "first_value\|second_value" filename.csv

实用选项:

  • alias grep="grep --color=auto" 使 grep 色彩丰富
  • grep -E 使用扩展正则表达式
  • grep -w 只匹配整个单词
  • grep -l 打印匹配的文件名
  • grep -v 非匹配

大人物们

sedawk 是本文中最强大的两个命令。为简洁起见,我不打算详细讨论这两个命令。相反,我将介绍各种能证明其令人印象深刻的力量的命令。如果你想了解更多,这儿就有一本书是关于它们的。

SED

sed 本质上是一个流编辑器。它擅长替换,但也可以用于所有输出重构。

最基本的 sed 命令由 s/old/new/g 组成。它的意思是搜索 old,全局替换为 new。 如果没有 /g,我们的命令将在 old 第一次出现后终止。

为了快速了解它的功能,我们可以深入了解一个例子。 在以下情景中,你已有以下文件:

balance,name
$1,000,john
$2,000,jack

我们可能想要做的第一件事是删除美元符号。-i 标志表示原位。'' 表示零长度文件扩展名,从而覆盖我们的初始文件。理想情况下,你可以单独测试,然后输出到新文件。

sed -i '' 's/\$//g' data.txt
# balance,name
# 1,000,john
# 2,000,jack

接下来,去除 blance 列的逗号。

sed -i '' 's/\([0-9]\),\([0-9]\)/\1\2/g' data.txt
# balance,name
# 1000,john
# 2000,jack

最后 jack 有一天决定辞职。所以,再见了,我的朋友。

sed -i '' '/jack/d' data.txt
# balance,name
# 1000,john

正如你所看到的,sed 有很多强大的功能,但乐趣并不止于此。

AWK

最好的留在最后。awk 不仅仅是一个简单的命令:它是一个成熟的语言。在本文中涉及的所有内容中,awk 是目前为止最酷的。如果你感兴趣,这里有很多很棒的资源 —— 看 这里这里这里

awk 的常见用例包括:

  • 文字处理
  • 格式化文本报告
  • 执行算术运算
  • 执行字符串操作

awk 可以以最原生的形式并行 grep

awk '/word/' filename.csv

或者更加神奇:将 grepcut 组合起来。在这里,对于所有带我们指定单词 word 的行,awk 打印第三和第四列,用 tab 分隔。-F, 用于指定切分时的列分隔符为逗号。

awk -F, '/word/ { print $3 "\t" $4 }' filename.csv

awk 内置了许多精巧的变量。比如,NF —— 字段数,和 NR —— 记录数。要获取文件中的第 53 条记录:

awk -F, 'NR == 53' filename.csv

更多的花招是其基于一个或多个值进行过滤的能力。下面的第一个示例将打印第一列等于给定字符串的行的行号和列。

awk -F, ' $1 == "string" { print NR, $0 } ' filename.csv

# Filter based off of numerical value in second column
awk -F, ' $2 == 1000 { print NR, $0 } ' filename.csv

多个数值表达式:

# Print line number and columns where column three greater
# than 2005 and column five less than one thousand

awk -F, ' $3 >= 2005 && $5 <= 1000 { print NR, $0 } ' filename.csv

求出第三列的总和:

awk -F, '{ x+=$3 } END { print x }' filename.csv

在第一列等于 something 的那些行,求出第三列值的总和。

awk -F, '$1 == "something" { x+=$3 } END { print x }' filename.csv

获取文件的行列数:

awk -F, 'END { print NF, NR }' filename.csv

# Prettier version
awk -F, 'BEGIN { print "COLUMNS", "ROWS" }; END { print NF, NR }' filename.csv

打印出现了两次的行:

awk -F, '++seen[$0] == 2' filename.csv

删除重复的行:

# Consecutive lines
awk 'a !~ $0; {a=$0}']

# Nonconsecutive lines
awk '! a[$0]++' filename.csv

# More efficient
awk '!($0 in a) {a[$0];print}

使用内置函数 gsub() 替换多个值。

awk '{gsub(/scarlet|ruby|puce/, "red"); print}'

这个 awk 命令将组合多个 CSV 文件,忽略标题,然后在最后附加它。

awk 'FNR==1 && NR!=1{next;}{print}' *.csv > final_file.csv

需要缩小一个庞大的文件? awk 可以在 sed 的帮助下处理它。具体来说,该命令根据行数将一个大文件分成多个较小的文件。这个一行脚本将增加一个扩展名。

sed '1d;$d' filename.csv | awk 'NR%NUMBER_OF_LINES==1{x="filename-"++i".csv";}{print > x}'

# Example: splitting big_data.csv into data_(n).csv every 100,000 lines
sed '1d;$d' big_data.csv | awk 'NR%100000==1{x="data_"++i".csv";}{print > x}'

结语

命令行拥有无穷无尽的力量。本文中介绍的命令足以将你从一无所知提升到英雄人物。除了涵盖的内容之外,还有许多实用程序可以考虑用于日常数据操作。Csvkitxsv 还有 q 是需要记住的三个。如果你希望更深入地了解命令行数据科学,查看这本书。它也可以免费在线获得!


via: http://kadekillary.work/post/cli-4-ds/

作者:Kade Killary 选题:lujun9972 译者:GraveAccent 校对:wxy

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

这是 Emacs 和 Org 模式系列的第五篇。

这篇博文是由 Org 模式的源文件生成的,其有几种格式:博客页面演示稿PDF 文档

1 Org 模式的输出

1.1 背景

Org 模式不仅仅只是一个议程生成程序,它也能输出许多不同的格式: LaTeX、PDF、Beamer、iCalendar(议程)、HTML、Markdown、ODT、普通文本、手册页和其它更多的复杂的格式,比如说网页文件。

这也不只是一些事后的想法,这是 Org 模式的设计核心部分并且集成的很好。

这一个文件可以同时是源代码、自动生成的输出、任务列表、文档和展示。

有些人将 Org 模式作为他们首选的标记格式,甚至对于 LaTeX 文档也是如此。Org 模式手册中的 输出一节 有更详细的介绍。

1.2 开始

对于任意的 Org 模式的文档,只要按下 C-c C-e 键,就会弹出一个让你选择多种输出格式和选项的菜单。这些选项通常是次键选择,所以很容易设置和执行。例如:要输出一个 PDF 文档,按 C-c C-e l p,要输出 HMTL 格式的, 按 C-c C-e h h

对于所有的输出选项,都有许多可用的设置;详情参见手册。事实上,使用 LaTeX 格式相当于同时使用 LaTeX 和 HTML 模式,在不同的模式中插入任意的前言和设置等。

1.3 第三方插件

ELPA 中也包含了许多额外的输出格式,详情参见 ELPA

2 Org 模式的 Beamer 演示

2.1 关于 Beamer

Beamer) 是一个生成演示稿的 LaTeX 环境. 它包括了以下特性:

  • 在演示稿中自动生成结构化的元素(例如 Marburg 主题)。 在演示稿中,这个特性可以为观众提供了视觉参考。
  • 对组织演示稿有很大的帮助。
  • 主题
  • 完全支持 LaTeX

2.2 Org 模式中 Beamer 的优点

在 Org 模式中用 Beamer 有很多好处,总的来说:

  • Org 模式很简单而且对可视化支持的很好,同时改变结构可以快速的重组你的材料。
  • 与 org-babel 绑定在一起,实时语法高亮源码和内嵌结果。
  • 语法通常更容易使用。

我已经完全用 Org 模式和 beamer 替换掉使用 LibreOffice/Powerpoint/GoogleDocs。事实上,当我必须使用其中一种工具时,这是相当令人沮丧的,因为它们在可视化表示演讲稿结构方面远远比不上 Org 模式。

2.3 标题层次

Org 模式的 Beamer 会将你文档中的部分(文中定义了标题的)转换成幻灯片。当然,问题是:哪些部分?这是由 H 输出设置org-export-headline-levels)决定的。

针对不同的人,有许多不同的方法。我比较喜欢我的演示稿这样:

#+OPTIONS: H:2
#+BEAMER_HEADER: \AtBeginSection{\frame{\sectionpage}}

这将为每个主题提供了独立部分,以突出主题的改变然后使用级别 2(两个星号)的标题来设置幻灯片。许多 Beamer 主题也有第三个间接层次,所以你可以将 H 设为 3。

2.4 主题和配置

你可以在 Org 模式的文件顶部来插入几行来配置 Beamer 和 LaTeX。在本文中,例如,你可以这样定义:

#+TITLE:  Documents and presentations with org-mode
#+AUTHOR: John Goerzen
#+BEAMER_HEADER: \institute{The Changelog}
#+PROPERTY: comments yes
#+PROPERTY: header-args :exports both :eval never-export
#+OPTIONS: H:2
#+BEAMER_THEME: CambridgeUS
#+BEAMER_COLOR_THEME: default

2.5 高级设置

我比教喜欢修改颜色、项目符号样式等。我的配置如下:

# We can't just +BEAMER_INNER_THEME: default because that picks the theme default.
# Override per https://tex.stackexchange.com/questions/11168/change-bullet-style-formatting-in-beamer
#+BEAMER_INNER_THEME: default
#+LaTeX_CLASS_OPTIONS: [aspectratio=169]
#+BEAMER_HEADER: \definecolor{links}{HTML}{0000A0}
#+BEAMER_HEADER: \hypersetup{colorlinks=,linkcolor=,urlcolor=links}
#+BEAMER_HEADER: \setbeamertemplate{itemize items}[default]
#+BEAMER_HEADER: \setbeamertemplate{enumerate items}[default]
#+BEAMER_HEADER: \setbeamertemplate{items}[default]
#+BEAMER_HEADER: \setbeamercolor*{local structure}{fg=darkred}
#+BEAMER_HEADER: \setbeamercolor{section in toc}{fg=darkred}
#+BEAMER_HEADER: \setlength{\parskip}{\smallskipamount}

在这里,aspectratio=169 将纵横比设为 16:9, 其它部分都是标准的 LaTeX/Beamer 配置。

2.6 缩小 (适应屏幕)

有时你会遇到一些非常大的代码示例,你可能更倾向与将幻灯片缩小以适应它们。

只要按下 C-c C-c pBEAMER_opt 属性设为 shrink=15\。(或者设为更大的 shrink 值)。上一张幻灯片就用到了这个。

2.7 效果

这就是最终的效果:

screenshot1

3 幻灯片之间的交互

3.1 交互式的 Emacs 幻灯片

使用 org-tree-slide 这个插件的话,就可以在 Emacs 的右侧显示幻灯片了。 只要按下 M-x,然后输入 org-tree-slide-mode,回车,然后你就可以用 C->C-< 在幻灯片之间切换了。

你可能会发现 C-c C-x C-v (即 org-toggle-inline-images)有助于使系统显示内嵌的图像。

3.2 HTML 幻灯片

有许多方式可以将 Org 模式的演讲稿导出为 HTML,并有不同级别的 JavaScript 集成。有关详细信息,请参见 Org 模式的 wiki 中的 非 beamer 演讲稿一节

4 更多

4.1 本文中的附加资源

4.2 下一个 Emacs 系列

mu4e 邮件!


via: http://changelog.complete.org/archives/9900-emacs-5-documents-and-presentations-with-org-mode

作者:John Goerzen 选题:lujun9972 译者:oneforalone 校对:wxy

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

zypper 是 Suse Linux 系统的包和补丁管理器,你可以根据下面的 12 条附带输出示例的实用范例来学习 zypper 命令的使用。

Suse Linux 使用 zypper 进行包管理,其是一个由 ZYpp 包管理引擎提供的包管理系统。在此篇文章中我们将分享 12 条附带输出示例的实用 zypper 命令,能帮助你处理日常的系统管理任务。

不带参数的 zypper 命令将列出所有可用的选项,这比参考详细的 man 手册要容易上手得多。

root@kerneltalks # zypper
  用法:
        zypper [--global-options] <command> [--command-options] [arguments]
        zypper <subcommand> [--command-options] [arguments]
 
  全局选项:
        --help, -h              帮助
        --version, -V           输出版本号
        --promptids             输出 zypper 用户提示符列表
        --config, -c <file>     使用指定的配置文件来替代默认的
        --userdata <string>     在历史和插件中使用的用户自定义事务 id
        --quiet, -q             忽略正常输出,只打印错误信息
        --verbose, -v           增加冗长程度
        --color
        --no-color              是否启用彩色模式,如果 tty 支持的话
        --no-abbrev, -A         表格中的文字不使用缩写
        --table-style, -s       表格样式(整型)
        --non-interactive, -n   不询问任何选项,自动使用默认答案
        --non-interactive-include-reboot-patches
                                针对带有重启标志的补丁,不使用交互模式
        --xmlout, -x            切换至 XML 输出
        --ignore-unknown, -i    忽略未知的包
 
        --reposd-dir, -D <dir>  使用自定义仓库文件目录
        --cache-dir, -C <dir>   为所有缓存启用可选路径
        --raw-cache-dir <dir>   启用可选 raw 元数据缓存路径
        --solv-cache-dir <dir>  启用可选 solv 文件缓存路径
        --pkg-cache-dir <dir>   启用可选包缓存路径
 
     仓库选项:
        --no-gpg-checks         忽略 GPG 检查失败并跳过
        --gpg-auto-import-keys  自动信任并导入新仓库的签名密钥
        --plus-repo, -p <URI>   使用附加仓库
        --plus-content <tag>    另外使用禁用的仓库来提供特定的关键词
                                尝试使用 '--plus-content debug' 选项来启用仓库
        --disable-repositories  不从仓库中读取元数据
        --no-refresh            不刷新仓库
        --no-cd                 忽略 CD/DVD 中的仓库
        --no-remote             忽略远程仓库
        --releasever            设置所有 .repo 文件中的 $releasever 变量(默认值:发行版版本)
 
     Target Options:
        --root, -R <dir>        在另一个根路径下进行操作
        --disable-system-resolvables
                                不读取已安装包
 
  命令:
        help, ?                 打印帮助
        shell, sh               允许多命令
 
     仓库管理:
        repos, lr               列出所有自定义仓库
        addrepo, ar             添加一个新仓库
        removerepo, rr          移除指定仓库
        renamerepo, nr          重命名指定仓库
        modifyrepo, mr          修改指定仓库
        refresh, ref            刷新所有仓库
        clean                   清除本地缓存
 
     服务管理:
        services, ls            列出所有自定义服务
        addservice, as          添加一个新服务
        modifyservice, ms       修改指定服务
        removeservice, rs       移除指定服务
        refresh-services, refs  刷新所有服务
 
     软件管理:
        install, in             安装包
        remove, rm              移除包
        verify, ve              确认包依赖的完整性
        source-install, si      安装源码包及其构建依赖
        install-new-recommends, inr
                                安装由已安装包建议一并安装的新包
 
     更新管理:
        update, up              更新已安装包至更新版本
        list-updates, lu        列出可用更新
        patch                   安装必要的补丁
        list-patches, lp        列出必要的补丁
        dist-upgrade, dup       进行发行版更新
        patch-check, pchk       检查补丁
 
     查询:
        search, se              查找符合匹配模式的包
        info, if                展示特定包的完全信息
        patch-info              展示特定补丁的完全信息
        pattern-info            展示特定模式的完全信息
        product-info            展示特定产品的完全信息
        patches, pch            列出所有可用的补丁
        packages, pa            列出所有可用的包
        patterns, pt            列出所有可用的模式
        products, pd            列出所有可用的产品
        what-provides, wp       列出提供特定功能的包
 
     包锁定:
        addlock, al             添加一个包锁定
        removelock, rl          移除一个包锁定
        locks, ll               列出当前的包锁定
        cleanlocks, cl          移除无用的锁定
 
     其他命令:
        versioncmp, vcmp        比较两个版本字符串
        targetos, tos           打印目标操作系统 ID 字符串
        licenses                打印已安装包的证书和 EULAs 报告
        download                使用命令行下载指定 rpm 包到本地目录
        source-download         下载所有已安装包的源码 rpm 包到本地目录
 
     子命令:
        subcommand              列出可用子命令
 
输入 'zypper help <command>' 来获得特定命令的帮助。

如何使用 zypper 安装包

zypper 通过 ininstall 子命令来在你的系统上安装包。它的用法与 yum 软件包安装 相同。你只需要提供包名作为参数,包管理器(此处是 zypper)就会处理所有的依赖并与你指定的包一并安装。

# zypper install telnet
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
Resolving package dependencies...
 
The following NEW package is going to be installed:
  telnet
 
1 new package to install.
Overall download size: 51.8 KiB. Already cached: 0 B. After the operation, additional 113.3 KiB will be used.
Continue? [y/n/...? shows all options] (y): y
Retrieving package telnet-1.2-165.63.x86_64                                                                                        (1/1),  51.8 KiB (113.3 KiB unpacked)
Retrieving: telnet-1.2-165.63.x86_64.rpm .........................................................................................................................[done]
Checking for file conflicts: .....................................................................................................................................[done]
(1/1) Installing: telnet-1.2-165.63.x86_64 .......................................................................................................................[done]

以上是我们安装 telnet 包时的输出,供你参考。

推荐阅读:在 YUM 和 APT 系统中安装包

如何使用 zypper 移除包

要在 Suse Linux 中擦除或移除包,使用 zypper 附带 removerm 子命令。

root@kerneltalks # zypper rm telnet
Loading repository data...
Reading installed packages...
Resolving package dependencies...
 
The following package is going to be REMOVED:
  telnet
 
1 package to remove.
After the operation, 113.3 KiB will be freed.
Continue? [y/n/...? shows all options] (y): y
(1/1) Removing telnet-1.2-165.63.x86_64 ..........................................................................................................................[done]

我们在此处移除了先前安装的 telnet 包。

使用 zypper 检查依赖或者认证已安装包的完整性

有时可以通过强制忽略依赖关系来安装软件包。zypper 使你能够扫描所有已安装的软件包并检查其依赖性。如果缺少任何依赖项,它将提供你安装或重新安装它的机会,从而保持已安装软件包的完整性。

使用附带 verifyve 子命令的 zypper 命令来检查已安装包的完整性。

root@kerneltalks # zypper ve
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
 
Dependencies of all installed packages are satisfied.

在上面的输出中,你能够看到最后一行说明已安装包的所有依赖都已安装完全,并且无需更多操作。

如何在 Suse Linux 中使用 zypper 下载包

zypper 提供了一种方法使得你能够将包下载到本地目录而不去安装它。你可以在其他具有同样配置的系统上使用这个已下载的软件包。包会被下载至 /var/cache/zypp/packages/<repo>/<arch>/ 目录。

root@kerneltalks # zypper download telnet
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
Retrieving package telnet-1.2-165.63.x86_64                                                                                        (1/1),  51.8 KiB (113.3 KiB unpacked)
(1/1) /var/cache/zypp/packages/SMT-http_smt-ec2_susecloud_net:SLES12-SP3-Pool/x86_64/telnet-1.2-165.63.x86_64.rpm ................................................[done]
 
download: Done.
 
# ls -lrt /var/cache/zypp/packages/SMT-http_smt-ec2_susecloud_net:SLES12-SP3-Pool/x86_64/
total 52
-rw-r--r-- 1 root root 53025 Feb 21 03:17 telnet-1.2-165.63.x86_64.rpm
 

你能看到我们使用 zypper 将 telnet 包下载到了本地。

推荐阅读:在 YUM 和 APT 系统中只下载包而不安装

如何使用 zypper 列出可用包更新

zypper 允许你浏览已安装包的所有可用更新,以便你可以提前计划更新活动。使用 list-updateslu 子命令来显示已安装包的所有可用更新。

root@kerneltalks # zypper lu
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
S | Repository                        | Name                       | Current Version               | Available Version                  | Arch
--|-----------------------------------|----------------------------|-------------------------------|------------------------------------|-------
v | SLES12-SP3-Updates                | at-spi2-core               | 2.20.2-12.3                   | 2.20.2-14.3.1                      | x86_64
v | SLES12-SP3-Updates                | bash                       | 4.3-82.1                      | 4.3-83.5.2                         | x86_64
v | SLES12-SP3-Updates                | ca-certificates-mozilla    | 2.7-11.1                      | 2.22-12.3.1                        | noarch
v | SLE-Module-Containers12-Updates   | containerd                 | 0.2.5+gitr639_422e31c-20.2    | 0.2.9+gitr706_06b9cb351610-16.8.1  | x86_64
v | SLES12-SP3-Updates                | crash                      | 7.1.8-4.3.1                   | 7.1.8-4.6.2                        | x86_64
v | SLES12-SP3-Updates                | rsync                      | 3.1.0-12.1                    | 3.1.0-13.10.1                      | x86_64

输出特意被格式化以便于阅读。每一列分别代表包所属仓库名称、包名、已安装版本、可用的更新版本和架构。

在 Suse Linux 中列出和安装补丁

使用 list-patcheslp 子命令来显示你的 Suse Linux 系统需要被应用的所有可用补丁。

root@kerneltalks # zypper lp
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
 
Repository                        | Name                                     | Category    | Severity  | Interactive | Status | Summary                                 
----------------------------------|------------------------------------------|-------------|-----------|-------------|--------|------------------------------------------------------------------------------------
SLE-Module-Containers12-Updates   | SUSE-SLE-Module-Containers-12-2018-273   | security    | important | ---         | needed | Version update for docker, docker-runc, containerd, golang-github-docker-libnetwork
SLE-Module-Containers12-Updates   | SUSE-SLE-Module-Containers-12-2018-62    | recommended | low       | ---         | needed | Recommended update for sle2docker       
SLE-Module-Public-Cloud12-Updates | SUSE-SLE-Module-Public-Cloud-12-2018-268 | recommended | low       | ---         | needed | Recommended update for python-ecdsa     
SLES12-SP3-Updates                | SUSE-SLE-SERVER-12-SP3-2018-116          | security    | moderate  | ---         | needed | Security update for rsync               
---- output clipped ----
SLES12-SP3-Updates                | SUSE-SLE-SERVER-12-SP3-2018-89           | security    | moderate  | ---         | needed | Security update for perl-XML-LibXML     
SLES12-SP3-Updates                | SUSE-SLE-SERVER-12-SP3-2018-90           | recommended | low       | ---         | needed | Recommended update for lvm2             
 
Found 37 applicable patches:
37 patches needed (18 security patches)

使用相应的表头可以很好地组织输出。你可以轻松地找出并根据情况计划你的补丁更新。我们能看到在我们的系统中,37 个可用补丁中有 18 个是安全补丁,需要被高优先级应用!

你可以通过发出 zypper patch 命令安装所有需要的补丁。

如何使用 zypper 更新包

要使用 zypper 更新包,使用 updateup 子命令后接包名。在上述列出的更新命令中,我们知道在我们的服务器上 rsync 包更新可用。让我们现在来更新它吧!

root@kerneltalks # zypper update rsync
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
Resolving package dependencies...
 
The following package is going to be upgraded:
  rsync
 
1 package to upgrade.
Overall download size: 325.2 KiB. Already cached: 0 B. After the operation, additional 64.0 B will be used.
Continue? [y/n/...? shows all options] (y): y
Retrieving package rsync-3.1.0-13.10.1.x86_64                                                                                      (1/1), 325.2 KiB (625.5 KiB unpacked)
Retrieving: rsync-3.1.0-13.10.1.x86_64.rpm .......................................................................................................................[done]
Checking for file conflicts: .....................................................................................................................................[done]
(1/1) Installing: rsync-3.1.0-13.10.1.x86_64 .....................................................................................................................[done]

在 Suse Linux 上使用 zypper 查找包

如果你不确定包的全名也不要担心。你可以使用 zypper 附带的 sesearch 子命令并提供查找字符串来查找包。

root@kerneltalks # zypper se lvm
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
 
S  | Name          | Summary                      | Type
---|---------------|------------------------------|-----------
   | libLLVM       | Libraries for LLVM           | package
   | libLLVM-32bit | Libraries for LLVM           | package
   | llvm          | Low Level Virtual Machine    | package
   | llvm-devel    | Header Files for LLVM        | package
   | lvm2          | Logical Volume Manager Tools | srcpackage
i+ | lvm2          | Logical Volume Manager Tools | package
   | lvm2-devel    | Development files for LVM2   | package 

在上述示例中我们查找了 lvm 字符串并得到了如上输出列表。你能在 zypper install/remove/update 命令中使用 Name 字段的名字。

使用 zypper 检查已安装包信息

你能够使用 zypper 检查已安装包的详细信息。infoif 子命令将列出已安装包的信息。它也可以显示未安装包的详细信息,在该情况下,Installed 参数将返回 No 值。

root@kerneltalks # zypper info rsync
Refreshing service 'SMT-http_smt-ec2_susecloud_net'.
Refreshing service 'cloud_update'.
Loading repository data...
Reading installed packages...
 
 
Information for package rsync:
------------------------------
Repository     : SLES12-SP3-Updates
Name           : rsync
Version        : 3.1.0-13.10.1
Arch           : x86_64
Vendor         : SUSE LLC <https://www.suse.com/>
Support Level  : Level 3
Installed Size : 625.5 KiB
Installed      : Yes
Status         : up-to-date
Source package : rsync-3.1.0-13.10.1.src
Summary        : Versatile tool for fast incremental file transfer
Description    :
    Rsync is a fast and extraordinarily versatile file  copying  tool. It can copy
    locally, to/from another host over any remote shell, or to/from a remote rsync
    daemon. It offers a large number of options that control every aspect of its
    behavior and permit very flexible specification of the set of files to be
    copied. It is famous for its delta-transfer algorithm, which reduces the amount
    of data sent over the network by sending only the differences between the
    source files and the existing files in the destination. Rsync is widely used
    for backups and mirroring and as an improved copy command for everyday use.

使用 zypper 列出仓库

使用 zypper 命令附带 lrrepos 子命令列出仓库。

root@kerneltalks # zypper lr
Refreshing service 'cloud_update'.
Repository priorities are without effect. All enabled repositories share the same priority.
 
#  | Alias                                                                                | Name                                                  | Enabled | GPG Check | Refresh
---|--------------------------------------------------------------------------------------|-------------------------------------------------------|---------|-----------|--------
 1 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Adv-Systems-Management12-Debuginfo-Pool    | SLE-Module-Adv-Systems-Management12-Debuginfo-Pool    | No      | ----      | ----
 2 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Adv-Systems-Management12-Debuginfo-Updates | SLE-Module-Adv-Systems-Management12-Debuginfo-Updates | No      | ----      | ----
 3 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Adv-Systems-Management12-Pool              | SLE-Module-Adv-Systems-Management12-Pool              | Yes     | (r ) Yes  | No
 4 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Adv-Systems-Management12-Updates           | SLE-Module-Adv-Systems-Management12-Updates           | Yes     | (r ) Yes  | Yes
 5 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Containers12-Debuginfo-Pool                | SLE-Module-Containers12-Debuginfo-Pool                | No      | ----      | ----
 6 | SMT-http_smt-ec2_susecloud_net:SLE-Module-Containers12-Debuginfo-Updates             | SLE-Module-Containers12-Debuginfo-Updates             | No      | ----      | ----

此处你需要检查 enabled 列来确定哪些仓库是已被启用的而哪些没有。

在 Suse Linux 中使用 zypper 添加或移除仓库

要添加仓库你需要仓库或 .repo 文件的 URI,否则你会遇到如下错误。

root@kerneltalks # zypper addrepo -c SLES12-SP3-Updates
If only one argument is used, it must be a URI pointing to a .repo file.

使用 URI,你可以像如下方式添加仓库:

root@kerneltalks # zypper  addrepo -c http://smt-ec2.susecloud.net/repo/SUSE/Products/SLE-SDK/12-SP3/x86_64/product?credentials=SMT-http_smt-ec2_susecloud_net SLE-SDK12-SP3-Pool
Adding repository 'SLE-SDK12-SP3-Pool' ...........................................................................................................................[done]
Repository 'SLE-SDK12-SP3-Pool' successfully added
 
URI         : http://smt-ec2.susecloud.net/repo/SUSE/Products/SLE-SDK/12-SP3/x86_64/product?credentials=SMT-http_smt-ec2_susecloud_net
Enabled     : Yes
GPG Check   : Yes
Autorefresh : No
Priority    : 99 (default priority)
 
Repository priorities are without effect. All enabled repositories share the same priority.

在 Suse 中使用附带 addrepoar 子命令的 zypper 命令添加仓库,后接 URI 以及你需要提供一个别名。

要在 Suse 中移除一个仓库,使用附带 removereporr 子命令的 zypper 命令。

root@kerneltalks # zypper removerepo nVidia-Driver-SLE12-SP3
Removing repository 'nVidia-Driver-SLE12-SP3' ....................................................................................................................[done]
Repository 'nVidia-Driver-SLE12-SP3' has been removed.

清除 zypper 本地缓存

使用 zypper clean 命令清除 zypper 本地缓存。

root@kerneltalks # zypper clean
All repositories have been cleaned up.

via: https://kerneltalks.com/commands/12-useful-zypper-command-examples/

作者:KernelTalks 译者:cycoe 校对:wxy

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

这是 Emacs 和 Org 模式系列的第四篇。

至今为止,你已经见识到了 Org 模式的强大和高效,如果你像我一样,你可能会想:

“我真的很想让它在我所有的设备上同步。”

或者是说:

“我能在 Org 模式中转发邮件吗?”

答案当然是肯定的,因为这就是 Emacs。

同步

由于 Org 模式只使用文本文件,所以使用任意工具都可以很容易地实现同步。我使用的是 git 的 git-remote-gcrypt。由于 git-remote-gcrypt 的一些限制,每台机器都倾向于推到自己的分支,并使用命令来控制。每台机器都会先合并其它所有的分支,然后再将合并后的结果推送到主干上。cron 作业可以实现将机器上的分支推送上去,而 elisp 会协调这一切 —— 确保在同步之前保存缓冲区,在同步之后从磁盘刷新缓冲区,等等。

这篇文章的代码有点多,所以我将把它链接到 github 上,而不是写在这里。

我有一个用来存放我所有的 Org 模式的文件的目录 $HOME/org,在 ~/org 目录下有个 Makefile 文件来处理同步。该文件定义了以下目标:

  • push: 添加、提交和推送到以主机命名的分支上
  • fetch: 一个简单的 git fetch 操作
  • sync: 添加、提交和拉取远程的修改,合并,并(假设合并成功)将其推送到以主机命名的分支和主干上

现在,在我的用户 crontab 中有这个:

*/15 * * * * make -C $HOME/org push fetch 2>&1 | logger --tag 'orgsync'

与之相关的 elisp 代码 定义了一个快捷键(C-c s)来调用同步。多亏了 cronjob,只要文件被保存 —— 即使我没有在另一个机器上同步 —— 它们也会被拉取进来。

我发现这个设置非常好用。

用 Org 模式发邮件

在继续下去之前,首先要问自己一下:你真的需要它吗? 我用的是带有 mu4e 的 Org 模式,而且它集成的也很好;任何 Org 模式的任务都可以通过 Message-id 链接到电子邮件,这很理想 —— 它可以让一个人做一些事情,比如提醒他在一周内回复一条消息。

然而,Org 模式不仅仅只有提醒。它还是一个知识库、创作系统等,但是并不是我所有的邮件客户端都使用 mu4e。(注意:移动设备中有像 MobileOrg 这样的应用)。我并没有像我想的那样经常使用它,但是它有它的用途,所以我认为我也应该在这里记录它。

现在我不仅想处理纯文本电子邮件。我希望能够处理附件、HTML 邮件等。这听起来很快就有问题了 —— 但是通过使用 ripmime 和 pandoc 这样的工具,情况还不错。

第一步就是要用某些方法将获取到的邮件放入指定的文件夹下。扩展名、特殊用户等。然后我用一个 fetchmail 配置 来将它拉取下来并运行我自己的 insorgmail 脚本。

这个脚本就是处理所有有趣的部分了。它开始用 ripmime 处理消息,用 pandoc 将 HTML 的部分转换为 Org 模式的格式。 Org 模式的层次结构是用来尽可能最好地表示 email 的结构。使用 HTML 和其他工具时,email 可能会变得相当复杂,但我发现这对于我来说是可以接受的。

下一篇

我最后一篇关于 Org 模式的文章将讨论如何使用它来编写文档和准备幻灯片 —— 我发现自己对 Org 模式的使用非常满意,但这需要一些调整。


via: http://changelog.complete.org/archives/9898-emacs-4-automated-emails-to-org-mode-and-org-mode-syncing

作者:John Goerzen 译者:oneforalone 校对:wxy

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

创建这样一个脚本,当指定的设备插入时触发你的计算机去做一个指定动作。

udev 是一个为你的计算机提供设备事件的 Linux 子系统。通俗来讲就是,当你的计算机上插入了像网卡、外置硬盘(包括 U 盘)、鼠标、键盘、游戏操纵杆和手柄、DVD-ROM 驱动器等等设备时,代码能够检测到它们。这样就能写出很多可能非常有用的实用程序,而它已经很好了,普通用户就可以写出脚本去做一些事情,比如当某个硬盘驱动器插入时,执行某个任务。

这篇文章教你去如何写一个由一些 udev 事件触发的 udev 脚本,比如插入了一个 U 盘。当你理解了 udev 的工作原理,你就可以用它去做各种事情,比如当一个游戏手柄连接后加载一个指定的驱动程序,或者当你用于备份的驱动器连接后,自动执行备份工作。

一个初级的脚本

使用 udev 的最佳方式是从一个小的代码块开始。不要指望从一开始就写出完整的脚本,而是从最简单的确认 udev 触发了某些指定的事件开始。

对于你的脚本,依据你的目标,并不是在任何情况下都能保证你亲眼看到你的脚本运行结果的,因此需要在你的脚本日志中确认它成功触发了。而日志文件通常放在 /var 目录下,但那个目录通常是 root 用户的领地。对于测试目的,可以使用 /tmp,它可以被普通用户访问并且在重启动后就被清除了。

打开你喜欢的文本编辑器,然后输入下面的简单脚本:

#!/usr/bin/bash

echo $date > /tmp/udev.log

把这个脚本放在 /usr/local/bin 或缺省可运行路径的位置中。将它命名为 trigger.sh,并运行 chmod +x 授予可运行权限:

$ sudo mv trigger.sh /usr/local/bin
$ sudo chmod +x /usr/local/bin/trigger.sh

这个脚本没有任何和 udev 有关的事情。当它运行时,这个脚本将在文件 /tmp/udev.log 中放入当前的时间戳。你可以自己测试一下这个脚本:

$ /usr/local/bin/trigger.sh
$ cat /tmp/udev.log
Tue Oct 31 01:05:28 NZDT 2035

接下来让 udev 去触发这个脚本。

唯一设备识别

为了让你的脚本能够被一个设备事件触发,udev 必须要知道在什么情况下调用该脚本。在现实中,你可以通过它的颜色、制造商、以及插入到你的计算机这一事实来识别一个 U 盘。而你的计算机,它需要一系列不同的标准。

udev 通过序列号、制造商、以及提供商 ID 和产品 ID 号来识别设备。由于现在你的 udev 脚本还处于它的生命周期的早期阶段,因此要尽可能地宽泛、非特定和包容。换句话说就是,你希望首先去捕获尽可能多的有效 udev 事件来触发你的脚本。

使用 udevadm monitor 命令你可以实时利用 udev,并且可以看到当你插入不同设备时发生了什么。用 root 权限试一试。

$ su
# udevadm monitor

该监视函数输出接收到的事件:

  • UDEV:在规则处理之后发出 udev 事件
  • KERNEL:内核发送 uevent 事件

udevadm monitor 命令运行时,插入一个 U 盘,你将看到各种信息在你的屏幕上滚动而出。注意那一个 ADD 事件的事件类型。这是你所需要的识别事件类型的一个好方法。

udevadm monitor 命令提供了许多很好的信息,但是你可以使用 udevadm info 命令以更好看的格式来看到它,假如你知道你的 U 盘当前已经位于你的 /dev 树。如果不在这个树下,拔下它并重新插入,然后立即运行这个命令:

$ su -c 'dmesg | tail | fgrep -i sd*'

举例来说,如果那个命令返回 sdb: sdb1,说明内核已经给你的 U 盘分配了 sdb 卷标。

或者,你可以使用 lsblk 命令去查看所有连接到你的系统上的驱动器,包括它的大小和分区。

现在,你的驱动器已经处于你的文件系统中了,你可以使用下面的命令去查看那个设备的相关 udev 信息:

# udevadm info -a -n /dev/sdb | less

这个命令将返回许多信息。现在我们只关心信息中的第一个块。

你的任务是从 udev 的报告中找出能唯一标识那个设备的部分,然后当计算机检测到这些唯一属性时,告诉 udev 去触发你的脚本。

udevadm info 命令处理一个(由设备路径指定的)设备上的报告,接着“遍历”父级设备链。对于找到的大多数设备,它以一个“键值对”格式输出所有可能的属性。你可以写一个规则,从一个单个的父级设备属性上去匹配插入设备的属性。

looking at device '/devices/000:000/blah/blah//block/sdb':
  KERNEL=="sdb"
  SUBSYSTEM=="block"
  DRIVER==""
  ATTR{ro}=="0"
  ATTR{size}=="125722368"
  ATTR{stat}==" 2765 1537 5393"
  ATTR{range}=="16"
  ATTR{discard\_alignment}=="0"
  ATTR{removable}=="1"
  ATTR{blah}=="blah"

一个 udev 规则必须包含来自单个父级设备的一个属性。

父级属性是描述一个设备的最基本的东西,比如它是插入到一个物理端口的东西、或是一个容量多大的东西、或这是一个可移除的设备。

由于 KERNEL 卷标 sdb 可能会由于分配给在它之前插入的其它驱动器而发生变化,因此卷标并不是一个 udev 规则的父级属性的好选择。但是,在做概念论证时你可以使用它。一个事件的最佳候选者是 SUBSYSTEM 属性,它表示那个设备是一个 “block” 系统设备(也就是为什么我们要使用 lsblk 命令来列出设备的原因)。

/etc/udev/rules.d 目录中打开一个名为 80-local.rules 的文件,然后输入如下代码:

SUBSYSTEM=="block", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

保存文件,拔下你的测试 U 盘,然后重启动系统。

等等,重启动 Linux 机器?

理论上说,你只需要运行 udevadm control —reload 即可,它将重新加载所有规则,但是在我们实验的现阶段,最好要排除可能影响实验结果的所有因素。udev 是非常复杂的,为了不让你躺在床上整晚都在思考为什么这个规则不能正常工作,是因为语法错误吗?还是应该重启动一下。所以,不管 POSIX 自负地告诉你过什么,你都应该去重启动一下。

当你的系统重启动完毕之后,(使用 Ctl+Alt+F3 或类似快捷键)切换到一个文本控制台,并插入你的 U 盘。如果你运行了一个最新的内核,当你插入 U 盘后你或许可以看到一大堆输出。如果看到一个错误信息,比如 “Could not execute /usr/local/bin/trigger.sh”,或许是因为你忘了授予这个脚本可运行的权限。否则你将看到的是,一个设备插入,它得到内核设备分配的一些东西,等等。

现在,见证奇迹的时刻到了。

$ cat /tmp/udev.log
Tue Oct 31 01:35:28 NZDT 2035

如果你在 /tmp/udev.log 中看到了最新的日期和时间,那么说明 udev 已经成功触发了你的脚本。

改进规则做一些有用的事情

现在的问题是使用的规则太通用了。插入一个鼠标、一个 U 盘、或某个人的 U 盘都将盲目地触发这个脚本。现在,我们开始专注于希望触发你的脚本的是确定的某个 U 盘。

实现上述目标的一种方式是使用提供商 ID 和产品 ID。你可以使用 lsusb 命令去得到这些数字。

$ lsusb
Bus 001 Device 002: ID 8087:0024 Slacker Corp. Hub
Bus 002 Device 002: ID 8087:0024 Slacker Corp. Hub
Bus 003 Device 005: ID 03f0:3307 TyCoon Corp.
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 hub
Bus 001 Device 003: ID 13d3:5165 SBo Networks

在这个例子中,TyCoon Corp 前面的 03f0:3307 就表示了提供商 ID 和产品 ID 的属性。你也可以通过 udevadm info -a -n /dev/sdb | grep vendor 的输出来查看这些数字,但是从 lsusb 的输出中可以很容易地一眼找到这些数字。

现在,可以在你的脚本中包含这些属性了。

SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/thumb.sh"

测试它(是的,为了确保不会有来自 udev 的影响因素,我们仍然建议先重新启动一下),它应该会像前面一样工作,现在,如果你插入一个不同公司制造的 U 盘(因为它们的提供商 ID 不一样)、或插入一个鼠标、或插入一个打印机,这个脚本将不会被触发。

继续添加新属性来进一步专注于你希望去触发你的脚本的那个唯一的 U 盘。使用 udevadm info -a -n /dev/sdb 命令,你可以找出像提供商名字、序列号、或产品名这样的东西。

为了保证思路清晰,确保每次只添加一个新属性。我们(和在网上看到的其他人)在 udev 规则中所遇到的大多数错误都是因为一次添加了太多的属性,而奇怪为什么不能正常工作了。逐个测试属性是最安全的作法,这样可以确保 udev 能够成功识别到你的设备。

安全

编写 udev 规则当插入一个驱动器后自动去做一些事情,将带来安全方面的担忧。在我的机器上,我甚至都没有打开自动挂载功能,而基于本文的目的,当设备插入时,脚本和规则可以运行一些命令来做一些事情。

在这里需要记住两个事情。

  1. 聚焦于你的 udev 规则,当你真实地使用它们时,一旦让规则发挥作用将触发脚本。执行一个脚本去盲目地复制数据到你的计算上,或从你的计算机上复制出数据,是一个很糟糕的主意,因为有可能会遇到一个人拿着和你相同品牌的 U 盘插入到你的机器上的情况。
  2. 不要在写了 udev 规则和脚本后忘记了它们的存在。我知道哪个计算上有我的 udev 规则,这些机器一般是我的个人计算机,而不是那些我带着去开会或办公室工作的计算机。一台计算机的 “社交” 程度越高,它就越不能有 udev 规则存在于它上面,因为它将潜在地导致我的数据最终可能会出现在某个人的设备、或某个人的数据中、或在我的设备上出现恶意程序。

换句话说就是,随着一个 GNU 系统提供了一个这么强大的功能,你的任务是小心地如何使用它们的强大功能。如果你滥用它或不小心谨慎地使用它,最终将让你出问题,它非常可能会导致可怕的问题。

现实中的 Udev

现在,你可以确认你的脚本是由 udev 触发的,那么,可以将你的关注点转到脚本功能上了。到目前为止,这个脚本是没有用的,它除了记录脚本已经运行过了这一事实外,再没有做更多的事情。

我使用 udev 去触发我的 U 盘的 自动备份 。这个创意是,将我正在处理的文档的主副本保存在我的 U 盘上(因为我随身带着它,这样就可以随时处理它),并且在我每次将 U 盘插入到那台机器上时,这些主文档将备份回我的计算机上。换句话说就是,我的计算机是备份驱动器,而产生的数据是移动的。源代码是可用的,你可以随意查看 attachup 的代码,以进一步限制你的 udev 的测试示例。

虽然我使用 udev 最多的情况就是这个例子,但是 udev 能抓取很多的事件,像游戏手柄(当连接游戏手柄时,让系统去加载 xboxdrv 模块)、摄像头、麦克风(当指定的麦克风连接时用于去设置输入),所以应该意识到,它能做的事情远比这个示例要多。

我的备份系统的一个简化版本是由两个命令组成的一个过程:

SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", SYMLINK+="safety%n"
SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"

第一行使用属性去检测我的 U 盘,这在前面已经讨论过了,接着在设备树中为我的 U 盘分配一个符号链接,给它分配的符号连接是 safety%n。这个 %n 是一个 udev 宏,它是内核分配给这个设备的任意数字,比如 sdb1、sdb2、sdb3、等等。因此 %n 应该是 1 或 2 或 3。

这将在 dev 树中创建一个符号链接,因此它不会干涉插入一个设备的正常过程。这意味着,如果你在自动挂载设备的桌面环境中使用它,将不会出现问题。

第二行运行这个脚本。

我的备份脚本如下:

#!/usr/bin/bash

mount /dev/safety1 /mnt/hd
sleep 2
rsync -az /mnt/hd/ /home/seth/backups/ && umount /dev/safety1

这个脚本使用符号链接,这将避免出现 udev 命名导致的意外情况(例如,假设一个命名为 DISK 的 U 盘已经插入到我的计算机上,而我插入的其它 U 盘恰好名字也是 DISK,那么第二个 U 盘的卷标将被命名为 DISK_,这将导致我的脚本不会正常运行),它在我喜欢的挂载点 /mnt/hd 上挂载了 safety1(驱动器的第一个分区)。

一旦 safely 挂载之后,它将使用 rsync 将驱动器备份到我的备份文件夹(我真实使用的脚本用的是 rdiff-backup,而你可以使用任何一个你喜欢的自动备份解决方案)。

udev 让你的设备你做主

udev 是一个非常灵活的系统,它可以让你用其它系统很少敢提供给用户的方式去定义规则和功能。学习它,使用它,去享受 POSIX 的强大吧。

本文内容来自 Slackermedia Handbook,它以 GNU Free Documentation License 1.3 许可证授权使用。


via: https://opensource.com/article/18/11/udev

作者:Seth Kenlon 选题:lujun9972 译者:qhwdw 校对:wxy

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