{"id":13448416,"url":"https://github.com/Miserlou/Loop","last_synced_at":"2025-03-22T09:31:21.303Z","repository":{"id":37549504,"uuid":"133870139","full_name":"Miserlou/Loop","owner":"Miserlou","description":"UNIX's missing `loop` command","archived":false,"fork":false,"pushed_at":"2022-09-30T13:17:09.000Z","size":110,"stargazers_count":662,"open_issues_count":30,"forks_count":29,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-04-14T08:43:21.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://github.com/Miserlou/Loop","language":"Rust","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/Miserlou.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-17T21:27:03.000Z","updated_at":"2024-04-10T03:29:03.000Z","dependencies_parsed_at":"2022-08-08T20:31:09.719Z","dependency_job_id":null,"html_url":"https://github.com/Miserlou/Loop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Miserlou%2FLoop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Miserlou%2FLoop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Miserlou%2FLoop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Miserlou%2FLoop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Miserlou","download_url":"https://codeload.github.com/Miserlou/Loop/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244937751,"owners_count":20535124,"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-07-31T05:01:44.975Z","updated_at":"2025-03-22T09:31:20.892Z","avatar_url":"https://github.com/Miserlou.png","language":"Rust","readme":"![Logo, with help from Linn Kristiansen](https://i.imgur.com/TQp8nu3.png)\n\n# loop [![Build Status](https://travis-ci.org/Miserlou/Loop.svg)](https://travis-ci.org/Miserlou/Loop) [![crates.io](https://img.shields.io/crates/v/loop-rs.svg)](https://crates.io/crates/loop-rs)\n\n_\"UNIX's missing `loop` command!\"_\n\n`loop` lets you write powerful, intuitive looping one-liners in your favorite shell! Finally, loops in Bash that make sense!\n\n## Why?\n\nLoops in bash are surprisingly complicated and fickle! I wanted a simple and intuitive way to write controllable loops that:\n\n * Run on controllable **timers**!\n   - `$ loop --every 10s -- ls`\n\n * Have **custom counters**!\n   - `$ loop --count-by 5 -- 'touch $COUNT.txt'`\n\n * Loop **until output matches** a condition!\n   - `$ loop --until-contains 200 -- ./get_response_code.sh --site mysite.biz`\n\n * Loop **until a certain time**!\n   - `$ loop --for-duration 8h -- ./poke_server`\n\n * Loop **until a program succeeds** (or fails!)\n    - `$ loop --until-success -- ./poke_server`\n\n * Iterate over the **standard input**!\n    - `$ cat files_to_create.txt | loop -- 'touch $ITEM'`\n\n * Get a **summary** of the runs!\n    - `$ loop --for-duration 10min --summary -- ls`\n\n * Run until output **changes or stays the same** between invocations!\n   - `$ loop --until-changes -- date +%s`\n   - `$ loop --until-same -- date +%s`\n\n * ..and **much more!**\n\n And so `loop` was born!\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Installation](#installation)\n  - [Linux](#linux)\n  - [OSX](#osx)\n  - [Rust Users](#rust-users)\n    - [Building](#building)\n- [Usage](#usage)\n  - [Counters](#counters)\n  - [Timed Loops](#timed-loops)\n  - [Until Conditions](#until-conditions)\n  - [Iterating Over Lists and Standard Inputs](#iterating-over-lists-and-standard-inputs)\n- [Useful Examples](#useful-examples)\n  - [Testing inputs to a program](#testing-inputs-to-a-program)\n  - [Waiting for a website to appear online](#waiting-for-a-website-to-appear-online)\n  - [Waiting for a file to be created](#waiting-for-a-file-to-be-created)\n  - [Create a backup for all files in a directory](#create-a-backup-for-all-files-in-a-directory)\n  - [Keep trying a failing script until it passes, up to 5 times](#keep-trying-a-failing-script-until-it-passes-up-to-5-times)\n  - [Keep trying a failing script until timeout](#keep-trying-a-failing-script-until-timeout)\n  - [Comparison with GNU Parallel](#comparison-with-gnu-parallel)\n  - [More examples](#more-examples)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Installation\n\n### Linux\n\n`loop` is available on Snapcraft for all distributions as `loop-rs`.\n\n    $ snap install loop-rs --beta\n\n_Issues related to this package are tracked [here](https://github.com/Miserlou/Loop/issues/4)._\n\nThere is also an AUR for Arch Linux users, but I don't maintain it, so use it at your own risk:\n\n    $ yaourt -S loop\n\n### OSX\n\nIf you're a Homebrew user:\n\n    $ brew tap miserlou/loop https://github.com/Miserlou/Loop.git\n    $ brew install loop --HEAD\n\n### Rust Users\n\n    $ cargo install loop-rs\n\n#### Building\n\n    $ cargo build\n    ./debug/loop\n    $ cargo run 'echo $COUNT'\n    1\n    2\n    [ .. ]\n\n## Usage\n\nWith no arguments, `loop` will simply repeatedly execute a command string as fast as it can until `^C` (control + C) is sent.\n\n    $ loop 'echo hello'\n    hello\n    hello\n    hello\n    hello\n    [ .. ]\n\nYou can also use double dashes ( ` -- ` ) to seperate arguments:\n\n    $ loop -- echo hello\n    hello\n    hello\n    hello\n    hello\n    [ .. ]\n\n### Counters\n\n`loop` places a counter value into the `$COUNT` environment variable.\n\n    $ loop -- 'echo $COUNT'\n    0\n    1\n    2\n    [ .. ]\n\nThe amount this counter increments can be changed with `--count-by`:\n\n    $ loop --count-by 2 -- 'echo $COUNT'\n    0\n    2\n    4\n    6\n    [ .. ]\n\nThe counter can be offset with `--offset`:\n\n    $ loop --count-by 2 --offset 10 -- 'echo $COUNT'\n    10\n    12\n    14\n    [ .. ]\n\nAnd iterators can be floats!\n\n    $ loop --count-by 1.1 -- 'echo $COUNT'\n    0\n    1.1\n    2.2\n    [ .. ]\n\nThere's also an `$ACTUALCOUNT`:\n\n    $ loop --count-by 2 -- 'echo $COUNT $ACTUALCOUNT'\n    0 0\n    2 1\n    4 2\n    [ .. ]\n\nYou can get a summary of successes and failures (based on exit codes) with `--summary`:\n\n    $ loop --num 3 --summary -- 'echo $COUNT'\n    0\n    1\n    2\n    Total runs:  3\n    Successes:   3\n    Failures:    0\n\nor\n\n    $ loop --num 3 --summary -- 'ls -foobarbatz'\n    [ .. ]\n    Total runs:  3\n    Successes:   0\n    Failures:    3 (1, 1, 1)\n\nIf you only want the output of the last result, you can use `--only-last`:\n\n    $ loop --count-by 2 --num 50 --offset 2 --only-last -- 'echo $COUNT' # Counting is 0-indexed\n    100\n\n### Timed Loops\n\nLoops can be set to timers which accept [humanized times](https://github.com/tailhook/humantime) from the microsecond to the year with `--every`:\n\n    $ loop --every 5s -- date\n    Thu May 17 10:51:03 EDT 2018\n    Thu May 17 10:51:08 EDT 2018\n    Thu May 17 10:51:13 EDT 2018\n\nLooping can be limited to a set duration with `--for-duration`:\n\n    $ loop --for-duration 8s --every 2s -- date\n    Fri May 25 16:46:42 EDT 2018\n    Fri May 25 16:46:44 EDT 2018\n    Fri May 25 16:46:46 EDT 2018\n    Fri May 25 16:46:48 EDT 2018\n    $\n\nOr until a certain date/time with `--until-time`:\n\n    $ loop --until-time '2018-05-25 20:50:00' --every 5s -- 'date -u'\n    Fri May 25 20:49:49 UTC 2018\n    Fri May 25 20:49:54 UTC 2018\n    Fri May 25 20:49:59 UTC 2018\n    $\n\n### Until Conditions\n\n`loop` can iterate until output contains a string with `--until-contains`:\n\n    $ loop --until-contains \"666\" -- 'echo $RANDOM'\n    11235\n    35925\n    666\n    $\n\n`loop` can iterate until the output changes with `--until-changes`:\n\n    $ loop --only-last --every 1s --until-changes -- 'date +%s'\n    1548884135\n    $\n\n`loop` can iterate until the output stays the same with `--until-same`. This would be useful, for instance,\nfor monitoring with `du` until a download or copy finishes:\n\n    $ loop --every 1s --until-same -- 'du -bs .'\n    236861997       .\n    $\n\nOr until a program succeeds with `--until-success`:\n\n    $ loop --until-success -- 'if (( RANDOM % 2 )); then (echo \"TRUE\"; true); else (echo \"FALSE\"; false); fi'\n    FALSE\n    FALSE\n    TRUE\n    $\n\nOr until it fails with `--until-error` (which also accepts an optional error code):\n\n    $ loop --until-error -- 'if (( RANDOM % 2 )); then (echo \"TRUE\"; true); else (echo \"FALSE\"; false); fi'\n    TRUE\n    TRUE\n    FALSE\n    $\n\nOr until it matches a regular expression with `--until-match`:\n\n    $ loop --until-match \"(\\d{4})\" -- `date`\n    Thu May 17 10:51:03 EDT 2018\n    $\n\n### Iterating Over Lists and Standard Inputs\n\nLoops can iterate over all sorts of lists with `--for`:\n\n    $ loop --for red,green,blue -- 'echo $ITEM'\n    red\n    green\n    blue\n    $\n\nAnd can read from the standard input via pipes:\n\n    $ cat /tmp/my-list-of-files-to-create.txt | loop -- 'touch $ITEM'\n    $ ls\n    hello.jpg\n    goodbye.jpg\n\nThis can be combined with various flags, such as `--until-changes`:\n\n    $ printf \"%s\\n\" 1 1 3 | loop --until-changes -- echo '$ITEM'\n    1\n    1\n    3\n\n    $ seq 10 | loop --until-changes -- echo '$ITEM'\n    1\n    2\n\nYou can also easily pipe lists to `loop`:\n\n    $ ls -1 | loop -- 'cp $ITEM $ITEM.bak'; ls\n    hello.jpg\n    hello.jpg.bak\n\n..or via the keyboard with `-i`:\n\n    $ loop -- 'echo $ITEM | tr a-z A-Z' -i\n    hello\n    world^D\n    HELLO\n    WORLD\n\n`--for` can accept all sorts of lists:\n\n    $ loop --for \"`ls`\" -- 'echo $ITEM'\n    Cargo.lock\n    Cargo.toml\n    README.md\n    src\n    target\n    $\n\n## Useful Examples\n\nHere are some handy things you can do with `loop`!\n\n### Testing inputs to a program\n\nIf you have a lot of files and a program, but don't know which file is the one the program takes, you can loop over them until you find it:\n\n    $ ls  -1 | loop --until-success -- './my_program $ITEM';\n\nOr, if you have a list of files but need to find the one which causes your program to fail:\n\n    $ ls  -1 | loop --until-fail -- './my_program $ITEM';\n\n### Waiting for a website to appear online\n\nIf you've just kicked off a website deployment pipeline, you might want to run a process when the site starts returning 200 response codes. With `--every` and `--until-contains`, you can do this without flooding the site with requests:\n\n    $ ./deploy.sh; loop  --every 5s --until-contains 200 -- 'curl -sw \"%{http_code}\" http://coolwebsite.biz'; ./announce_to_slack.sh\n\nOr until a host is online:\n\n    $ loop --until-success -- ping -c 1 mysite.com; ./do_next_thing\n\n### Waiting for a file to be created\n\nIf you have a long-running process that creates a new file, you might want to kick off another program when that process outputs a new file, like so:\n\n    $ ./create_big_file -o my_big_file.bin; loop --until-contains 'my_big_file.bin' -- 'ls'; ./upload_big_file my_big_file.bin\n\n### Create a backup for all files in a directory\n\nIf you've got a whole list of files that you want to create backup copies of, you can do it like so:\n\n    $ ls\n    hello.jpg\n    $ ls -1 | loop -- 'cp $ITEM $ITEM.bak'\n    $ ls\n    hello.jpg\n    hello.jpg.bak\n\n### Keep trying a failing script until it passes, up to 5 times\n\n_This is an [example from StackExchange](https://unix.stackexchange.com/questions/82598/how-do-i-write-a-retry-logic-in-script-to-keep-retrying-to-run-it-upto-5-times/)._\n\n\u003e I want to write logic in shell script which will retry it to run again after 15 sec upto 5 times based on \"status code=FAIL\" if it fails due to some issue.\n\nThere are so many questions like this on StackExchange, which all end up with long threads of complicated answers.\n\nWith `loop`, it's a simple one liner:\n\n    loop --every 15s --until-success --num 5 -- './do_thing.sh'\n\nWhich will do the thing every 15 seconds until it succeeds, for a maximum of five times.\n\n### Keep trying a failing script until timeout\n\nIf dealing with a command or script that occasionally fails in a CI environment, you may want to try for a given amount of time before giving up and failing the build.\n\nWith `loop` you can do that with:\n\n    loop --every 5s --until-success --for-duration 180s --duration-error -- './do_thing.sh'\n\nWhich will do the thing every 5 seconds until it succeeds or until the duration is met. If the duration is met, it will give the same non-zero return as the `timeout` command 124.\n\n### Comparison with GNU Parallel\n\nThis [thread on Reddit](https://www.reddit.com/r/debian/comments/9ha2dj/ive_written_a_useful_system_utility_how_do_i_get/e6abuht/) with GNU Parallel author Ole Tange has some interesting side-by-side comparisons between `loop` and `parallel`.\n\n### More examples\n\nGot any more useful examples? Send a pull request!\n\n## Contributing\n\nThis project is still young, so there is still plenty to be done. Contributions are more than welcome!\n\nPlease file tickets for discussion before submitting patches. Pull requests should target `master` and should leave Loop in a \"shippable\" state if merged.\n\nIf you are adding a non-trivial amount of new code, please include a functioning test in your PR. The test suite will be run by [Travis CI](https://travis-ci.org/Miserlou/Zappa) once you open a pull request. Please include the GitHub issue or pull request URL that has discussion related to your changes as a comment in the code ([example](https://github.com/Miserlou/Zappa/blob/fae2925431b820eaedf088a632022e4120a29f89/zappa/zappa.py#L241-L243)). This greatly helps for project maintainability, as it allows us to trace back use cases and explain decision making. Similarly, please make sure that you meet all of the requirements listed in the [pull request template](https://raw.githubusercontent.com/Miserlou/Zappa/master/.github/PULL_REQUEST_TEMPLATE.md).\n\nPlease feel free to work on any open ticket, especially any ticket marked with the \"help-wanted\" label!\n\n## License\n\n(c) Rich Jones, 2018-2019+. MIT License.\n","funding_links":[],"categories":["Rust","Command-Line Productivity"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMiserlou%2FLoop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMiserlou%2FLoop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMiserlou%2FLoop/lists"}