Sachin Patil 发布的文章

了解一下 setV,它是一个轻量级的 Python 虚拟环境管理器,是 virtualenvwrapper 的替代产品。

这一年多来,我的 bash\_scripts 项目中悄悄隐藏这 setV,但现在是时候该公开了。setV 是一个 Bash 函数,我可以用它代替 virtualenvwrapper。它提供了使你能够执行以下操作的基本功能:

  • 默认使用 Python 3
  • 创建一个新的虚拟环境
  • 使用带有 -p(或 --python)的自定义 Python 路径来创建新的虚拟环境
  • 删除现有的虚拟环境
  • 列出所有现有的虚拟环境
  • 使用制表符补全(以防你忘记虚拟环境名称)

安装

要安装 setV,请下载该脚本:

curl https://gitlab.com/psachin/setV/raw/master/install.sh

审核一下脚本,然后运行它:

sh ./install.sh

当安装 setV 时,安装脚本会要求你引入(source)一下 ~/.bashrc~/.bash_profile 的配置,根据你的喜好选择一个。

用法

基本的命令格式是 setv

创建虚拟环境

setv --new rango  # setv -n rango

# 或使用定制的 Python 路径
setv --new --python /opt/python/python3 rango  # setv -n -p /opt/python/python3 rango

激活已有的虚拟环境

setv VIRTUAL_ENVIRONMENT_NAME
# 示例
setv rango

列出所有的虚拟环境

setv --list
# 或
setv [TAB] [TAB]

删除虚拟环境

setv --delete rango

切换到另外一个虚拟环境

# 假设你现在在 'rango',切换到 'tango'
setv tango

制表符补完

如果你不完全记得虚拟环境的名称,则 Bash 式的制表符补全也可以适用于虚拟环境名称。

参与其中

setV 在 GNU GPLv3下开源,欢迎贡献。要了解更多信息,请访问它的 GitLab 存储库中的 setV 的 README 的贡献部分。

setV 脚本

#!/usr/bin/env bash
# setV - A Lightweight Python virtual environment manager.
# Author: Sachin (psachin) <[email protected]>
# Author's URL: https://psachin.gitlab.io/about
#
# License: GNU GPL v3, See LICENSE file
#
# Configure(Optional):
# Set `SETV_VIRTUAL_DIR_PATH` value to your virtual environments
# directory-path. By default it is set to '~/virtualenvs/'
#
# Usage:
# Manual install: Added below line to your .bashrc or any local rc script():
# ---
# source /path/to/virtual.sh
# ---
#
# Now you can 'activate' the virtual environment by typing
# $ setv <YOUR VIRTUAL ENVIRONMENT NAME>
#
# For example:
# $ setv rango
#
# or type:
# setv [TAB] [TAB]  (to list all virtual envs)
#
# To list all your virtual environments:
# $ setv --list
#
# To create new virtual environment:
# $ setv --new new_virtualenv_name
#
# To delete existing virtual environment:
# $ setv --delete existing_virtualenv_name
#
# To deactivate, type:
# $ deactivate

# Path to virtual environment directory
SETV_VIRTUAL_DIR_PATH="$HOME/virtualenvs/"
# Default python version to use. This decides whether to use `virtualenv` or `python3 -m venv`
SETV_PYTHON_VERSION=3  # Defaults to Python3
SETV_PY_PATH=$(which python${SETV_PYTHON_VERSION})

function _setvcomplete_()
{
    # Bash-autocompletion.
    # This ensures Tab-auto-completions work for virtual environment names.
    local cmd="${1##*/}" # to handle command(s).
                         # Not necessary as such. 'setv' is the only command

    local word=${COMP_WORDS[COMP_CWORD]} # Words thats being completed
    local xpat='${word}'                 # Filter pattern. Include
                                         # only words in variable '$names'
    local names=$(ls -l "${SETV_VIRTUAL_DIR_PATH}" | egrep '^d' | awk -F " " '{print $NF}') # Virtual environment names

    COMPREPLY=($(compgen -W "$names" -X "$xpat" -- "$word")) # compgen generates the results
}

function _setv_help_() {
    # Echo help/usage message
    echo "Usage: setv [OPTIONS] [NAME]"
    echo Positional argument:
    echo -e "NAME                       Activate virtual env."
    echo Optional arguments:
    echo -e "-l, --list                 List all Virtual Envs."
    echo -e "-n, --new NAME             Create a new Python Virtual Env."
    echo -e "-d, --delete NAME          Delete existing Python Virtual Env."
    echo -e "-p, --python PATH          Python binary path."
}

function _setv_custom_python_path()
{
    if [ -f "${1}" ];
    then
        if [ "`expr $1 : '.*python\([2,3]\)'`" = "3" ];
        then
            SETV_PYTHON_VERSION=3
        else
            SETV_PYTHON_VERSION=2
        fi
        SETV_PY_PATH=${1}
        _setv_create $2
    else
        echo "Error: Path ${1} does not exist!"
    fi
}

function _setv_create()
{
    # Creates new virtual environment if ran with -n|--new flag
    if [ -z ${1} ];
    then
        echo "You need to pass virtual environment name"
        _setv_help_
    else
        echo "Creating new virtual environment with the name: $1"

        if [ ${SETV_PYTHON_VERSION} -eq 3 ];
        then
            ${SETV_PY_PATH} -m venv ${SETV_VIRTUAL_DIR_PATH}${1}
        else
            virtualenv -p ${SETV_PY_PATH} ${SETV_VIRTUAL_DIR_PATH}${1}
        fi

        echo "You can now activate the Python virtual environment by typing: setv ${1}"
    fi
}

function _setv_delete()
{
    # Deletes virtual environment if ran with -d|--delete flag
    # TODO: Refactor
    if [ -z ${1} ];
    then
        echo "You need to pass virtual environment name"
        _setv_help_
    else
        if [ -d ${SETV_VIRTUAL_DIR_PATH}${1} ];
        then
            read -p "Really delete this virtual environment(Y/N)? " yes_no
            case $yes_no in
                Y|y) rm -rvf ${SETV_VIRTUAL_DIR_PATH}${1};;
                N|n) echo "Leaving the virtual environment as it is.";;
                *) echo "You need to enter either Y/y or N/n"
            esac
        else
            echo "Error: No virtual environment found by the name: ${1}"
        fi
    fi
}

function _setv_list()
{
    # Lists all virtual environments if ran with -l|--list flag
    echo -e "List of virtual environments you have under ${SETV_VIRTUAL_DIR_PATH}:\n"
    for virt in $(ls -l "${SETV_VIRTUAL_DIR_PATH}" | egrep '^d' | awk -F " " '{print $NF}')
    do
        echo ${virt}
    done
}

function setv() {
    # Main function
    if [ $# -eq 0 ];
    then
        _setv_help_
    elif [ $# -le 3 ];
    then
        case "${1}" in
            -n|--new) _setv_create ${2};;
            -d|--delete) _setv_delete ${2};;
            -l|--list) _setv_list;;
            *) if [ -d ${SETV_VIRTUAL_DIR_PATH}${1} ];
               then
                   # Activate the virtual environment
                   source ${SETV_VIRTUAL_DIR_PATH}${1}/bin/activate
               else
                   # Else throw an error message
                   echo "Sorry, you don't have any virtual environment with the name: ${1}"
                   _setv_help_
               fi
               ;;
        esac
    elif [ $# -le 5 ];
    then
        case "${2}" in
            -p|--python) _setv_custom_python_path ${3} ${4};;
            *) _setv_help_;;
        esac
    fi
}

# Calls bash-complete. The compgen command accepts most of the same
# options that complete does but it generates results rather than just
# storing the rules for future use.
complete  -F _setvcomplete_ setv

via: https://opensource.com/article/20/1/setv-bash-function

作者:Sachin Patil 选题:lujun9972 译者:wxy 校对:wxy

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

Emacs 的 Magit 扩展插件使得使用 Git 进行版本控制变得简单起来。

Git 是一个很棒的用于项目管理的 版本控制 工具,就是新人学习起来太难。Git 的命令行工具很难用,你不仅需要熟悉它的标志和选项,还需要知道什么环境下使用它们。这使人望而生畏,因此不少人只会非常有限的几个用法。

好在,现今大多数的集成开发环境 (IDE) 都包含了 Git 扩展,大大地简化了使用使用的难度。Emacs 中就有这么一款 Git 扩展名叫 Magit

Magit 项目成立有差不多 10 年了,它将自己定义为 “一件 Emacs 内的 Git 瓷器”。也就是说,它是一个操作界面,每个操作都能一键完成。本文会带你领略一下 Magit 的操作界面并告诉你如何使用它来管理 Git 项目。

若你还没有做,请在开始本教程之前先 安装 Emacs,再 安装 Magit

Magit 的界面

首先用 Emacs 的 Dired 模式 访问一个项目的目录。比如我所有的 Emacs 配置存储在 ~/.emacs.d/ 目录中,就是用 Git 来进行管理的。

若你在命令行下工作,则你需要输入 git status 来查看项目的当前状态。Magit 也有类似的功能:magit-status。你可以通过 M-x magit-status (快捷方式是 Alt+x magit-status )来调用该功能。结果看起来像下面这样:

Magit 显示的信息比 git status 命令的要多得多。它分别列出了未追踪文件列表、未暂存文件列表以及已暂存文件列表。它还列出了 储藏 stash 列表以及最近几次的提交 —— 所有这些信息都在一个窗口中展示。

如果你想查看修改了哪些内容,按下 Tab 键。比如,我移动光标到未暂存的文件 custom_functions.org 上,然后按下 Tab 键,Magit 会显示修改了哪些内容:

这跟运行命令 git diff custom_functions.org 类似。储藏文件更简单。只需要移动光标到文件上然后按下 s 键。该文件就会迅速移动到已储藏文件列表中:

反储藏 unstage 某个文件,使用 u 键。按下 su 键要比在命令行输入 git add -u <file>git reset HEAD <file> 快的多也更有趣的多。

提交更改

在同一个 Magit 窗口中,按下 c 键会显示一个提交窗口,其中提供了许多标志,比如 --all 用来暂存所有文件或者 --signoff 来往提交信息中添加签名行。

将光标移动到想要启用签名标志的行,然后按下回车。--signoff 文本会变成高亮,这说明该标志已经被启用。

再次按下 c 键会显示一个窗口供你输入提交信息。

最后,使用 C-c C-c(按键 Ctrl+cc 的缩写形式) 来提交更改。

推送更改

更改提交后,提交行将会显示在 Recent commits 区域中显示。

将光标放到该提交处然后按下 p 来推送该变更。

若你想感受一下使用 Magit 的感觉,我已经在 YouTube 上传了一段 演示。本文只涉及到 Magit 的一点皮毛。它有许多超酷的功能可以帮你使用 Git 分支、变基等功能。你可以在 Magit 的主页上找到 文档、支持,以及更多 的链接。


via: https://opensource.com/article/19/1/how-use-magit

作者:Sachin Patil 选题:lujun9972 译者:lujun9972 校对:wxy

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

这篇教程将带你遍历在 Emacs 使用强大的开源排版系统 LaTex 来创建文档的全过程。

一篇由 Aaron Cocker 写的很棒的文章 “在 LaTeX 中创建文件的介绍” 中,介绍了 LaTeX 排版系统 并描述了如何使用 TeXstudio 来创建 LaTeX 文档。同时,他也列举了一些很多用户觉得创建 LaTeX 文档很方便的编辑器。

Greg Pittman 对这篇文章的评论吸引了我:“当你第一次开始使用 LaTeX 时,他似乎是个很差劲的排版……” 事实也确实如此。LaTeX 包含了多种排版字体和调试,如果你漏了一个特殊的字符比如说感叹号,这会让很多用户感到沮丧,尤其是新手。在本文中,我将介绍如何使用 GNU Emacs 来创建 LaTeX 文档。

创建你的第一个文档

启动 Emacs:

emacs -q --no-splash helloworld.org

参数 -q 确保 Emacs 不会加载其他的初始化配置。参数 --no-splash-screen 防止 Emacs 打开多个窗口,确保只打开一个窗口,最后的参数 helloworld.org 表示你要创建的文件名为 helloworld.org

 title=

GNU Emacs 打开文件名为 helloworld.org 的窗口时的样子。

现在让我们用 Emacs 添加一些 LaTeX 的标题吧:在菜单栏找到 “Org” 选项并选择 “Export/Publish”。

 title=

导入一个默认的模板

在下一个窗口中,Emacs 同时提供了导入和导出一个模板。输入 #(“[#] Insert template”)来导入一个模板。这将会使光标跳转到一个带有 “Options category:” 提示的 mini-buffer 中。第一次你可能不知道这个类型的名字,但是你可以使用 Tab 键来查看所有的补全。输入 “default” 然后按回车,之后你就能看到如下的内容被插入了:

#+TITLE: helloworld
#+DATE: <2018-03-12 Mon>
#+AUTHOR:
#+EMAIL: makerpm@nubia
#+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline
#+OPTIONS: author:t c:nil creator:comment d:(not "LOGBOOK") date:t
#+OPTIONS: e:t email:nil f:t inline:t num:t p:nil pri:nil stat:t
#+OPTIONS: tags:t tasks:t tex:t timestamp:t toc:t todo:t |:t
#+CREATOR: Emacs 25.3.1 (Org mode 8.2.10)
#+DESCRIPTION:
#+EXCLUDE_TAGS: noexport
#+KEYWORDS:
#+LANGUAGE: en
#+SELECT_TAGS: export

根据自己的需求修改标题、日期、作者和 email。我自己的话是下面这样的:

#+TITLE: Hello World! My first LaTeX document
#+DATE: \today
#+AUTHOR: Sachin Patil
#+EMAIL: [email protected]

我们目前还不想创建一个目录,所以要将 toc 的值由 t 改为 nil,具体如下:

#+OPTIONS: tags:t tasks:t tex:t timestamp:t toc:nil todo:t |:t

现在让我们添加一个章节和段落吧。章节是由一个星号(*)开头。我们从 Aaron 的贴子(来自 Lipsum Lorem Ipsum 生成器)复制一些文本过来:

* Introduction

  \paragraph{}
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras lorem
  nisi, tincidunt tempus sem nec, elementum feugiat ipsum. Nulla in
  diam libero. Nunc tristique ex a nibh egestas sollicitudin.

  \paragraph{}
  Mauris efficitur vitae ex id egestas. Vestibulum ligula felis,
  pulvinar a posuere id, luctus vitae leo. Sed ac imperdiet orci, non
  elementum leo. Nullam molestie congue placerat. Phasellus tempor et
  libero maximus commodo.

 title=

helloworld.org 文件

将内容修改好后,我们要把它导出为 PDF 格式。再次在 “Org” 的菜单选项中选择 “Export/Publish”,但是这次,要输入 l(“export to LaTeX”),紧跟着输入 o(“as PDF file and open”)。这次操作不止会打开 PDF 文件让你浏览,同时也会将文件保存为 helloworld.pdf,并保存在与 helloworld.org 的同一个目录下。

 title=

将 helloworld.org 导出为 helloworld.pdf

 title=

打开 helloworld.pdf 文件

你也可以按下 Alt + x 键,然后输入 org-latex-export-to-pdf 来将 org 文件导出为 PDF 文件。可以使用 Tab 键来自动补全命令。

Emacs 也会创建 helloworld.tex 文件来让你控制具体的内容。

 title=

Emacs 在三个不同的窗口中分别打开 LaTeX,org 和 PDF 文档。

你可以使用命令来将 .tex 文件转换为 .pdf 文件:

pdflatex helloworld.tex

你也可以将 .org 文件输出为 HTML 或是一个简单的文本格式的文件。我最喜欢 .org 文件的原因是他们可以被推送到 GitHub 上,然后同 markdown 一样被渲染。

创建一个 LaTeX 的 Beamer 简报

现在让我们更进一步,通过少量的修改上面的文档来创建一个 LaTeX Beamer 简报,如下所示:

#+TITLE: LaTeX Beamer presentation
#+DATE: \today
#+AUTHOR: Sachin Patil
#+EMAIL: [email protected]
#+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline
#+OPTIONS: author:t c:nil creator:comment d:(not "LOGBOOK") date:t
#+OPTIONS: e:t email:nil f:t inline:t num:t p:nil pri:nil stat:t
#+OPTIONS: tags:t tasks:t tex:t timestamp:t toc:nil todo:t |:t
#+CREATOR: Emacs 25.3.1 (Org mode 8.2.10)
#+DESCRIPTION:
#+EXCLUDE_TAGS: noexport
#+KEYWORDS:
#+LANGUAGE: en
#+SELECT_TAGS: export
#+LATEX_CLASS: beamer
#+BEAMER_THEME: Frankfurt
#+BEAMER_INNER_THEME: rounded


* Introduction
*** Programming
    - Python
    - Ruby

*** Paragraph one

    Lorem ipsum dolor sit amet, consectetur adipiscing
    elit. Cras lorem nisi, tincidunt tempus sem nec, elementum feugiat
    ipsum. Nulla in diam libero. Nunc tristique ex a nibh egestas
    sollicitudin.

*** Paragraph two

    Mauris efficitur vitae ex id egestas. Vestibulum
    ligula felis, pulvinar a posuere id, luctus vitae leo. Sed ac
    imperdiet orci, non elementum leo. Nullam molestie congue
    placerat. Phasellus tempor et libero maximus commodo.

* Thanks
*** Links
    - Link one
    - Link two

我们给标题增加了三行:

#+LATEX_CLASS: beamer
#+BEAMER_THEME: Frankfurt
#+BEAMER_INNER_THEME: rounded

导出为 PDF,按下 Alt + x 键后输入 org-beamer-export-to-pdf

 title=

用 Emacs 和 Org 模式创建的 Latex Beamer 简报

希望你会爱上使用 Emacs 来创建 LaTex 和 Beamer 文档(注意:使用快捷键比用鼠标更快些)。Emacs 的 Org 模式提供了比我在这篇文章中说的更多的功能,你可以在 orgmode.org 获取更多的信息.


via: https://opensource.com/article/18/4/how-create-latex-documents-emacs

作者:Sachin Patil 选题:lujun9972 译者:oneforalone 校对:wxy

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

用这个方便的工具来更有效的运行和编译你的程序。

当你需要在一些源文件改变后运行或更新一个任务时,通常会用到 make 工具。make 工具需要读取一个 Makefile(或 makefile)文件,在该文件中定义了一系列需要执行的任务。你可以使用 make 来将源代码编译为可执行程序。大部分开源项目会使用 make 来实现最终的二进制文件的编译,然后使用 make install 命令来执行安装。

本文将通过一些基础和进阶的示例来展示 makeMakefile 的使用方法。在开始前,请确保你的系统中安装了 make

基础示例

依然从打印 “Hello World” 开始。首先创建一个名字为 myproject 的目录,目录下新建 Makefile 文件,文件内容为:

say_hello:
        echo "Hello World"

myproject 目录下执行 make,会有如下输出:

$ make
echo "Hello World"
Hello World

在上面的例子中,“say\_hello” 类似于其他编程语言中的函数名。这被称之为 目标 target 。在该目标之后的是预置条件或依赖。为了简单起见,我们在这个示例中没有定义预置条件。echo ‘Hello World' 命令被称为 步骤 recipe 。这些步骤基于预置条件来实现目标。目标、预置条件和步骤共同构成一个规则。

总结一下,一个典型的规则的语法为:

目标: 预置条件
<TAB> 步骤

作为示例,目标可以是一个基于预置条件(源代码)的二进制文件。另一方面,预置条件也可以是依赖其他预置条件的目标。

final_target: sub_target final_target.c
        Recipe_to_create_final_target
        
sub_target: sub_target.c
        Recipe_to_create_sub_target

目标并不要求是一个文件,也可以只是步骤的名字,就如我们的例子中一样。我们称之为“伪目标”。

再回到上面的示例中,当 make 被执行时,整条指令 echo "Hello World" 都被显示出来,之后才是真正的执行结果。如果不希望指令本身被打印处理,需要在 echo 前添加 @

say_hello:
        @echo "Hello World"

重新运行 make,将会只有如下输出:

$ make
Hello World

接下来在 Makefile 中添加如下伪目标:generateclean

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

随后当我们运行 make 时,只有 say_hello 这个目标被执行。这是因为Makefile 中的第一个目标为默认目标。通常情况下会调用默认目标,这就是你在大多数项目中看到 all 作为第一个目标而出现。all 负责来调用它他的目标。我们可以通过 .DEFAULT_GOAL 这个特殊的伪目标来覆盖掉默认的行为。

Makefile 文件开头增加 .DEFAULT_GOAL

.DEFAULT_GOAL := generate

make 会将 generate 作为默认目标:

$ make
Creating empty text files...
touch file-{1..10}.txt

顾名思义,.DEFAULT_GOAL 伪目标仅能定义一个目标。这就是为什么很多 Makefile 会包括 all 这个目标,这样可以调用多个目标。

下面删除掉 .DEFAULT_GOAL,增加 all 目标:

all: say_hello generate

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

运行之前,我们再增加一些特殊的伪目标。.PHONY 用来定义这些不是文件的目标。make 会默认调用这些伪目标下的步骤,而不去检查文件名是否存在或最后修改日期。完整的 Makefile 如下:

.PHONY: all say_hello generate clean

all: say_hello generate

say_hello:
        @echo "Hello World"

generate:
        @echo "Creating empty text files..."
        touch file-{1..10}.txt

clean:
        @echo "Cleaning up..."
        rm *.txt

make 命令会调用 say_hellogenerate

$ make
Hello World
Creating empty text files...
touch file-{1..10}.txt

clean 不应该被放入 all 中,或者被放入第一个目标中。clean 应当在需要清理时手动调用,调用方法为 make clean

$ make clean
Cleaning up...
rm *.txt

现在你应该已经对 Makefile 有了基础的了解,接下来我们看一些进阶的示例。

进阶示例

变量

在之前的实例中,大部分目标和预置条件是已经固定了的,但在实际项目中,它们通常用变量和模式来代替。

定义变量最简单的方式是使用 = 操作符。例如,将命令 gcc 赋值给变量 CC

CC = gcc

这被称为递归扩展变量,用于如下所示的规则中:

hello: hello.c
    ${CC} hello.c -o hello

你可能已经想到了,这些步骤将会在传递给终端时展开为:

gcc hello.c -o hello

${CC}$(CC) 都能对 gcc 进行引用。但如果一个变量尝试将它本身赋值给自己,将会造成死循环。让我们验证一下:

CC = gcc
CC = ${CC}

all:
    @echo ${CC}

此时运行 make 会导致:

$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually).  Stop.

为了避免这种情况发生,可以使用 := 操作符(这被称为简单扩展变量)。以下代码不会造成上述问题:

CC := gcc
CC := ${CC}

all:
    @echo ${CC}

模式和函数

下面的 Makefile 使用了变量、模式和函数来实现所有 C 代码的编译。我们来逐行分析下:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)

all: ${BINS}

%: %.o
        @echo "Checking.."
        ${CC} ${LINKERFLAG} $< -o $@

%.o: %.c
        @echo "Creating object.."
        ${CC} -c $<

clean:
        @echo "Cleaning up..."
        rm -rvf *.o ${BINS}
  • # 开头的行是评论。
  • .PHONY = all clean 行定义了 allclean 两个伪目标。
  • 变量 LINKERFLAG 定义了在步骤中 gcc 命令需要用到的参数。
  • SRCS := $(wildcard *.c)$(wildcard pattern) 是与文件名相关的一个函数。在本示例中,所有 “.c”后缀的文件会被存入 SRCS 变量。
  • BINS := $(SRCS:%.c=%):这被称为替代引用。本例中,如果 SRCS 的值为 'foo.c bar.c',则 BINS的值为 'foo bar'
  • all: ${BINS} 行:伪目标 all 调用 ${BINS} 变量中的所有值作为子目标。
  • 规则:
%: %.o
  @echo "Checking.."
  ${CC} ${LINKERFLAG} $&lt; -o $@

下面通过一个示例来理解这条规则。假定 foo 是变量 ${BINS} 中的一个值。% 会匹配到 foo%匹配任意一个目标)。下面是规则展开后的内容:

foo: foo.o
  @echo "Checking.."
  gcc -lm foo.o -o foo

如上所示,%foo 替换掉了。$<foo.o 替换掉。$<用于匹配预置条件,$@ 匹配目标。对 ${BINS} 中的每个值,这条规则都会被调用一遍。

  • 规则:
%.o: %.c
  @echo "Creating object.."
  ${CC} -c $&lt;

之前规则中的每个预置条件在这条规则中都会都被作为一个目标。下面是展开后的内容:

foo.o: foo.c
  @echo "Creating object.."
  gcc -c foo.c
  • 最后,在 clean 目标中,所有的二进制文件和编译文件将被删除。

下面是重写后的 Makefile,该文件应该被放置在一个有 foo.c 文件的目录下:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
        @echo "Checking.."
        gcc -lm foo.o -o foo

foo.o: foo.c
        @echo "Creating object.."
        gcc -c foo.c

clean:
        @echo "Cleaning up..."
        rm -rvf foo.o foo

关于 Makefile 的更多信息,GNU Make 手册提供了更完整的说明和实例。


via: https://opensource.com/article/18/8/what-how-makefile

作者:Sachin Patil 选题:lujun9972 译者:Zafiry 校对:wxy

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

在 Linux 中有几种使用命令行或图形界面终止一个程序的方式。

进程出错的时候,您可能会想要中止或是杀掉这个进程。在本文中,我们将探索在命令行和图形界面中终止进程或是应用程序,这里我们使用 gedit 作为样例程序。

使用命令行或字符终端界面

Ctrl + C

在命令行中调用 gedit (如果您没有使用 gedit & 命令)程序的一个问题是 shell 会话被阻塞,没法释放命令行提示符。在这种情况下,Ctrl + CCtrlC 的组合键) 会很管用。这会终止 gedit ,并且所有的工作都将丢失(除非文件已经被保存)。Ctrl + C 会给 gedit 发送了 SIGINT 信号。这是一个默认终止进程的停止信号,它将指示 shell 停止 gedit 的运行,并返回到主函数的循环中,您将返回到提示符。

$ gedit
^C

Ctrl + Z

它被称为挂起字符。它会发送 SIGTSTP 信号给进程。它也是一个停止信号,但是默认行为不是杀死进程,而是挂起进程。

下面的命令将会停止(杀死/中断) gedit 的运行,并返回到 shell 提示符。

$ gedit
^Z
[1]+  Stopped                 gedit
$

一旦进程被挂起(以 gedit 为例),将不能在 gedit 中写入或做任何事情。而在后台,该进程变成了一个作业,可以使用 jobs 命令验证。

$ jobs
[1]+  Stopped                 gedit

jobs 允许您在单个 shell 会话中控制多个进程。您可以终止,恢复作业,或是根据需要将作业移动到前台或是后台。

让我们在后台恢复 gedit,释放提示符以运行其它命令。您可以通过 bg 命令来做到,后跟作业 ID(注意上面的 jobs 命令显示出来的 [1],这就是作业 ID)。

$ bg 1
[1]+ gedit &amp;

这和直接使用 gedit & 启动程序效果差不多:

$ gedit &amp;

使用 kill

kill 命令提供信号的精确控制,允许您通过指定信号名或是信号数字为进程发送信号,后跟进程 ID 或是 PID。

我喜欢 kill 命令的一点是它也能够根据作业 ID 控制进程。让我们使用 gedit & 命令在后台开启 gedit 服务。假设通过 jobs 命令我得到了一个 gedit 的作业 ID,让我们为 gedit 发送 SIGINT 信号:

$ kill -s SIGINT %1

作业 ID 需要使用 % 前缀,不然 kill 会将其视作 PID。

不明确指定信号,kill 仍然可以工作。此时,默认会发送能中断进程的 SIGTERM 信号。执行 kill -l 可以查看信号名列表,使用 man kill 命令阅读手册。

使用 killall

如果您不想使用特定的工作 ID 或者 PID,killall 允许您使用特定的进程名。中断 gedit 最简单的 killall 使用方式是:

$ killall gedit

它将终止所有名为 gedit 的进程。和 kill 相似,默认发送的信号是 SIGTERM。使用 -I 选项忽略进程名的大小写。

$ gedit &amp;
[1] 14852

$ killall -I GEDIT
[1]+  Terminated              gedit

查看手册学习更多 killall 命令选项(如 -u)。

使用 xkill

您是否遇见过播放器崩溃,比如 VLC 灰屏或挂起?现在你可以像上面一样获得进程的 PID 来杀掉它,或者使用 xkill 命令终止应用程序。

 title=

xkill 允许您使用鼠标关闭窗口。仅需在终端执行 xkill 命令,它将会改变鼠标光标为一个 X 或是一个小骷髅图标。在你想关闭的进程窗口上点击 x。小心使用 xkill,如手册描述的一致,它很危险。我已经提醒过您了!

参阅手册,了解上述命令更多信息。您还可以接续探索 pkillpgrep 命令。


via: https://opensource.com/article/18/5/how-kill-process-stop-program-linux

作者:Sachin Patil 选题:lujun9972 译者:CYLeft 校对:wxy

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