/usr/bin/time 使用记录和参数解析(一)

/usr/bin/time 使用记录和参数解析(一)

前言

参考

$ /usr/bin/time -V
GNU time 1.7

源码

wget https://ftp.gnu.org/gnu/time/time-1.7.tar.gz 
tar -zxf time-1.7.tar.gz
./configure
make

使用

一个文件读写测试案例

$ /usr/bin/time -v ./directIo > out2.log
        Command being timed: "./directIo"
        User time (seconds): 0.04
        System time (seconds): 1.24
        Percent of CPU this job got: 52%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:02.48
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1524
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 69
        Voluntary context switches: 9989
        Involuntary context switches: 1
        Swaps: 0
        File system inputs: 10000
        File system outputs: 10704
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0

参数解析代码阅读

getopt.h

#ifndef ANT_GETOPT
#define ANT_GETOPT
#ifdef __cplusplus
// 让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码
extern "C" {
#endif

struct option {
    const char *name;
    int has_arg;
    int *flag;
    int val;
};

#define no_argument       0
#define required_argument 1
#define optional_argument 2

extern char *optarg;
extern int optind, opterr, optopt;

int getopt(int argc, char * const argv[],
                  const char *optstring);

int getopt_long(int argc, char * const argv[],
                  const char *optstring,
                  const struct option *longopts, int *longindex);

int getopt_long_only(int argc, char * const argv[],
            const char *optstring,
            const struct option *longopts, int *longindex);


#ifdef __cplusplus
}
#endif
#endif

getopt.c


#include "getopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 如果某个选项有参数,用于获取传入的参数值
char *optarg = NULL;
// argv 的当前索引值。循环结束后,剩下的字串视为操作数,在argv[optind]至argv[argc-1]中可以找到
int optind = 0;
// 指向选择参数(不包括-, --), 用于处理类似 -abc 的连续短选项 
static char *nextchar;
// 出错原因
int opterr = 1;
// 包含了所发现的无效选项字符
int optopt = '?';

static enum {
    /* 当看到第一个非选项时停止选项处理:POSIXLY_CORRECT 环境变量,或者开头使用+ */
    REQUIRE_ORDER,
    /* 默认,会打乱顺序,非选项都会放在后面*/
    PERMUTE,

    RETURN_IN_ORDER
} ordering;

static char *my_index(const char *str, int chr)
{
    while (*str) {
        if (*str == chr)
            return (char *)str;
        str++;
    }
    return 0;
}

// 第一个非选项的所在索引
static int first_nonopt;
// 最后一个非选项所在索引
static int last_nonopt;

// Bash 2.0有一个环境变量,其中包含指示argv元素的标志,不应被视为参数
static const char *nonoption_flags;
static int nonoption_flags_len;

static void exchange(char **argv)
{
    int bottom = first_nonopt;
    int middle = last_nonopt;
    int top = optind;
    char *tem;

    /* Exchange the shorter segment with the far end of the longer segment.
       That puts the shorter segment into the right place.
       It leaves the longer segment in the right place overall,
       but it consists of two parts that need to be swapped next.  */

    while (top > middle && middle > bottom) {
        if (top - middle > middle - bottom) {
            /* Bottom segment is the short one.  */
            int len = middle - bottom;
            register int i;

            /* Swap it with the top part of the top segment.  */
            for (i = 0; i < len; i++) {
                tem = argv[bottom + i];
                argv[bottom + i] = argv[top - (middle - bottom) + i];
                argv[top - (middle - bottom) + i] = tem;
            }
            /* Exclude the moved bottom segment from further swapping.  */
            top -= len;
        } else {
            /* Top segment is the short one.  */
            int len = top - middle;
            register int i;

            /* Swap it with the bottom part of the bottom segment.  */
            for (i = 0; i < len; i++) {
                tem = argv[bottom + i];
                argv[bottom + i] = argv[middle + i];
                argv[middle + i] = tem;
            }
            /* Exclude the moved top segment from further swapping.  */
            bottom += len;
        }
    }

    /* Update records for the slots the non-options now occupy.  */

    first_nonopt += (optind - last_nonopt);
    last_nonopt = optind;
}

static char *posixly_correct;

static const char *_getopt_initialize(const char *optstring)
{

    first_nonopt = last_nonopt = optind = 1;

    nextchar = NULL;
    // 测试环境为NULL
    posixly_correct = getenv("POSIXLY_CORRECT");

    if (optstring[0] == '-') {
        ordering = RETURN_IN_ORDER;
        ++optstring;
    } else if (optstring[0] == '+') {
        ordering = REQUIRE_ORDER;
        ++optstring;
    } else if (posixly_correct != NULL)
        ordering = REQUIRE_ORDER;
    else
        ordering = PERMUTE;

    if (posixly_correct == NULL) {
        char var[100];
        sprintf(var, "_%d_GNU_nonoption_argv_flags_", getpid());
        nonoption_flags = getenv(var);
        // 测试环境为NULL
        if (nonoption_flags == NULL)
            nonoption_flags_len = 0;
        else
            nonoption_flags_len = strlen(nonoption_flags);
    }

    return optstring;
}

int _getopt_internal(int argc, char *const *argv, const char *optstring,
                     const struct option *longopts, int *longind, int long_only)

{
    optarg = NULL;
    /* Don't scan argv[0], 同时做一些初始化工作  */
    if (optind == 0) {
        optstring = _getopt_initialize(optstring);
        optind = 1;
    }

// 判断是否为选择格式:不以-开头,或者只有-
#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
    || (optind < nonoption_flags_len && nonoption_flags[optind] == '1'))


    // 如果不是连续的短选项处理,则获取下一个的选项参数
    if (nextchar == NULL || *nextchar == '\0') {
        /* Advance to the next ARGV-element.  */

        /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
           moved back by the user (who may also have changed the arguments).  */
        if (last_nonopt > optind)
            last_nonopt = optind;
        if (first_nonopt > optind)
            first_nonopt = optind;

        if (ordering == PERMUTE) {
            /* If we have just processed some options following some non-options,
               exchange them so that the options come first.  */

            if (first_nonopt != last_nonopt && last_nonopt != optind)
                exchange((char **)argv);
            else if (last_nonopt != optind)
                first_nonopt = optind;

            // 跳过非选择参数
            while (optind < argc && NONOPTION_P)
                optind++;
            last_nonopt = optind;
        }

        /* The special ARGV-element `--' means premature end of options.
           Skip it like a null option,
           then exchange with previous non-options as if it were an option,
           then skip everything else like a non-option.  */

        if (optind != argc && !strcmp(argv[optind], "--")) {
            optind++;

            if (first_nonopt != last_nonopt && last_nonopt != optind)
                exchange((char **)argv);
            else if (first_nonopt == last_nonopt)
                first_nonopt = optind;
            last_nonopt = argc;

            optind = argc;
        }

        // 命令行处理完
        if (optind == argc) {
            // 如果存在非选择参数,则让程序自己处理
            if (first_nonopt != last_nonopt)
                optind = first_nonopt;
            return EOF;
        }

        if (NONOPTION_P) {
            // 遇到一个非选项参数,且是REQUIRE_ORDER 则不在处理
            if (ordering == REQUIRE_ORDER)
                return EOF;
            optarg = argv[optind++];
            return 1;
        }

        // 找到选项参数,跳过 - 或 --
        nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-'));
    }

    // getopt_long_only 长选项可以不用输入两个--, 、
    if (longopts != NULL && (argv[optind][1] == '-' || 
        (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) {
        char *nameend;
        const struct option *p;
        const struct option *pfound = NULL;
        int exact = 0;
        int ambig = 0;
        int indfound;
        int option_index;

        // 解析出参数名
        for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
            /* Do nothing.  */;

        // 这里要求long_options 最后一个元素 全为0,类似字符串的判断
        for (p = longopts, option_index = 0; p->name; p++, option_index++) {
            if (!strncmp(p->name, nextchar, nameend - nextchar)) {
                // 发现前缀部分相同
                if (nameend - nextchar == strlen(p->name)) {
                    /* 完全一致  */
                    pfound = p;
                    indfound = option_index;
                    exact = 1;
                    break;
                } else if (pfound == NULL) {
                    // 如果只能推测出一个参数,就能往下继续执行
                    pfound = p;
                    indfound = option_index;
                } else {
                    /* 发现多个前缀相同 */
                    ambig = 1;
                }
            }
        }
            
        // 这里表示如果有多个前缀相同,但不存在精准匹配则报错
        if (ambig && !exact) {
            if (opterr)
                fprintf(stderr, ("%s: option `%s' is ambiguous\n"),
                        argv[0], argv[optind]);
            nextchar += strlen(nextchar);
            optind++;
            optopt = 0;
            return '?';
        }

        if (pfound != NULL) {
            option_index = indfound;
            optind++;
            if (*nameend) {
                // *nameend为‘=’, 即--prefix=(parameter)
                if (pfound->has_arg)
                    optarg = nameend + 1;
                else {
                    // 不准携带参数,却带了=则报错
                    if (opterr)
                        if (argv[optind - 1][1] == '-')
                            /* --option */
                            fprintf(stderr,
                                    ("%s: option `--%s' doesn't allow an argument\n"),
                                    argv[0], pfound->name);
                        else
                            /* +option or -option */
                            fprintf(stderr,
                                    ("%s: option `%c%s' doesn't allow an argument\n"),
                                    argv[0], argv[optind - 1][0], pfound->name);

                    nextchar += strlen(nextchar);

                    optopt = pfound->val;
                    return '?';
                }
            } else if (pfound->has_arg == 1) {
                // 必须有参数
                if (optind < argc)
                    optarg = argv[optind++];
                else {
                    if (opterr)
                        fprintf(stderr,
                                ("%s: option `%s' requires an argument\n"),
                                argv[0], argv[optind - 1]);
                    nextchar += strlen(nextchar);
                    optopt = pfound->val;
                    return optstring[0] == ':' ? ':' : '?';
                }
            }
            nextchar += strlen(nextchar);
            if (longind != NULL)
                *longind = option_index;

            // flag=NULL时,返回val;不为空时,*flag=val,返回0 
            if (pfound->flag) {
                *(pfound->flag) = pfound->val;
                return 0;
            }
            return pfound->val;
        }

        // 选项不存在
        if (!long_only || argv[optind][1] == '-' || my_index(optstring, *nextchar) == NULL) {
            if (opterr) {
                if (argv[optind][1] == '-')
                    /* --option */
                    fprintf(stderr, ("%s: unrecognized option `--%s'\n"),
                            argv[0], nextchar);
                else
                    /* +option or -option */
                    fprintf(stderr, ("%s: unrecognized option `%c%s'\n"),
                            argv[0], argv[optind][0], nextchar);
            }
            // 类似置空
            nextchar = (char *)"";
            optind++;
            optopt = 0;
            return '?';
        }
    }
    
    // 获取短选项
    {
        char c = *nextchar++;
        char *temp = my_index(optstring, c);

        /* Increment `optind' when we start to process its last character.  */
        if (*nextchar == '\0')
            ++optind;

        // 如果没有找到,选项不能设置为 ':'
        if (temp == NULL || c == ':') {
            if (opterr) {
                if (posixly_correct)
                    /* 1003.2 specifies the format of this message.  */
                    fprintf(stderr, ("%s: illegal option -- %c\n"),  argv[0], c);
                else
                    fprintf(stderr, ("%s: invalid option -- %c\n"),  argv[0], c);
            }
            // 设置非法选项
            optopt = c;
            return '?';
        }
        // 如果该选项可以有参数
        if (temp[1] == ':') {
            if (temp[2] == ':') {
                // 可选参数格式:-n 或 -n(parameter),但不能为 -n (parameter)
                if (*nextchar != '\0') {
                    optarg = nextchar;
                    optind++;
                } else
                    optarg = NULL;
                nextchar = NULL;
            } else {
                // 必须带有选项为:-p 的形式 -p(parameter) 或 -p (parameter)
                if (*nextchar != '\0') {
                    // 参数形式为 -p(parameter)
                    optarg = nextchar;
                    optind++;
                } else if (optind == argc) {
                    // 没有参数
                    if (opterr) {
                        /* 1003.2 specifies the format of this message.  */
                        fprintf(stderr,("%s: option requires an argument -- %c\n"),  argv[0], c);
                    }
                    optopt = c;
                    if (optstring[0] == ':')
                        c = ':';
                    else
                        c = '?';
                } else {
                    // 参数形式为 -p (parameter)
                    optarg = argv[optind++];
                }
                nextchar = NULL;
            }
        }
        return c;
    }
}

int getopt(int argc, char *const *argv, const char *optstring)
{
    return _getopt_internal(argc, argv, optstring,
                            (const struct option *)0,
                            (int *)0,
                            0);
}

int getopt_long(int argc,
                char *const *argv,
                const char *options,
                const struct option *long_options,
                int *opt_index)

{
    return _getopt_internal(argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int getopt_long_only(int argc,
                     char *const *argv,
                     const char *options,
                     const struct option *long_options,
                     int *opt_index)

{
    return _getopt_internal(argc, argv, options, long_options, opt_index, 1);
}

测试代码

main.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
// #include <unistd.h>
// #include <getopt.h>

int main1(int argc, char **argv)
{
    int c;
    int digit_optind = 0;

    while (1) {
        int this_option_optind = optind ? optind : 1;

        c = getopt(argc, argv, "abc:d:0123456789");
        if (c == EOF)
            break;

        switch (c) {
        case '0' ... '9':
            if (digit_optind != 0 && digit_optind != this_option_optind)
                printf("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf("option %c\n", c);
            break;

        case 'a':
            printf("option a\n");
            break;

        case 'b':
            printf("option b\n");
            break;

        case 'c':
            printf("option c with value `%s'\n", optarg);
            break;

        case '?':
            printf("invalid opt: %c\n",optopt);
            break;

        default:
            printf("?? getopt returned character code 0%o ??\n", c);
        }
    }

    if (optind < argc) {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
            printf("%s ", argv[optind++]);
        printf("\n");
    }

    exit(0);
}

int main(int argc, char **argv)
{
    int c;
    int digit_optind = 0;

    while (1) {
        int this_option_optind = optind ? optind : 1;
        int option_index = 0;
        static struct option long_options[] =
            {
                {"add", 1, 0, 0},
                {"append", 0, 0, 0},
                {"delete", 1, 0, 0},
                {"verbose", 0, 0, 0},
                {"create", 0, 0, 0},
                {"file", 1, 0, 0},
                {0, 0, 0, 0}};

        c = getopt_long_only(argc, argv, "abc:d:0123456789",
                        long_options, &option_index);
        if (c == EOF)
            break;

        switch (c) {
        case 0:
            printf("option %s", long_options[option_index].name);
            if (optarg)
                printf(" with arg %s", optarg);
            printf("\n");
            break;

        case '0' ... '9':
            if (digit_optind != 0 && digit_optind != this_option_optind)
                printf("digits occur in two different argv-elements.\n");
            digit_optind = this_option_optind;
            printf("option %c\n", c);
            break;

        case 'a':
            printf("option a\n");
            break;

        case 'b':
            printf("option b\n");
            break;

        case 'c':
            printf("option c with value `%s'\n", optarg);
            break;

        case 'd':
            printf("option d with value `%s'\n", optarg);
            break;

        case '?':
            break;

        default:
            printf("?? getopt returned character code 0%o ??\n", c);
        }
    }

    if (optind < argc) {
        printf("non-option ARGV-elements: ");
        while (optind < argc)
            printf("%s ", argv[optind++]);
        printf("\n");
    }

    exit(0);
}

Makefile:

main : main.c getopt.c
	gcc -g -o $@ $^

clean:
	rm -rf main
<think>好的,用户现在想了解如何配置`/usr/bin/time`的输出存储位置,或者如何指定其结果的保存路径。我需要结合之前提供的引用之前的回答来帮助他。 首先,回顾之前的引用,用户提到了关于路径的问题,比如修改pip的解释器路径、解决sqlplus找不到的问题,以及elasticsearch因为Java路径问题启动失败。这说明用户对Linux环境变量路径配置有定的了解,可能现在遇到了类似的问题,需要将`/usr/bin/time`的输出定向到特定文件。 用户之前的问题是关于测量命令的时间内存开销,我之前的回答提到了使用`/usr/bin/time -v`来获取详细信息。现在用户想更进步,知道如何保存这些结果到指定位置。 我需要回忆`/usr/bin/time`的选项。记得`-o`选项可以用来指定输出文件,但可能需要结合`-a`来追加内容。另外,用户可能希望同时将结果输出到文件终端,或者处理覆盖追加的情况。 还要注意,`/usr/bin/time`的输出默认是标准错误,所以可能需要重定向。但使用`-o`选项可以直接指定文件,避免使用shell的重定向,这样更可靠。 另外,用户可能希望保存详细输出(使用`-v`或`--verbose`),或者使用不同的格式。例如,`-f`选项可以自定义输出格式,然后配合`-o`保存到文件。 需要检查是否有权限问题,比如指定的存储路径是否有写入权限,以及路径是否存在。这可能也是用户需要注意的地方。 总结步骤: 1. 使用`-o`选项指定输出文件。 2. 需要详细输出时,加上`-v`。 3. 自定义格式使用`-f`,并结合`-o`保存到文件。 4. 处理覆盖追加模式。 5. 可能的重定向方法,如果用户不想用`-o`的话。 还要考虑用户可能的错误,比如路径不存在或权限不足,需要提醒用户检查这些。此外,如果用户想要同时显示在终端保存到文件,可能需要使用tee命令,但要注意`/usr/bin/time`的输出是stderr,所以需要重定向。例如:`/usr/bin/time -v command 2>&1 | tee output.txt`。 最后,根据用户之前的引用,他们可能习惯使用符号链接或环境变量,可能需要建议在脚本中自动化保存结果,或者设置别名方便使用。例如,在.bashrc中添加别名,自动将time的输出保存到特定位置。 需要确保回答结构清晰,步骤明确,并包含示例,这样用户能轻松跟随操作。同时,相关问题可以引导用户进步学习,比如如何自动化性能测试或处理权限问题。</think>#### 分步解决方案:配置 `/usr/bin/time` 的输出存储路径 --- ### 1. **基础输出重定向** `/usr/bin/time` 的输出默认写入 **标准错误流(stderr)**,可通过 Shell 重定向操作符 `>` 或 `>>` 将结果保存到指定文件: ```bash # 覆盖写入文件(若文件已存在则清空) /usr/bin/time -v your_command 2> time_output.txt # 追加写入文件(保留历史记录) /usr/bin/time -v your_command 2>> time_log.txt ``` **注意**:必须使用 `2>` 而非 `>`,因为 `time` 的输出通过 stderr 传递[^1]。 --- ### 2. **专用 `-o` 参数控制存储** `/usr/bin/time` 提供 `-o` 参数直接指定输出文件路径,**无需手动处理流重定向**: ```bash # 指定输出到文件(默认覆盖模式) /usr/bin/time -o /path/to/output.txt -v your_command # 追加模式(添加 -a 参数) /usr/bin/time -o /path/to/log.txt -a -v your_command ``` **优势**:避免因 Shell 环境差异导致的重定向错误,支持追加/覆盖模式切换[^2]。 --- ### 3. **自定义输出格式与路径** 通过 `-f` 参数定义输出内容的格式,结合 `-o` 实现结构化存储: ```bash # 定义时间+内存的简洁格式并保存 /usr/bin/time -o metrics.csv -f "Time: %E, Memory: %M KB" your_command # 多列数据记录(适合脚本解析) /usr/bin/time -o perf.log -f "CPU%%:%P Mem(KB):%M Time:%E" your_command ``` **常用格式符号**: - `%E`: 实际耗时(格式: 时:分:秒) - `%M`: 峰值内存占用(KB) - `%P`: CPU 使用率((user + sys)/real * 100%) - `%e`: 实际耗时(秒) --- ### 4. **多目标输出(终端+文件同步显示)** 若需**同时查看实时输出并保存到文件**,可通过管道组合 `tee`: ```bash /usr/bin/time -v your_command 2>&1 | tee /path/to/full_log.txt ``` **解析**:`2>&1` 将 stderr 合并到 stdout,`tee` 将内容同时输出到终端文件。 --- ### 5. **环境变量全局配置(可选)** 通过修改 Shell 配置文件(如 `~/.bashrc`)定义默认输出路径: ```bash # 添加别名简化操作 alias mytime='/usr/bin/time -o ~/benchmarks/$(date +\%Y\%m\%d-\%H\%M\%S).log -v' # 使用自定义别名执行命令 mytime your_command ``` **效果**:自动按时间戳生成独立日志文件,便于长期性能追踪[^3]。 --- ### 注意事项 1. **路径权限**:确保目标目录有写入权限(可通过 `ls -ld /path/to/dir` 检查)。 2. **文件覆盖风险**:未使用 `-a` 时,重复执行会覆盖原有文件内容。 3. **跨平台差异**:BSD 与 GNU 版本的 `time` 参数可能不同(建议通过 `man time` 确认本地支持选项)。 --- ### 相关问题 1. 如何自动化批量测试并汇总 `/usr/bin/time` 的结果? 2. 在无权限修改系统路径时,如何安全地重定向性能日志? 3. 如何解析 `/usr/bin/time` 生成的日志文件并生成可视化报告?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值