标签 参数 下的文章

通过使用命令行让用户告诉程序要什么,可以让程序更加灵活。

 title=

在已经知道要处理什么文件和对文件进行哪些操作的情况下,编写处理文件的 C 语言程序就很容易了。如果将文件名“硬编码”在程序中,或者你的程序只以一种方式来处理文件,那么你的程序总是知道要做什么。

但是如果程序每次运行时能够对用户的输入做出反应,可以使程序更灵活。让用户告诉程序要处理什么文件,或者以不同的方式完成任务,要实现这样的功能就需要读取命令行参数。

读取命令行

一个 C 语言程序可以用如下声明开头:

int main()

这是启动 C 程序最简单的形式。但如果在圆括号中加入标准参数,你的程序就可以从命令行中读取选项了:

int main(int argc, char **argv)

argc 表示命令行中的参数个数。它总是一个至少为 1 的数。

argv 是一个二级指针,它指向一个字符串数组。这个数组中保存的是从命令行接收的各个参数。数组的第一个元素 *argv[0] 是程序的名称。**argv 数组的其它元素包含剩下的命令行参数。

下面我将写一个简单的示例程序,它能够回显通过命令行参数传递给它的选项。它跟 Linux 的 echo 命令类似,只不过我们的程序会打印出程序名。同时它还会调用 puts 函数将命令行选项按行打印输出。

#include <stdio.h>

int
main(int argc, char **argv)
{
  int i;

  printf("argc=%d\n", argc); /* debugging */

  for (i = 0; i < argc; i++) {
    puts(argv[i]);
  }

  return 0;
}

编译此程序,并在运行时提供一些命令行参数,你会看到传入的命令行参数被逐行打印出来:

$ ./echo this program can read the command line
argc=8
./echo
this
program
can
read
the
command
line

这个命令行将程序的 argc 置为 8,**argv 数组包含 8 个元素:程序名以及用户输入的 7 个单词。由于 C 语言中数组下标从 0 开始,所以这些元素的标号分别是 0 到 7。这也是在 for 循环中处理命令行参数时能够用 i < argc 作为比较条件的原因。

你也可以用这个方式实现自己的 catcp 命令。cat 命令的基本功能是显示一个或几个文件的内容。下面是一个简化版的cat 命令,它从命令行获取文件名:

#include <stdio.h>

void
copyfile(FILE *in, FILE *out)
{
  int ch;

  while ((ch = fgetc(in)) != EOF) {
    fputc(ch, out);
  }
}

int
main(int argc, char **argv)
{
  int i;
  FILE *fileptr;

  for (i = 1; i < argc; i++) {
    fileptr = fopen(argv[i], "r");

    if (fileptr != NULL) {
      copyfile(fileptr, stdout);
      fclose(fileptr);
    }
  }

  return 0;
}

这个简化版的 cat 命令从命令行读取文件名列表,然后将各个文件的内容逐字符地显示到标准输出上。假定我有一个叫做 hello.txt 的文件,其中包含数行文本内容。我能用自己实现的 cat 命令将它的内容显示出来:

$ ./cat hello.txt
Hi there!
This is a sample text file.

以这个简单程序为出发点,你也可以实现自己版本的其它 Linux 命令。比如 cp 命令,它从命令行读取两个文件名:要读取的文件和要写入的文件。

读取命令行选项

通过命令行读取文件名和其它文本固然很棒,但是如果想要程序根据用户给出的选项改变行为呢?比如 Linux 的 cat 命令就支持以下命令行选项:

  • -b 显示非空行的行号
  • -E 在行尾显示 $
  • -n 显示行号
  • -s 合并显示空行
  • -T 将制表符显示为 ^I
  • -v^xM-x 方式显示非打印字符,换行符和制表符除外

这些以一个连字符开头的单字母的选项叫做短选项。通常短选项是分开使用的,就像这样 cat -E -n。但是也可以将多个短选项合并,比如 cat -En

值得庆幸的是,所有 Linux 和 Unix 系统都包含 getopt 库。它提供了一种简单的方式来读取命令行参数。getopt 定义在头文件 unistd.h 中。你可以在程序中使用 getopt 来读取命令行短选项。

与其它 Unix 系统不同的是,Linux 上的 getopt 总是保证短选项出现在命令行参数的最前面。比如,用户输入的是 cat -E file -n-E 在最前面,-n 在文件名之后。如果使用 Linux 的 getopt 来处理,程序会认为用户输入的是 cat -E -n file。这样做可以使处理过程更顺畅,因为 getopt 可以解析完所有短选项,剩下的文件名列表可以通过 **argv 来统一处理。

你可以这样使用 getopt:

#include <unistd.h>

int getopt(int argc, char **argv, char *optstring);

optstring 是由所有合法的选项字符组成的字符串。比如你的程序允许的选项是 -E-n, 那么 optstring 的值就是 "En"

通常通过在循环中调用 getopt 来解析命令行选项。每次调用时 getopt 会返回找到的下一个短选项,如果遇到无法识别的选项则返回 '?'。当没有更多短选项时它返回 -1,并且设置全局变量 optind 的值指向 **argv 中所有段选项之后的第一个元素。

下面看一个简单的例子。这个演示程序没有实现 cat 命令的所有选项,但它只是能够解析命令行。每当发现一个合法的命令行选项,它就打印出相应的提示消息。在你自己的程序中,你可能会根据这些命令行选项执行变量赋值等者其它操作。

#include <stdio.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
  int i;
  int option;

  /* parse short options */

  while ((option = getopt(argc, argv, "bEnsTv")) != -1) {
    switch (option) {
    case 'b':
      puts("Put line numbers next to non-blank lines");
      break;
    case 'E':
      puts("Show the ends of lines as $");
      break;
    case 'n':
      puts("Put line numbers next to all lines");
      break;
    case 's':
      puts("Suppress printing repeated blank lines");
      break;
    case 'T':
      puts("Show tabs as ^I");
      break;
    case 'v':
      puts("Verbose");
      break;
    default:                          /* '?' */
      puts("What's that??");
    }
  }

  /* print the rest of the command line */

  puts("------------------------------");

  for (i = optind; i < argc; i++) {
    puts(argv[i]);
  }

  return 0;
}

假如你把程序编译为 args,你可以通过尝试不同的命令行参数组合,来了解程序是怎么解析短选项,以及是怎么将其它的命令行参数留下来的。最简单的例子是将所有的选项都放在最前面,就像这样:

$ ./args -b -T file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2

现在试试将两个短选项合并使用的效果:

$ ./args -bT file1 file2
Put line numbers next to non-blank lines
Show tabs as ^I
------------------------------
file1
file2

如果有必要的话,getopt可以对命令行参数进行重排:

$ ./args -E file1 file2 -T
Show the ends of lines as $
Show tabs as ^I
------------------------------
file1
file2

如果用户输入了错误的短选项,getopt 会打印一条消息:

$ ./args -s -an file1 file2
Suppress printing repeated blank lines
./args: invalid option -- 'a'
What's that??
Put line numbers next to all lines
------------------------------
file1
file2

下载速查表

getopt 还有更多的功能。例如,通过设计 -s string-f file 这样的命令行语法规则,可以让短选项拥有自己的二级选项。你也可以告诉 getopt 在遇到无法识别的选项时不显示错误信息。使用 man 3 getopt 命令查看 getopt(3) 手册可以了解 getopt 的更多功能。

如果你需要 getopt()getopt_long()的使用语法和结构上的提示,可以 下载我制作的速查表。它提供了最小可行代码,并列出了你需要了解的一些全局变量的含义。速查表的一面是 getopt() 的用法,另一面是 getopt_long()的用法。


via: https://opensource.com/article/21/8/short-option-parsing-c

作者:Jim Hall 选题:lujun9972 译者:toknow-gh 校对:wxy

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

参数对于交互式计算至关重要,Lua 编程语言提供了 {...} 表达式来封装在启动 Lua 脚本时给定的可变参数。

大多数计算机命令由两部分组成:命令和参数。命令是要执行的程序,而参数可能是命令选项或用户输入。如果没有这种结构,用户将不得不编辑命令的代码,以改变命令所处理的数据。想象一下重写 printf 命令只是为了让你的计算机用 “hello world” 消息问候你。参数对于交互式计算至关重要,Lua 编程语言 提供了 {...} 表达式来封装在启动 Lua 脚本时给定的可变参数。

在 Lua 中使用参数

几乎每一个给计算机的命令都假定一个参数,即使它期望参数是一个空列表。 Lua 会记录启动后写入的内容,即使你可能对这些参数不做任何操作。要在 Lua 启动时使用用户提供的参数,请迭代 {...} 表:

local args = {...}

for i,v in ipairs(args) do
    print(v)
end

运行代码:

$ lua ./myargs.lua
$ lua ./myargs.lua foo --bar baz
foo
--bar
baz
----

参数是不安全的,Lua 会完全按照输入的方式打印所有参数。

解析参数

对于简单的命令,Lua 的基本功能足以解析和处理参数。这是一个简单的例子:

-- setup

local args = {...}

-- engine

function echo(p)
   print(p)
end

-- go

for i,v in ipairs(args) do
  print(i .. ": " .. v)
end

for i,v in ipairs(args) do
  if args[i] == "--say" then
    echo("echo: " .. args[i+1])
  end
end

setup 部分,将所有命令参数转储到名为 args 的变量中。

engine 部分,创建一个名为 echo 的函数,用于打印你“输入”其中的任何内容。

最后,在 go 部分,解析 args 变量(用户在启动时提供的参数)中的索引和值。在此示例代码中,为清楚起见,第一个 for 循环仅打印每个索引和值。

第二个 for 循环使用索引来检查第一个参数,它被假定是一个选项。此示例代码中唯一有效的选项是 --say。如果循环找到字符串 --say,它会调用 echo 函数,并将当前参数的索引 加 1下一个 参数)作为函数参数提供。

命令参数的分隔符是一个或多个空格。运行代码查看结果:

$ lua ./echo.lua --say zombie apocalypse
1: --say
2: zombie
3: apocalypse
echo: zombie

大多数用户都知道在向计算机发出命令时空格很重要,因此在这种情况下删除第三个参数是预期的行为。下面是演示两种有效“转义”方法的变体:

$ lua ./echo.lua --say "zombie apocalypse"
1: --say
2: zombie apocalypse
echo: zombie apocalypse

$ lua ./echo.lua --say zombie\ apocalypse
1: --say
2: zombie apocalypse
echo: zombie apocalypse

解析参数

手动解析参数简单且无依赖性。但是,你必须考虑一些细节。大多数现代命令都允许使用短选项(例如,-f)和长选项(--foo),并且大多数命令都提供 -h--help 或者在没有所需参数时显示帮助菜单。

使用 LuaRocks 可以轻松安装其他库。有一些非常好的工具,例如 alt-getopt,它们为解析参数提供了额外的基础设施。


via: https://opensource.com/article/22/11/lua-command-arguments

作者:Seth Kenlon 选题:lkxed 译者:geekpi 校对:wxy

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

为你的用户提供选项是任何应用的一个重要功能,而 Commander.js 使它变得容易做到。你最喜欢的 JavaScript 命令行构建器是什么?

 title=

JavaScript 是一种为 Web 开发的语言,但它的用处已经远远超出了互联网的范畴。由于 Node.js 和 Electron 这样的项目,JavaScript 既是一种通用的脚本语言,也是一种浏览器组件。有专门设计的 JavaScript 库来构建命令行界面。是的,你可以在你的终端中运行 JavaScript。

现在,当你在终端中输入一个命令时,一般都有 选项,也叫 开关标志,你可以用来修改命令的运行方式。这是由 POSIX 规范 定义的一个有用的惯例,所以作为一个程序员,知道如何检测和解析这些选项是很有帮助的。要从 JavaScript 获得此功能,使用旨在简化构建命令行界面的库很有用。我最喜欢的是 Commander.js。它很简单,很灵活,而且很直观。

安装 node

要使用 Commander.js 库,你必须安装 Node.js。在 Linux 上,你可以用你的包管理器安装 Node。例如,在 Fedora、CentOS、Mageia 和其他系统上:

$ sudo dnf install nodejs

在 Windows 和 macOS 上,你可以 从 nodejs.org 网站下载安装程序

安装 Commander.js

要安装 Commander.js,请使用 npm 命令:

$ npm install commander

在你的 JavaScript 代码中添加一个库

在 JavaScript 中,你可以使用 require 关键字在你的代码中包含(或者导入,如果你习惯于 Python)一个库。创建一个名为 example.js 的文件,并在你喜欢的文本编辑器中打开它。在顶部添加这一行,以包括 Commander.js 库:

const { program } = require('commander');

JavaScript 中的选项解析

要解析选项,你必须做的第一件事是定义你的应用可以接受的有效选项。Commander.js 库可以让你定义短选项和长选项,同时还有一个有用的信息来澄清每个选项的目的。

program
  .description('A sample application to parse options')
  .option('-a, --alpha', 'Alpha')
  .option('-b, --beta <VALUE>', 'Specify a VALUE', 'Foo');

第一个选项,我称之为 --alpha(简写 -a),是一个布尔型开关:它要么存在,要么不存在。它不需要任何参数。第二个选项,我称之为 --beta(简写 -b),接受一个参数,甚至在你没有提供任何参数的情况下指定一个默认值。

访问命令行数据

当你定义了有效的选项,你就可以使用长的选项名称来引用这些值:

program.parse();

const options = program.opts();
console.log('Options detected:');

if (options.alpha) console.log('alpha');
 
const beta = !options.beta ? 'no' : options.beta;
console.log('beta is: %s', beta);

运行应用

试着用 node 命令来运行它,首先不使用选项:

$ node ./example.js 
Options detected: 
beta is: Foo

在用户没有覆盖的情况下,beta 的默认值被使用。

再次运行它,这次使用选项:

$ node ./example.js --beta hello --alpha
Options detected: 
alpha
beta is: hello

这次,测试脚本成功检测到了选项 --alpha,以及用户提供的 --beta 选项的值。

选项解析

下面是完整的演示代码供你参考:

const { program } = require('commander');

program
  .description('A sample application to parse options')
  .option('-a, --alpha', 'Alpha')
    .option('-b, --beta <VALUE>', 'Specify a VALUE', 'Foo');

program.parse();

const options = program.opts();
console.log('Options detected:');

console.log(typeof options);

if (options.alpha) console.log(' * alpha');
const beta = !options.beta ? 'no' : options.beta;
console.log(' * beta is: %s', beta);

在该项目的 Git 仓库 中还有更多例子。

对任何应用来说,包括用户的选项都是一个重要的功能,而 Commander.js 使它很容易做到。除了 Commander.js,还有其他库,但我觉得这个库使用起来很方便快捷。你最喜欢的 JavaScript 命令行构建器是什么?


via: https://opensource.com/article/21/11/javascript-command-line-apps

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

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

使用 argparse 模块为应用程序设置命令行选项。

 title=

有一些第三方库用于命令行解析,但标准库 argparse 与之相比也毫不逊色。

无需添加很多依赖,你就可以编写带有实用参数解析功能的漂亮命令行工具。

Python 中的参数解析

使用 argparse 解析命令行参数时,第一步是配置一个 ArgumentParser 对象。这通常在全局模块内完成,因为单单\_配置\_一个解析器没有副作用。

import argparse

PARSER = argparse.ArgumentParser()

ArgumentParser 中最重要的方法是 .add_argument(),它有几个变体。默认情况下,它会添加一个参数,并期望一个值。

PARSER.add_argument("--value")

查看实际效果,调用 .parse_args()

PARSER.parse_args(["--value", "some-value"])
Namespace(value='some-value')

也可以使用 = 语法:

PARSER.parse_args(["--value=some-value"])
Namespace(value='some-value')

为了缩短在命令行输入的命令,你还可以为选项指定一个短“别名”:

PARSER.add_argument("--thing", "-t")

可以传入短选项:

PARSER.parse_args("-t some-thing".split())
Namespace(value=None, thing='some-thing')

或者长选项:

PARSER.parse_args("--thing some-thing".split())
Namespace(value=None, thing='some-thing')

类型

有很多类型的参数可供你使用。除了默认类型,最流行的两个是布尔类型和计数器。布尔类型有一个默认为 True 的变体和一个默认为 False 的变体。

PARSER.add_argument("--active", action="store_true")
PARSER.add_argument("--no-dry-run", action="store_false", dest="dry_run")
PARSER.add_argument("--verbose", "-v", action="count")

除非显式传入 --active,否则 active 就是 Falsedry-run 默认是 True,除非传入 --no-dry-run。无值的短选项可以并列。

传递所有参数会导致非默认状态:

PARSER.parse_args("--active --no-dry-run -vvvv".split())
Namespace(value=None, thing=None, active=True, dry_run=False, verbose=4)

默认值则比较单一:

PARSER.parse_args("".split())
Namespace(value=None, thing=None, active=False, dry_run=True, verbose=None)

子命令

经典的 Unix 命令秉承了“一次只做一件事,并做到极致”,但现代的趋势把“几个密切相关的操作”放在一起。

gitpodmankubectl 充分说明了这种范式的流行。argparse 库也可以做到:

MULTI_PARSER = argparse.ArgumentParser()
subparsers = MULTI_PARSER.add_subparsers()
get = subparsers.add_parser("get")
get.add_argument("--name")
get.set_defaults(command="get")
search = subparsers.add_parser("search")
search.add_argument("--query")
search.set_defaults(command="search")
MULTI_PARSER.parse_args("get --name awesome-name".split())
Namespace(name='awesome-name', command='get')
MULTI_PARSER.parse_args("search --query name~awesome".split())
Namespace(query='name~awesome', command='search')`

程序架构

使用 argparse 的一种方法是使用下面的结构:

## my_package/__main__.py
import argparse
import sys

from my_package import toplevel

parsed_arguments = toplevel.PARSER.parse_args(sys.argv[1:])
toplevel.main(parsed_arguments)
## my_package/toplevel.py

PARSER = argparse.ArgumentParser()
## .add_argument, etc.

def main(parsed_args):

    ...

    # do stuff with parsed_args

在这种情况下,使用 python -m my_package 运行。或者,你可以在包安装时使用 console\_scprits 入口点。

总结

argparse 模块是一个强大的命令行参数解析器,还有很多功能没能在这里介绍。它能实现你想象的一切。


via: https://opensource.com/article/21/8/python-argparse

作者:Moshe Zadka 选题:lujun9972 译者:MjSeven 校对:wxy

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

借鉴 C 语言的历史,学习如何用 Python 编写有用的 CLI 程序。

本文的目标很简单:帮助新的 Python 开发者了解一些关于命令行接口(CLI)的历史和术语,并探讨如何在 Python 中编写这些有用的程序。

最初……

首先,从 Unix 的角度谈谈命令行界面设计。

Unix 是一种计算机操作系统,也是 Linux 和 macOS(以及许多其他操作系统)的祖先。在图形用户界面之前,用户通过命令行提示符与计算机进行交互(想想如今的 Bash 环境)。在 Unix 下开发这些程序的主要语言是 C),它的功能非常强大。

因此,我们至少应该了解 C 程序的基础知识。

假设你没有读过上面那个链接的内容,C 程序的基本架构是一个叫做 main 的函数,它的签名是这样的。

   int main(int argc, char **argv)
   {
   ...
   }

对于 Python 程序员来说,这应该不会显得太奇怪。C 函数首先有一个返回类型、一个函数名,然后是括号内的类型化参数。最后,函数的主体位于大括号之间。函数名 main运行时链接器(构造和运行程序的程序)如何决定从哪里开始执行你的程序。如果你写了一个 C 程序,而它没有包含一个名为 main 的函数,它将什么也做不了。伤心。

函数参数变量 argcargv 共同描述了程序被调用时用户在命令行输入的字符串列表。在典型的 Unix 命名传统中,argc 的意思是“ 参数计数 argument count ”,argv 的意思是“ 参数向量 argument vector ”。向量听起来比列表更酷,而 argl 听起来就像一个要勒死的求救声。我们是 Unix 系统的程序员,我们不求救。我们让其他人哭着求救。

再进一步

$ ./myprog foo bar -x baz

如果 myprog 是用 C 语言实现的,则 argc 的值是 5,而 argv 是一个有五个条目的字符指针数组。(不要担心,如果这听起来过于技术,那换句话说,这是一个由五个字符串组成的列表。)向量中的第一个条目 argv[0] 是程序的名称。argv 的其余部分包含参数。

   argv[0] == "./myprog"
   argv[1] == "foo"
   argv[2] == "bar"
   argv[3] == "-x"
   argv[4] == "baz"
   
   /* 注:不是有效的 C 代码 */

在 C 语言中,你有很多方法来处理 argv 中的字符串。你可以手动地循环处理数组 argv,并根据程序的需要解释每个字符串。这相对来说比较简单,但会导致程序的接口大相径庭,因为不同的程序员对什么是“好”有不同的想法。

include <stdio.h>

/* 一个打印 argv 内容的简单 C 程序。 */

int main(int argc, char **argv) {
    int i;
   
    for(i=0; i<argc; i++)
      printf("%s\n", argv[i]);
}

早期对命令行标准化的尝试

命令行武器库中的下一个武器是一个叫做 getoptC 标准库函数。这个函数允许程序员解析开关,即前面带破折号的参数(比如 -x),并且可以选择将后续参数与它们的开关配对。想想 /bin/ls -alSh 这样的命令调用,getopt 就是最初用来解析该参数串的函数。使用 getopt 使命令行的解析变得相当简单,并改善了用户体验(UX)。

include <stdio.h>
#include <getopt.h>

#define OPTSTR "b:f:"

extern char *optarg;

int main(int argc, char **argv) {
    int opt;
    char *bar = NULL;
    char *foo = NULL;
   
    while((opt=getopt(argc, argv, OPTSTR)) != EOF)
       switch(opt) {
          case 'b':
              bar = optarg;
              break;
          case 'f':
              foo = optarg;
              break;
          case 'h':
          default':
              fprintf(stderr, "Huh? try again.");
              exit(-1);
              /* NOTREACHED */
       }
    printf("%s\n", foo ? foo : "Empty foo");
    printf("%s\n", bar ? bar : "Empty bar");
}

就个人而言,我希望 Python 有开关,但这永远、永远不会发生

GNU 时代

GNU 项目出现了,并为他们实现的传统 Unix 命令行工具引入了更长的格式参数,比如--file-format foo。当然,我们这些 Unix 程序员很讨厌这样,因为打字太麻烦了,但是就像我们这些旧时代的恐龙一样,我们输了,因为用户喜欢更长的选项。我从来没有写过任何使用 GNU 风格选项解析的代码,所以这里没有代码示例。

GNU 风格的参数也接受像 -f foo 这样的短名,也必须支持。所有这些选择都给程序员带来了更多的工作量,因为他们只想知道用户要求的是什么,然后继续进行下去。但用户得到了更一致的用户体验:长格式选项、短格式选项和自动生成的帮助,使用户不必再试图阅读臭名昭著的难以解析的手册页面(参见 ps 这个特别糟糕的例子)。

但我们正在讨论 Python?

你现在已经接触了足够多(太多?)的命令行的历史,对如何用我们最喜欢的语言来编写 CLI 有了一些背景知识。Python 在命令行解析方面给出了类似的几个选择:自己解析, 自给自足 batteries-included 的方式,以及大量的第三方方式。你选择哪一种取决于你的特定情况和需求。

首先,自己解析

你可以从 sys 模块中获取程序的参数。

import sys

if __name__ == '__main__':
   for value in sys.argv:
       print(value)

自给自足

在 Python 标准库中已经有几个参数解析模块的实现:getoptoptparse,以及最近的 argparseargparse 允许程序员为用户提供一致的、有帮助的用户体验,但就像它的 GNU 前辈一样,它需要程序员做大量的工作和“模板代码”才能使它“奏效”。

from argparse import ArgumentParser

if __name__ == "__main__":

   argparser = ArgumentParser(description='My Cool Program')
   argparser.add_argument("--foo", "-f", help="A user supplied foo")
   argparser.add_argument("--bar", "-b", help="A user supplied bar")
   
   results = argparser.parse_args()
   print(results.foo, results.bar)

好处是当用户调用 --help 时,有自动生成的帮助。但是 自给自足 batteries included 的优势呢?有时,你的项目情况决定了你对第三方库的访问是有限的,或者说是没有,你不得不用 Python 标准库来“凑合”。

CLI 的现代方法

然后是 ClickClick 框架使用装饰器的方式来构建命令行解析。突然间,写一个丰富的命令行界面变得有趣而简单。在装饰器的酷炫和未来感的使用下,很多复杂的东西都消失了,用户惊叹于自动支持关键字补完以及上下文帮助。所有这些都比以前的解决方案写的代码更少。任何时候,只要你能写更少的代码,还能把事情做好,就是一种胜利。而我们都想要胜利。

import click

@click.command()
@click.option("-f", "--foo", default="foo", help="User supplied foo.")
@click.option("-b", "--bar", default="bar", help="User supplied bar.")
def echo(foo, bar):
    """My Cool Program
   
    It does stuff. Here is the documentation for it.
    """
    print(foo, bar)
   
if __name__ == "__main__":
    echo()

你可以在 @click.option 装饰器中看到一些与 argparse 相同的模板代码。但是创建和管理参数分析器的“工作”已经被抽象化了。现在,命令行参数被解析,而值被赋给函数参数,从而函数 echo魔法般地调用。

Click 接口中添加参数就像在堆栈中添加另一个装饰符并将新的参数添加到函数定义中一样简单。

但是,等等,还有更多!

Typer 建立在 Click 之上,是一个更新的 CLI 框架,它结合了 Click 的功能和现代 Python 类型提示。使用 Click 的缺点之一是必须在函数中添加一堆装饰符。CLI 参数必须在两个地方指定:装饰符和函数参数列表。Typer 免去你造轮子 去写 CLI 规范,让代码更容易阅读和维护。

import typer

cli = typer.Typer()

@cli.command()
def echo(foo: str = "foo", bar: str = "bar"):
    """My Cool Program
   
    It does stuff. Here is the documentation for it.
    """
    print(foo, bar)
   
if __name__ == "__main__":
    cli()

是时候开始写一些代码了

哪种方法是正确的?这取决于你的用例。你是在写一个只有你才会使用的快速而粗略的脚本吗?直接使用 sys.argv 然后继续编码。你需要更强大的命令行解析吗?也许 argparse 就够了。你是否有很多子命令和复杂的选项,你的团队是否会每天使用它?现在你一定要考虑一下 ClickTyper。作为一个程序员的乐趣之一就是魔改出替代实现,看看哪一个最适合你。

最后,在 Python 中有很多用于解析命令行参数的第三方软件包。我只介绍了我喜欢或使用过的那些。你喜欢和/或使用不同的包是完全可以的,也是我们所期望的。我的建议是先从这些包开始,然后看看你最终的结果。

去写一些很酷的东西吧。


via: https://opensource.com/article/20/6/c-python-cli

作者:Erik O'Shaughnessy 选题:lujun9972 译者:wxy 校对:wxy

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

使用 argparse 模块像专业人士一样解析参数。

如果你在使用 Python 进行开发,你可能会在终端中使用命令,即使只是为了启动 Python 脚本或使用 pip 安装 Python 模块。命令可能简单而单一:

$ ls

命令也可能需要参数:

$ ls example

命令也可以有选项或标志:

$ ls --color example

有时选项也有参数:

$ sudo firewall-cmd  --list-all --zone home

参数

POSIX shell 会自动将你输入的内容作为命令分成数组。例如,这是一个简单的命令:

$ ls example

命令 ls 的位置是 $0,参数 example 位置是 $1

可以写一个循环迭代每项。确定它是否是命令、选项还是参数。并据此采取行动。幸运的是,已经有一个名为 argparse 的模块。

argparse

argparse 模块很容易集成到 Python 程序中,并有多种便利功能。例如,如果你的用户更改选项的顺序或使用一个不带参数的选项(称为布尔,意味着选项可以打开或关闭设置),然后另一个需要参数(例如 --color red),argparse 可以处理多种情况。如果你的用户忘记了所需的选项,那么 argparse 模块可以提供友好的错误消息。

要在应用中使用 argparse,首先要定义为用户提供的选项。你可以接受几种不同的参数,而语法一致又简单。

这是一个简单的例子:

#!/usr/bin/env python
import argparse
import sys

def getOptions(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description="Parses command.")
    parser.add_argument("-i", "--input", help="Your input file.")
    parser.add_argument("-o", "--output", help="Your destination output file.")
    parser.add_argument("-n", "--number", type=int, help="A number.")
    parser.add_argument("-v", "--verbose",dest='verbose',action='store_true', help="Verbose mode.")
    options = parser.parse_args(args)
    return options

此示例代码创建一个名为 getOptions 的函数,并告诉 Python 查看每个可能的参数,前面有一些可识别的字符串(例如 --input 或者 -i)。 Python 找到的任何选项都将作为 options 对象从函数中返回(options 是一个任意名称,且没有特殊含义。它只是一个包含函数已解析的所有参数的摘要的数据对象)。

默认情况下,Python 将用户给出的任何参数视为字符串。如果需要提取整数(数字),则必须指定选项 type=int,如示例代码中的 --number 选项。

如果你有一个只是关闭和打开功能的参数,那么你必须使用 boolean 类型,就像示例代码中的 --verbose 标志一样。这种选项只保存 TrueFalse,用户用来指定是否使用标志。如果使用该选项,那么会激活 stored_true

getOptions 函数运行时,你就可以使用 options 对象的内容,并让程序根据用户调用命令的方式做出决定。你可以使用测试打印语句查看 options 的内容。将其添加到示例文件的底部:

print(getOptions())

然后带上参数运行代码:

$ python3 ./example.py -i foo -n 4
Namespace(input='foo', number=4, output=None, verbose=False)

检索值

示例代码中的 options 对象包含了用户提供的选项后面的值(或派生的布尔值)。例如,在示例代码中,可以通过 options.number 来检索 --number

options = getOptions(sys.argv[1:])

if options.verbose:
    print("Verbose mode on")
else:
    print("Verbose mode off")

print(options.input)
print(options.output)
print(options.number)

# 这里插入你的 Python 代码

示例中的布尔选项 --verbose 是通过测试 options.verbose 是否为 True(意味着用户使用了 --verbose 标志)或 False(用户没有使用 --verbose 标志),并采取相应的措施。

帮助和反馈

argparse 还包含一个内置的 --help(简写 -h)选项,它提供了有关如何使用命令的提示。这是从你的代码派生的,因此生成此帮助系统不需要额外的工作:

$ ./example.py --help
usage: example.py [-h] [-i INPUT] [-o OUTPUT] [-n NUMBER] [-v]

Parses command.

optional arguments:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
                        Your input file.
  -o OUTPUT, --output OUTPUT
                        Your destination output file.
  -n NUMBER, --number NUMBER
                        A number.
  -v, --verbose         Verbose mode.

像专业人士一样用 Python 解析

这是一个简单的示例,来演示如何在 Python 应用中的解析参数以及如何快速有效地记录它的语法。下次编写 Python 脚本时,请使用 argparse 为其提供一些选项。你以后会感到自得,你的命令不会像一个快速的临时脚本,更像是一个“真正的” Unix 命令!

以下是可用于测试的示例代码:

#!/usr/bin/env python3
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

import argparse
import sys

def getOptions(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description="Parses command.")
    parser.add_argument("-i", "--input", help="Your input file.")
    parser.add_argument("-o", "--output", help="Your destination output file.")
    parser.add_argument("-n", "--number", type=int, help="A number.")
    parser.add_argument("-v", "--verbose",dest='verbose',action='store_true', help="Verbose mode.")
    options = parser.parse_args(args)
    return options

options = getOptions(sys.argv[1:])

if options.verbose:
    print("Verbose mode on")
else:
    print("Verbose mode off")

print(options.input)
print(options.output)
print(options.number)

via: https://opensource.com/article/19/7/parse-arguments-python

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

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