{"id":13831673,"url":"https://github.com/9seconds/ah","last_synced_at":"2025-05-07T01:50:19.757Z","repository":{"id":20109415,"uuid":"23379123","full_name":"9seconds/ah","owner":"9seconds","description":"A better history","archived":false,"fork":false,"pushed_at":"2015-03-30T20:08:18.000Z","size":752,"stargazers_count":331,"open_issues_count":2,"forks_count":8,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-31T05:11:19.153Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/9seconds.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2014-08-27T06:20:26.000Z","updated_at":"2025-02-27T06:32:52.000Z","dependencies_parsed_at":"2022-09-02T13:51:10.375Z","dependency_job_id":null,"html_url":"https://github.com/9seconds/ah","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9seconds%2Fah","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9seconds%2Fah/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9seconds%2Fah/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/9seconds%2Fah/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/9seconds","download_url":"https://codeload.github.com/9seconds/ah/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252798781,"owners_count":21805880,"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":[],"created_at":"2024-08-04T10:01:36.680Z","updated_at":"2025-05-07T01:50:19.709Z","avatar_url":"https://github.com/9seconds.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"ah\n==\n\n[![Build Status](https://travis-ci.org/9seconds/ah.svg?branch=master)](https://travis-ci.org/9seconds/ah)\n\nah is a complementary software for a builtin shell `history` command\nyou've used to use for years and I hope you've dreamt about it as I did.\n\nIt is not a replacement for `history` but anyway it perfectly matches\na common `history | grep` pattern of usage but it allows you to do a bit more.\nIt allows you to trace an output of a command, to fetch it from the archive,\nto bookmark some commands and to execute them.\n\nHow often do you kick yourself for loosing important output of your SSH session?\nSometimes you use screen or tmux for that purposes but it is pretty awkward to\nsearch through it. Or how often do you find yourself typing CTRL+R or juggling\nwith Up and Down buttons to find something like\n`make \u0026\u0026 mv -f coolapp $DIR/bin \u0026\u0026 coolapp`. Stop it for a great good, ah will\nlikely help you here.\n\nCurrently it supports following features:\n* Tracing an output\n* Fetching the output trace\n* Bookmarking command\n* Executing of a command by number or bookmark\n* Showing a history with greping on regular expression or fuzzy match\n\nah does not maintains its own history file, it uses your regular `~/.bash_history`\nor `.zsh_history`. So no worries here: bash or zsh maintains a history and\nah gives you several features on the top.\n\nah supports Zsh and Bash.\n\n\n\nInstallation\n------------\n\nYou may build ah from sources or just download proper binary from\n[releases](https://github.com/9seconds/ah/releases).\n\nTo install it from sources, just do following:\n\n```bash\n$ git clone https://github.com/9seconds/ah.git $GOPATH/src/github.com/9seconds/ah\n$ cd $GOPATH/src/github.com/9seconds/ah\n$ make install\n```\n\nIt will copy the binary into `$GOBIN/ah`. Or you may do that:\n\n```bash\n$ git clone https://github.com/9seconds/ah.git $GOPATH/src/github.com/9seconds/ah\n$ cd $GOPATH/src/github.com/9seconds/ah\n$ make\n$ mv ah /wherever/you/want\n```\n\nAlso, if you use [HomeBrew](http://brew.sh)/[LinuxBrew](http://brew.sh/linuxbrew), you may want to check the formula:\n\n```bash\n$ brew tap 9seconds/homebrew-ah\n$ brew install ah\n```\n\nUpdate with Brews are trivial\n\n```bash\n$ brew update\n$ brew reinstall ah\n```\n\n\nTracing an output\n-----------------\n\nSo you want ~~to be a hero~~ to capture an output of some of your commands.\nUsually if you know that output is rather important, you use `tee` command.\n\n```bash\n$ find . -name \"*.go\" -type f | tee files.log\n```\n\nThe main problem here is that only stdout will go to the `files.log`. You will\nlose stderr, right? The common way of solving that is redirecting a streams\n\n```bash\n$ find . -name \"*.go\" -type f 2\u003e\u00261 | tee files.log\n```\n\nor\n\n```bash\n$ find . -name \"*.go\" -type f |\u0026 tee files.log\n```\n\nin a recent Bash-compatible shells. The problem here, you are streams are\ndangerously mixed into one and there is no way to pipe them into different processes.\nLet's say, you want to have output stored persistently but in the same time you\nwant to have only filtered log messages on the screen. You can't do something\nlike this.\n\n```bash\n$ find . -name \"*.go\" -type f |\u0026 tee files.log \u003e /dev/null 2\u003e grep -i localhost\n```\n\nOkay, this a rare case but please notice that ah knows how to handle that. Let's\ntalk on how to store this output, where to keep it. You may store it somehow\nbut it is a way better to have a tool which allows you not to think on how to\nname this output and how to remember which was the command.\n\nLet's talk about ah now. Ah has its main `t` command which works rather simple.\n\n```bash\n$ ah t -- find . -name \"*.go\" -type f\n```\n\nThats all. You will see output on the screen and you may pipe both streams wherever\nyou want! Ah will store it persistently. And it will finish execution with\nprecisely the same exit code as the original command does. Neat, right?\n\nIf you want to run a program which requires a pseudo TTY, just use `-y` option.\nAnd if you want to have your aliases to work, just run it with `-x` option!\n\nAh supports SSH and you may even run curses apps there, they will work, no worries.\n\n\n\nShow the history\n----------------\n\nNow let's talk about viewing the history. ah does that with `s` command.\n\n```bash\n$ ah s\n...\n!10024  (01.11.14 16:01:09) *  ah t -- find . -name \"*.go\" -type f\n```\n\nWhat do we have here: we have banged command number (gues why it has ! here),\nwe have a date (yes, `HISTTIMEFORMAT` supported!), we have a rather strange\nstar mark and a command. What does that star mark mean? Basically it just shows\nthat ah keeps a mixed output of that command and you may fetch it on demand.\n\nah has `-g` options which allows you to grep this list. Argument - a regular\nexpression. It also has a convenient flag `-z` which activates fuzzy match. It\nworks like this\n\n```bash\n$ ah s -z -g doigreREPOsoru\n```\n\nAnd I will see matched `docker images | egrep -v 'REPOSITORY|\u003cnone\u003e' | cut -d' ' -f1 | sort -u`\nI bold important letters here. Basically I do it thinking like this:\n*\"I want __do__ cker __i__ mages, it was __gre__ p __REPO__ SITORY\nand __sor__ ted with -__u__\"* typing just a few letters.\n\nIt also supports number argument. Let's say `ah s 10` will show latest 10\ncommands, `ah s 10 20` will show commands from 10 to 20. Also negative numbers\nare supported (but with underscore prefix, not hyphen), they are mostly work\nas Python slices. `ah s 10 _20` means literally \"from 10 to the latest 20\".\nBasically `ah s 10` equal to `ah s _10 _1`\n\n\n\nShow an output\n--------------\n\nOutput could be checked with `l` command. Just type `ah l 10024` and you are\ngood.\n\n\n\nBookmarks\n---------\n\nYou may pin any command number with bookmark using `b` command. After that\nyou may execute it with `e` command. To fetch a list of bookmarks use `lb` commands,\nto remove several, use `rb` command.\n\nSo simple.\n\n\nGarbage collecting\n------------------\n\nIf you do not need a lot of traces or bookmarks, you may get rid of them using\n`gt` (garbage collect traces) and `gb` (garbage collect bookmarks) commands.\n\n\nAutomatic execution\n-------------------\n\n_(Only for zsh)_\n\nAh allows user to set it for automatic tracing outputs. To do that please do\nthe following:\n\n1. You have to download script to [source](https://raw.githubusercontent.com/9seconds/ah/master/sourceit/zsh.sh)\n   and place it wherever you want. Let's say, I put it to `~/.auto_ah.sh`\n2. Add following line to your `~/.zshrc`: `source ~/.zshrc` and please be noticed\n   that `ah` should be in your `PATH`. If `which ah` works, then you're done.\n   Otherwise just add it to the path.\n\nBasically, ah will track all your executions automatically. But since it is\ndangerous to execute automatically everything around, there is a whitelist. ah\nhas 3 commands you should be interested in:\n\n1. `al` shows the list of all whitelist command ah will automatically apply to.\n2. `ar` removes command from the whitelist and ah won't execute it automatically.\n3. `ad` add a command to the whitelist.\n\nLet's check my current setup.\n\n```bash\n$ ah al\nag                   [interactive=false, pseudoTty=false]\naptg                 [interactive=true , pseudoTty=false]\nawk                  [interactive=false, pseudoTty=false]\ndocker               [interactive=false, pseudoTty=true ]\ndocker_clean         [interactive=true , pseudoTty=false]\ndocker_stop          [interactive=true , pseudoTty=false]\ndocker_update        [interactive=true , pseudoTty=false]\nfind                 [interactive=false, pseudoTty=false]\ngrep                 [interactive=false, pseudoTty=false]\nipython              [interactive=true , pseudoTty=true ]\nmake                 [interactive=false, pseudoTty=false]\npython               [interactive=false, pseudoTty=false]\nsed                  [interactive=false, pseudoTty=false]\nssh                  [interactive=false, pseudoTty=false]\nvagrant              [interactive=true , pseudoTty=true ]\n```\n\nAs you can see, I have a mixed setup. I trace an output of `ag` or `find` command\nand do it in non-interactive (interactive means `zsh -i -c`) way and do not\nallocate pseudo TTY for them. There are several aliases (`docker_update` or `aptg`)\nand to execute them I use interactive mode. And I use pseudo TTY for `ipython`.\n\nNow let's add `go` for the list.\n\n```bash\n$ ah ad go\n```\n\nSo simple. But how can I set interactiveness or pseudo TTYs? Pretty simple and\nobvious (remember `t` command?)\n\n```bash\n$ ah ad -x go\n```\n\nfor interactiveness. And for pseudo TTY\n\n```bash\n$ ah ad -y go\n```\n\nIf you decide to use another set of options, just execute `ah ad` with another\nset of options, it will override previous setting.\n\nTo remove command just use `ar`\n\n```bash\n$ ah ar go\n```\n\nNo need to resource or do something more.\n\n\nConfiguration\n-------------\n\nah supports configuration with YAML file. It should be placed in `~/.ah/config.yaml`.\nHere is the full example (everything may be omit)\n\n```yaml\nshell: zsh\nhistfile: /home/9seconds/.zsh_history\nhisttimeformat: \"%d.%m.%y %H:%M:%S\"\n\ntmpdir: /tmp\n```\n\nThat simple, yes. It is useful, if you bring a lot of commandline options in aliases\nor if you want to execute ah automatically.\n\nHere is the sequence of argument overriding:\n\n1. Default options\n2. Config options\n3. Commandline options\n\nSo commandline options overrides config.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F9seconds%2Fah","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F9seconds%2Fah","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F9seconds%2Fah/lists"}