{"id":19401463,"url":"https://github.com/ayosec/timehistory-bash","last_synced_at":"2025-04-24T07:30:42.599Z","repository":{"id":47471732,"uuid":"400003053","full_name":"ayosec/timehistory-bash","owner":"ayosec","description":"Bash loadable builtin to track resources used by programs.","archived":false,"fork":false,"pushed_at":"2021-11-29T20:13:05.000Z","size":128,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-03T01:04:50.624Z","etag":null,"topics":["bash","command-line"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ayosec.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-26T01:22:55.000Z","updated_at":"2025-03-14T06:52:58.000Z","dependencies_parsed_at":"2022-09-01T05:21:48.176Z","dependency_job_id":null,"html_url":"https://github.com/ayosec/timehistory-bash","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayosec%2Ftimehistory-bash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayosec%2Ftimehistory-bash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayosec%2Ftimehistory-bash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ayosec%2Ftimehistory-bash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ayosec","download_url":"https://codeload.github.com/ayosec/timehistory-bash/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250582800,"owners_count":21453917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","command-line"],"created_at":"2024-11-10T11:18:34.989Z","updated_at":"2025-04-24T07:30:42.245Z","avatar_url":"https://github.com/ayosec.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# timehistory for bash\n\ntimehistory is a [*loadable builtin*] for bash that tracks programs executed in\nthe running shell. When a program is finished, timehistory collects the\n[resources used] by it. The data can be displayed in a [customizable format] or\nas JSON.\n\n[*loadable builtin*]: #what-is-a-loadable-builtin:\n[customizable format]: ./FORMAT.md\n[resources used]: https://man7.org/linux/man-pages/man2/getrusage.2.html\n\n## Example\n\n```console\n$ enable -f /…/libtimehistory_bash.so timehistory\n\n$ head -c 10M /dev/zero | sha512sum\n…\n\n$ timehistory\nNUMBER  TIME      %CPU  ELAPSED  COMMAND\n1       09:29:08  100%  0.046    sha512sum\n2       09:29:08  16%   0.045    head -c 10M /dev/zero\n\n$ timehistory -f '[header,table]%n\\t%(pid)\\t%e\\t%P\\t%M\\t%w\\t%C'\nNUMBER  PID   ELAPSED  %CPU  MAXRSS  VCSW  COMMAND\n1       6651  0.046    100%  4428    2     sha512sum\n2       6650  0.045    16%   4488    320   head -c 10M /dev/zero\n\n$ timehistory -v 1\nPID:                          6651\nCommand:                      sha512sum\nTime:                         2021-08-22 09:29:08\nUser time:                    0.042 seconds\nSystem time:                  0.004 seconds\nPercent of CPU:               100%\nElapsed time:                 0:00.046\nMaximum resident set size:    4428 Kib\nMajor page faults:            0\nMinor page faults:            135\nVoluntary context switches:   2\nInvoluntary context switches: 1\nFile system inputs:           0\nFile system outputs:          0\nExit status:                  0\n```\n\n## What is a *Loadable Builtin*\n\nBash, like many other shells, provides [*builtins*]. A builtin is a command\nimplemented by the shell itself, so it does not need to invoke another program\nwhen the command is executed. Some builtins are used to interact with the shell\n(like `cd` or `jobs`), and others are common utilities (like `printf` or\n`test`).\n\nA *loadable builtin* is a builtin implemented in a [dynamic library]. Bash can\nload a shared object (a `.so` file), and create a new builtin from it.\n\nFor example, if the builtin `foo` is implemented in a `libfoo.so` file, it can\nbe loaded in a running shell with the following command:\n\n```console\n$ enable -f /…/libfoo.so foo\n\n$ foo\nexecutes a function from libfoo.so\n```\n\nDocumentation on *loadable builtins* is very scarce. There are some notes and\nexamples in the [`examples/loadables`] directory of the bash source code, and a\nfew articles around the web, but barely anything else.\n\n[*builtins*]: https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html\n[`examples/loadables`]: https://git.savannah.gnu.org/cgit/bash.git/tree/examples/loadables?h=bash-5.1\n[dynamic library]: https://en.wikipedia.org/wiki/Shared_libraries\n\n## Format Strings\n\nThe *format string* controls how to render every entry in the history list.\n\nThe syntax for the format string is composed by plain text and *resource\nspecifiers*. Each *resource specifier* is preceded by a percent sign (`%`).\n\nSome specifiers have an alias. For example, both `%M` and `%(maxrss)` refers to\nthe maximum resident set size.\n\nMany specifiers are taken from [GNU time], so most format strings for it should\nbe compatible with timehistory.\n\nTo see more details about the syntax, please see [`FORMAT.md`](./FORMAT.md).\n\n[GNU time]: https://www.gnu.org/software/time/\n\n## Installation\n\ntimehistory only requires the `.so` built from the sources in this repository.\nYou can download a [precompiled package](#precompiled-packages), or\n[build it from sources](#installation-from-sources).\n\nThe `.so` file can be in any directory of the file system. The full path is\nrequired to enable it:\n\n```console\n$ enable -f /usr/lib/bash/libtimehistory_bash.so timehistory\n```\n\nThe directory can be omitted if it is added to the `$BASH_LOADABLES_PATH`\nvariable:\n\n```console\n$ BASH_LOADABLES_PATH=/usr/lib/bash\n\n$ enable -f libtimehistory_bash.so timehistory\n```\n\nCurrently, timehistory has been tested only in Linux x86-64, but it may work in\nother platforms.\n\n### Precompiled Packages\n\nThere are packages for some Linux distributions in the [Releases] page. After\ninstalling any of them, timehistory is available in `/usr/lib/bash`.\n\nAlternatively, there is a tarball (built in Debian stable) with the shared\nobject. To install it, just copy the `libtimehistory_bash.so` file to any path\nof the file system.\n\n```console\n$ tar xzf timehistory-bash-0.1.0.tar.gz\n\n$ sudo install -t /usr/lib/bash -D -o root -g root libtimehistory_bash.so\n```\n\nThese packages are built in a GitHub Actions runner.\n\n### Build from Sources\n\nThe [Rust compiler] is required to build the package from sources. Once it is\ninstalled, type the following command to generate the shared object:\n\n```console\n$ cargo build --release\n```\n\nThe shared object will be available in `target/release/libtimehistory_bash.so`.\nType the following command to install it in `/usr/lib/bash`:\n\n```console\n$ sudo install -t /usr/lib/bash -D -o root -g root target/release/libtimehistory_bash.so\n```\n\n[Releases]: https://github.com/ayosec/timehistory-bash/releases\n[Rust compiler]: https://www.rust-lang.org/tools/install\n\n## Usage\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e:information_source:\u003c/td\u003e\n\u003ctd\u003e\nThis section assumes that the \u003ccode\u003elibtimehistory_bash.so\u003c/code\u003e file is\ninstalled in the \u003ccode\u003e/usr/lib/bash\u003c/code\u003e directory.\n\u003cbr\u003e\u003cbr\u003e\nYou must modify the commands to use the actual path if the file is in another\ndirectory.\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\ntimehistory is enabled with the [`enable -f`] command:\n\n```bash\nenable -f /usr/lib/bash/libtimehistory_bash.so timehistory\n```\n\nOnce it is activated, it collects resources for every executed program.\n\nIt can be removed from the running shell with `enable -d timehistory`.\n\n### Enable timehistory Automatically\n\nTo enable timehistory automatically, add the `enable` command to the\n`~/.bashrc` file. However, this method is not recommended because timehistory is\nvery young, and it still may have bugs that can crash the bash process.\n\nA safer approach is to load it only when you need to collect data. For example,\nwith a function like this in the `~/.bashrc` file:\n\n```bash\n_load_timehistory() {\n    enable -f /usr/lib/bash/libtimehistory_bash.so timehistory\n    # TIMEHISTORY_LIMIT=…\n    # TIMEHISTORY_FORMAT='…'\n    # TIMEHISTORY_CMDLINE_LIMIT=…\n}\n```\n\nThen, type `_load_timehistory` before running the commands that have to be\ntracked.\n\nFinally, if you have a separated bash configuration for development environments\n(for example, in a Docker container) it can be *not-so-risky* to enable it\nautomatically.\n\n[`enable -f`]: https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-enable\n\n### Display Data\n\nType `timehistory` to see all entries in the history list.\n\nEvery entry is rendered using the default [format string]. To use a different\n[format string] you can use the `$TIMEHISTORY_FORMAT` shell variable, or add\nthe `-f '…'` option in the command-line.\n\nTo see a single entry, type `timehistory \u003cn\u003e`, where `\u003cn\u003e` is the number of the\nentry. If the number starts with a plus sign (`+`), the number is relative to\nthe end of the list (`+1` is the last entry, `+2` the previous one, etc.).\n\nUse the `-v` to print entries in an [extended format], similar to `time -v` from\n[GNU time].\n\nUse `-j` to print entries in JSON format.\n\nSee the [Example](#example) section to see examples of these options.\n\n[extended format]: ./src/format/verbose.fmt\n\n### Track Commands in Shell Scripts\n\ntimehistory can be used to collect executed commands in a bash script. The JSON\noutput allows to get the raw data, so it can be analyzed later.\n\nWith the following snippet at the beginning of the script, timehistory will\nwrite a JSON array with data collected from the programs executed by the script\nto a `commands.\u003cPID\u003e.json` file.\n\n```bash\n#!/bin/bash\n\nenable -f /usr/lib/bash/libtimehistory_bash.so timehistory\ntrap 'timehistory -j \u003e commands.$$.json' EXIT\n\n# rest of the script\n```\n\n### Delete Data\n\nUse the `-R` option to delete all history entries.\n\n### Available Options\n\nType `timehistory --help` or `help timehistory` to see all available options:\n\n```console\n$ timehistory --help\ntimehistory: timehistory [-f FMT | -v | -j] [\u003cn\u003e | +\u003cn\u003e] | -s | -R\n    Displays information about the resources used by programs executed in\n    the running shell.\n\n    Options:\n      -f FMT    Use FMT as the format string for every history entry,\n                instead of the default value.\n      -v        Use the verbose format, similar to GNU time.\n      -j        Print information as JSON format.\n      -s        Print the current configuration settings.\n      -R        Remove all entries in the history.\n\n    If \u003cn\u003e is given, it displays information for a specific history entry.\n    The number for every entry is printed with the %n specifier in the\n    format string. If the number is prefixed with a plus symbol (+\u003cn\u003e) it\n    is the offset from the end of the list ('+1' is the last entry).\n\n    Format:\n      Use '-f help' to get information about the formatting syntax.\n\n    Settings:\n      The following shell variables can be used to change the configuration:\n\n        TIMEHISTORY_FORMAT          Default format string.\n        TIMEHISTORY_LIMIT           History limit.\n        TIMEHISTORY_CMDLINE_LIMIT   Number of bytes to copy from the\n                                    command line.\n```\n\n## Configuration\n\ntimehistory configuration can be modified using shell variables:\n\n* `TIMEHISTORY_FORMAT`\n\n    Set the default [format string] for history entries.\n\n    This value is used when the timehistory is invoked without the `-f` option.\n\n* `TIMEHISTORY_LIMIT`\n\n    Set the maximum number of entries stored in the history list.\n\n    When an entry is added to the history, and the number of entries exceeds\n    this limit, the oldest entry is removed.\n\n* `TIMEHISTORY_CMDLINE_LIMIT`\n\n    Set the maximum number of bytes from the command line to be added to the\n    history.\n\n    If a command line exceeds this limit, then it is truncated.\n\nThe current configuration settings are printed with `timehistory -s`:\n\n```console\n$ timehistory -s\nTIMEHISTORY_FORMAT        = [header,table]%n\\t%(time:%X)\\t%P\\t%e\\t%C\nTIMEHISTORY_LIMIT         = 500\nTIMEHISTORY_CMDLINE_LIMIT = 512\n```\n\n[format string]: ./FORMAT.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayosec%2Ftimehistory-bash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fayosec%2Ftimehistory-bash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fayosec%2Ftimehistory-bash/lists"}