{"id":22908112,"url":"https://github.com/p6m7g8-dotfiles/p6common","last_synced_at":"2026-04-06T05:01:52.379Z","repository":{"id":43038273,"uuid":"471516348","full_name":"p6m7g8-dotfiles/p6common","owner":"p6m7g8-dotfiles","description":"P6 Dotfiles: POSIX.2 Common utility functions for shell scripting","archived":false,"fork":false,"pushed_at":"2026-01-28T13:25:39.000Z","size":879,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-29T06:07:25.894Z","etag":null,"topics":["bash","cli","common","p6","p6m7g8","sh","shell","terminal","zsh"],"latest_commit_sha":null,"homepage":"https://continuouslearning.io","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/p6m7g8-dotfiles.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-03-18T21:03:22.000Z","updated_at":"2026-01-28T13:25:33.000Z","dependencies_parsed_at":"2024-04-14T21:23:47.974Z","dependency_job_id":"d84ef83b-2013-45ec-b2af-5db633b95f1d","html_url":"https://github.com/p6m7g8-dotfiles/p6common","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/p6m7g8-dotfiles/p6common","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p6m7g8-dotfiles%2Fp6common","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p6m7g8-dotfiles%2Fp6common/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p6m7g8-dotfiles%2Fp6common/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p6m7g8-dotfiles%2Fp6common/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/p6m7g8-dotfiles","download_url":"https://codeload.github.com/p6m7g8-dotfiles/p6common/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/p6m7g8-dotfiles%2Fp6common/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29211774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T22:58:45.823Z","status":"ssl_error","status_checked_at":"2026-02-07T22:58:45.272Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","cli","common","p6","p6m7g8","sh","shell","terminal","zsh"],"created_at":"2024-12-14T03:19:41.675Z","updated_at":"2026-04-03T05:05:07.299Z","avatar_url":"https://github.com/p6m7g8-dotfiles.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# P6's POSIX.2: p6common\n\n## Table of Contents\n\n- [Badges](#badges)\n- [Summary](#summary)\n- [Contributing](#contributing)\n- [Code of Conduct](#code-of-conduct)\n- [Usage](#usage)\n  - [Hooks](#hooks)\n  - [Functions](#functions)\n- [Hierarchy](#hierarchy)\n- [Author](#author)\n\n## Badges\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg)](https://opensource.org/licenses/Apache-2.0)\n\n## Summary\n\nTODO: Add a short summary of this module.\n\n## Contributing\n\n- [How to Contribute](\u003chttps://github.com/p6m7g8-dotfiles/.github/blob/main/CONTRIBUTING.md\u003e)\n\n## Code of Conduct\n\n- [Code of Conduct](\u003chttps://github.com/p6m7g8-dotfiles/.github/blob/main/CODE_OF_CONDUCT.md\u003e)\n\n## Usage\n\n### Hooks\n\n- `init` -\u003e `p6df::modules::p6common::init(_module, dir)`\n\n### Functions\n\n#### cicd\n\n##### p6common/lib/cicd/build.sh\n\n- `p6_cicd_build_run([dockerfile=Dockerfile])`\n  - Synopsis: Builds the current directory with Docker using the given Dockerfile.\n  - Args:\n    - OPTIONAL dockerfile - Dockerfile path [Dockerfile]\n\n##### p6common/lib/cicd/doc.sh\n\n- `p6_cicd_doc_gen()`\n  - Synopsis: Regenerates module inline docs and README for the current module.\n\n##### p6common/lib/cicd/release.sh\n\n- `p6_cicd_release_make()`\n  - Synopsis: Creates a standard-version release and pushes tags to origin.\n\n#### date\n\n##### p6common/lib/date/convert.sh\n\n- `float hours = p6_date_convert_seconds_to_hours(seconds, [scale=3])`\n  - Synopsis: Converts seconds to hours with the requested decimal scale.\n  - Args:\n    - seconds - The seconds to convert\n    - OPTIONAL scale - Default scale is 3 if not provided [3]\n- `str str = p6_date_convert_ms_epoch_to_local(ms_epoch)`\n  - Synopsis: Converts milliseconds since epoch to a local ISO8601 string.\n  - Args:\n    - ms_epoch - milliseconds since epoch\n\n##### p6common/lib/date/fmt.sh\n\n- `p6_date_fmt_relative_to_absolute(relative)`\n  - Synopsis: Converts a relative duration (e.g. \"3 days\") to an absolute date.\n  - Args:\n    - relative - relative duration string\n\n##### p6common/lib/date/math.sh\n\n- `float delta_hours = p6_date_math_delta_in_hours(d1, d2, fmt)`\n  - Synopsis: Returns the delta between d1 and d2 in hours.\n  - Args:\n    - d1 - first date\n    - d2 - second date\n    - fmt - input format\n- `int delta = p6_date_math_delta_in_seconds(d1, d2, in_fmt)`\n  - Synopsis: Returns the delta between d1 and d2 in seconds.\n  - Args:\n    - d1 - first date\n    - d2 - second date\n    - in_fmt - input format\n- `p6_date_math_move(date, offset, fmt_from, fmt_to)`\n  - Synopsis: Applies a date offset and converts from fmt_from to fmt_to.\n  - Args:\n    - date - input date string\n    - offset - date offset expression\n    - fmt_from - input format\n    - fmt_to - output format\n\n##### p6common/lib/date/point.sh\n\n- `int day = p6_date_point_last_day_of_ym(year, month)`\n  - Synopsis: Computes the last day number of a given year and month.\n  - Args:\n    - year - year (YYYY)\n    - month - month (1-12)\n- `int end_month = p6_date_point_last_month_of_quarter(start_month)`\n  - Synopsis: Returns the last month number for a quarter given its start month.\n  - Args:\n    - start_month - quarter start month\n- `int first_month = p6_date_point_first_month_of_quarter(quarter)`\n  - Synopsis: Returns the first month number for a given quarter.\n  - Args:\n    - quarter - quarter (1-4)\n- `p6_date_point_now_epoch_seconds()`\n  - Synopsis: Returns the current time as epoch seconds.\n- `p6_date_point_now_ymd()`\n  - Synopsis: Returns the current date as YYYYMMDD.\n- `p6_date_point_today_ymd()`\n  - Synopsis: Returns today as YYYY-MM-DD.\n- `p6_date_point_tomorrow_ymd()`\n  - Synopsis: Returns tomorrow as YYYYMMDD.\n- `p6_date_point_yesterday_ymd()`\n  - Synopsis: Returns yesterday as YYYYMMDD.\n\n##### p6common/lib/date/range.sh\n\n- `stream  = p6_date_range_fill(start_date, end_date, file, fmt, [sep=])`\n  - Synopsis: Fills missing dates between start and end with zero values from a file.\n  - Args:\n    - start_date - first date (inclusive)\n    - end_date - last date (inclusive)\n    - file - input file of date/value rows\n    - fmt - date format (unused)\n    - OPTIONAL sep - column separator []\n\n#### filter\n\n##### p6common/lib/filter/aggregate.sh\n\n- `filter  = p6_filter_aggregate_count()`\n  - Synopsis: Count identical lines (expects sorted input).\n- `filter  = p6_filter_aggregate_map_reduce()`\n  - Synopsis: Sort input then count identical lines.\n- `filter  = p6_filter_aggregate_table_by_group_with_count([sep=\\t])`\n  - Synopsis: Aggregate group rows into counts and per-column averages. Input: group c1 c2 c3... group c1 c2 c3...\n  - Args:\n    - OPTIONAL sep - field separator [\\t]\n- `filter  = p6_filter_aggregate_table_with_count([sep=\\t])`\n  - Synopsis: Aggregate a table into total count and per-column averages.\n  - Args:\n    - OPTIONAL sep - field separator [\\t]\n\n##### p6common/lib/filter/column.sh\n\n- `filter  = p6_filter_column_pair_to_kv(key_column, value_column, [sep= ], [kv_sep==])`\n  - Synopsis: Emit key/value pairs from two columns.\n  - Args:\n    - key_column - key column index\n    - value_column - value column index\n    - OPTIONAL sep - field separator [ ]\n    - OPTIONAL kv_sep - key/value separator [=]\n- `filter  = p6_filter_column_pluck(columns, [split= ], [selector=])`\n  - Synopsis: Extract columns from delimited input.\n  - Args:\n    - columns - column spec (list, range, or index)\n    - OPTIONAL split - field separator [ ]\n    - OPTIONAL selector - optional line selector []\n- `filter  = p6_filter_column_swap([sep=\\t])`\n  - Synopsis: Swap the first two columns.\n  - Args:\n    - OPTIONAL sep - field separator [\\t]\n- `filter  = p6_filter_columns_count([sep=\\t])`\n  - Synopsis: Print the number of columns in the first row.\n  - Args:\n    - OPTIONAL sep - field separator [\\t]\n\n##### p6common/lib/filter/date.sh\n\n- `filter  = p6_filter_translate_date_to_iso8601_utc(column, input_fmt, ofs, fs)`\n  - Synopsis: Replace a date column with UTC ISO8601 timestamps.\n  - Args:\n    - column - column index\n    - input_fmt - input date format\n    - ofs - output field separator\n    - fs - input field separator\n- `filter  = p6_filter_translate_ms_epoch_to_iso8601_local()`\n  - Synopsis: Convert millisecond epoch values to local ISO8601 timestamps.\n\n##### p6common/lib/filter/escape.sh\n\n- `filter  = p6_filter_sql_escape_single_quote()`\n  - Synopsis: Escape single quotes for SQL string literals.\n\n##### p6common/lib/filter/extract.sh\n\n- `filter  = p6_filter_extract_after(pattern)`\n  - Synopsis: Strip everything through the pattern, leaving content after it.\n  - Args:\n    - pattern - delimiter pattern\n- `filter  = p6_filter_extract_before(pattern)`\n  - Synopsis: Strip everything after the pattern, leaving content before it.\n  - Args:\n    - pattern - delimiter pattern\n- `filter  = p6_filter_extract_between(start, end)`\n  - Synopsis: Extract content between start and end patterns.\n  - Args:\n    - start - start pattern\n    - end - end pattern\n\n##### p6common/lib/filter/kv.sh\n\n- `filter  = p6_filter_kv_value(key, [sep==], [trim=1])`\n  - Synopsis: Extract the value for a key from key/value lines.\n  - Args:\n    - key - key to match\n    - OPTIONAL sep - key/value separator [=]\n    - OPTIONAL trim - trim spaces when 1 [1]\n\n##### p6common/lib/filter/row.sh\n\n- `filter  = p6_filter_row_exclude(selector)`\n  - Synopsis: Exclude rows matching a pattern.\n  - Args:\n    - selector - pattern\n- `filter  = p6_filter_row_exclude_icase(selector)`\n  - Synopsis: Exclude rows matching a pattern, case-insensitive.\n  - Args:\n    - selector - pattern\n- `filter  = p6_filter_row_exclude_regex(selector)`\n  - Synopsis: Exclude rows matching an extended regex.\n  - Args:\n    - selector - extended regex pattern\n- `filter  = p6_filter_row_first(n)`\n  - Synopsis: Keep only the first N rows.\n  - Args:\n    - n - number of rows\n- `filter  = p6_filter_row_from_end(n)`\n  - Synopsis: Emit the Nth row from the end.\n  - Args:\n    - n - index from end\n- `filter  = p6_filter_row_last(n)`\n  - Synopsis: Keep only the last N rows.\n  - Args:\n    - n - number of rows\n- `filter  = p6_filter_row_n(n)`\n  - Synopsis: Emit the Nth row from the start.\n  - Args:\n    - n - row number\n- `filter  = p6_filter_row_select(...)`\n  - Synopsis: Select rows matching the given grep arguments.\n  - Args:\n    - ... - grep options and pattern\n- `filter  = p6_filter_row_select_and_after(selector, count)`\n  - Synopsis: Select matching rows and the following N rows.\n  - Args:\n    - selector - pattern\n    - count - number of following rows\n- `filter  = p6_filter_row_select_icase(selector)`\n  - Synopsis: Select rows matching a pattern, case-insensitive.\n  - Args:\n    - selector - pattern\n- `filter  = p6_filter_row_select_regex(selector)`\n  - Synopsis: Select rows matching an extended regex.\n  - Args:\n    - selector - extended regex pattern\n- `filter  = p6_filter_rows_count()`\n  - Synopsis: Count the number of rows.\n\n##### p6common/lib/filter/sort.sh\n\n- `filter  = p6_filter_sort(...)`\n  - Synopsis: Sort stdin using `sort` with any provided options.\n  - Args:\n    - ... - sort options\n- `filter  = p6_filter_sort_by_column(column, [sep=])`\n  - Synopsis: Sort stdin by a column number with an optional separator.\n  - Args:\n    - column - column number (1-based)\n    - OPTIONAL sep - field separator []\n- `filter  = p6_filter_sort_by_key(key, [sep=])`\n  - Synopsis: Sort stdin by a key field with an optional field separator.\n  - Args:\n    - key - sort key spec\n    - OPTIONAL sep - field separator []\n- `filter  = p6_filter_sort_numeric_by_column(column, [sep=])`\n  - Synopsis: Sort stdin numerically by a column number.\n  - Args:\n    - column - column number (1-based)\n    - OPTIONAL sep - field separator []\n- `filter  = p6_filter_sort_numeric_reverse(...)`\n  - Synopsis: Sort stdin numerically in reverse order.\n  - Args:\n    - ... - sort options\n- `filter  = p6_filter_sort_reverse(...)`\n  - Synopsis: Sort stdin in reverse order.\n  - Args:\n    - ... - sort options\n- `filter  = p6_filter_sort_reverse_by_column(column, [sep=])`\n  - Synopsis: Sort stdin by a column number in reverse order.\n  - Args:\n    - column - column number (1-based)\n    - OPTIONAL sep - field separator []\n- `filter  = p6_filter_sort_reverse_by_key(key, [sep=])`\n  - Synopsis: Sort stdin by a key field in reverse order.\n  - Args:\n    - key - sort key spec\n    - OPTIONAL sep - field separator []\n- `filter  = p6_filter_sort_unique(...)`\n  - Synopsis: Sort stdin and remove duplicate lines.\n  - Args:\n    - ... - sort options\n\n##### p6common/lib/filter/string.sh\n\n- `filter  = p6_filter_lowercase()`\n  - Synopsis: Lowercase each input line.\n- `filter  = p6_filter_string_first_character()`\n  - Synopsis: Emit the first character of each input line.\n- `filter  = p6_filter_uppercase()`\n  - Synopsis: Uppercase each input line.\n\n##### p6common/lib/filter/strip.sh\n\n- `filter  = p6_filter_dos2unix()`\n  - Synopsis: Convert CRLF line endings to LF.\n- `filter  = p6_filter_strip_alum()`\n  - Synopsis: Remove alphanumeric characters from each line.\n- `filter  = p6_filter_strip_alum_and_underscore()`\n  - Synopsis: Remove alphanumerics and underscores from each line.\n- `filter  = p6_filter_strip_chars(chars)`\n  - Synopsis: Strip the specified characters from each line.\n  - Args:\n    - chars - characters to remove\n- `filter  = p6_filter_strip_double_quote()`\n  - Synopsis: Strip double quotes from each line.\n- `filter  = p6_filter_strip_leading_and_trailing_spaces()`\n  - Synopsis: Strip leading and trailing spaces from each line.\n- `filter  = p6_filter_strip_leading_spaces()`\n  - Synopsis: Strip leading spaces from each line.\n- `filter  = p6_filter_strip_quotes()`\n  - Synopsis: Strip both single and double quotes from each line.\n- `filter  = p6_filter_strip_single_quote()`\n  - Synopsis: Strip single quotes from each line.\n- `filter  = p6_filter_strip_spaces()`\n  - Synopsis: Remove all spaces from each line.\n- `filter  = p6_filter_strip_trailing_slash()`\n  - Synopsis: Strip a trailing slash from each line.\n- `filter  = p6_filter_strip_trailing_spaces()`\n  - Synopsis: Strip trailing spaces from each line.\n\n##### p6common/lib/filter/translate.sh\n\n- `filter  = p6_filter_convert_multispace_delimited_columns_to_pipes()`\n  - Synopsis: Convert multi-space-delimited columns to pipes.\n- `filter  = p6_filter_insert_null_at_position(position)`\n  - Synopsis: Ensure a NULL field exists at the given position.\n  - Args:\n    - position - 1-based field position\n- `filter  = p6_filter_replace(from, to, [flags=g])`\n  - Synopsis: Replace pattern matches with a replacement string on each line.\n  - Args:\n    - from - pattern to replace\n    - to - replacement string\n    - OPTIONAL flags - sed flags [g]\n- `filter  = p6_filter_strip_leading_go()`\n  - Synopsis: Strip a leading 'go' prefix when present.\n- `filter  = p6_filter_strip_leading_v()`\n  - Synopsis: Strip a leading 'v' prefix when present.\n- `filter  = p6_filter_strip_scala3_prefix()`\n  - Synopsis: Strip a leading 'scala3-' prefix when present.\n- `filter  = p6_filter_translate_blank_to_null()`\n  - Synopsis: Replace empty pipe-delimited fields with NULL.\n- `filter  = p6_filter_translate_first_field_slash_to_pipe()`\n  - Synopsis: Replace the first field's slash with a pipe.\n- `filter  = p6_filter_translate_glob_to_underscore(glob)`\n  - Synopsis: Replace a glob pattern with underscores on each line.\n  - Args:\n    - glob - glob pattern to replace\n- `filter  = p6_filter_translate_hex_pairs_to_csv()`\n  - Synopsis: Insert commas after each pair of hex characters.\n- `filter  = p6_filter_translate_parens_to_slash()`\n  - Synopsis: Replace parentheses with slashes.\n- `filter  = p6_filter_translate_quoted_null_to_null()`\n  - Synopsis: Replace quoted 'NULL' tokens with bare NULL.\n- `filter  = p6_filter_translate_resource_records_label_to_tab()`\n  - Synopsis: Replace RESOURCERECORDS with a tab delimiter.\n- `filter  = p6_filter_translate_space_to_newline()`\n  - Synopsis: Translate spaces into newlines.\n- `filter  = p6_filter_translate_space_to_tab()`\n  - Synopsis: Replace spaces with tabs.\n- `filter  = p6_filter_translate_space_to_underscore()`\n  - Synopsis: Replace spaces with underscores.\n- `filter  = p6_filter_translate_start_to_arg(arg)`\n  - Synopsis: Prefix each line with the provided string.\n  - Args:\n    - arg - prefix string\n- `filter  = p6_filter_translate_tab_to_pipe()`\n  - Synopsis: Replace tabs with pipe separators.\n- `filter  = p6_filter_translate_trailing_slash_bang_to_bang()`\n  - Synopsis: Replace '/!' sequences with '!'.\n- `filter  = p6_filter_translate_words_to_sql_list([sep=|])`\n  - Synopsis: Convert delimited words into a SQL list tuple.\n  - Args:\n    - OPTIONAL sep - field separator [|]\n\n#### math\n\n##### p6common/lib/math/arithmetic.sh\n\n- `int result = p6_math_dec(a, [b=1])`\n  - Synopsis: Subtracts b from a (default 1) and returns the integer result.\n  - Args:\n    - a - base value\n    - OPTIONAL b - decrement [1]\n- `int result = p6_math_inc(a, [b=1])`\n  - Synopsis: Adds b to a (default 1) and returns the integer result.\n  - Args:\n    - a - base value\n    - OPTIONAL b - increment [1]\n- `int result = p6_math_multiply(a, b)`\n  - Synopsis: Multiplies a by b and returns the integer result.\n  - Args:\n    - a - left operand\n    - b - right operand\n- `int rv = p6_math_sub(a, b)`\n  - Synopsis: Subtracts b from a and returns the integer result.\n  - Args:\n    - a - minuend\n    - b - subtrahend\n- `p6_math_gt(a, b)`\n  - Synopsis: Returns true when a is greater than b.\n  - Args:\n    - a - left operand\n    - b - right operand\n- `p6_math_gte(a, b)`\n  - Synopsis: Returns true when a is greater than or equal to b.\n  - Args:\n    - a - left operand\n    - b - right operand\n- `p6_math_lt(a, b)`\n  - Synopsis: Returns true when a is less than b.\n  - Args:\n    - a - left operand\n    - b - right operand\n- `p6_math_lte(a, b)`\n  - Synopsis: Returns true when a is less than or equal to b.\n  - Args:\n    - a - left operand\n    - b - right operand\n\n##### p6common/lib/math/range.sh\n\n- `words elements = p6_math_range_generate(begin, end)`\n  - Synopsis: Generates an inclusive numeric range from begin to end.\n  - Args:\n    - begin - range start\n    - end - range end\n\n#### network\n\n##### p6common/lib/network/download.sh\n\n- `path dest = p6_network_file_download(url, dest, ...)`\n  - Synopsis: Downloads a URL to a destination file.\n  - Args:\n    - url - source URL\n    - dest - destination file path\n    - ... - unused extra args\n\n##### p6common/lib/network/network.sh\n\n- `ipv4 ip = p6_network_ip_public()`\n  - Synopsis: Fetches the public IPv4 address via ifconfig.me.\n\n##### p6common/lib/network/ssh.sh\n\n- `p6_ssh_key_add(key_file_priv)`\n  - Synopsis: Adds a private key to the ssh-agent.\n  - Args:\n    - key_file_priv - private key file\n- `p6_ssh_key_check(priv, test_pub)`\n  - Synopsis: Compares a private key to a public key for a match.\n  - Args:\n    - priv - private key file\n    - test_pub - public key file to compare\n- `p6_ssh_key_delete(key_file_priv)`\n  - Synopsis: Removes all keys from ssh-agent (with OS-specific flags).\n  - Args:\n    - key_file_priv - private key file\n- `p6_ssh_key_fingerprint(key_file_pub)`\n  - Synopsis: Prints the fingerprint for a public key.\n  - Args:\n    - key_file_pub - public key file\n- `p6_ssh_key_make(key_file_priv)`\n  - Synopsis: Creates a new ed25519 SSH key pair.\n  - Args:\n    - key_file_priv - private key file\n- `p6_ssh_key_pub_from_priv(key_file_priv, [key_file_pub=${key_file_priv])`\n  - Synopsis: Derives a public key from a private key file.\n  - Args:\n    - key_file_priv - private key file\n    - OPTIONAL key_file_pub - public key file [${key_file_priv]\n- `p6_ssh_key_remove(key_file_priv, [key_file_pub=${key_file_priv])`\n  - Synopsis: Removes a private key and its associated public key.\n  - Args:\n    - key_file_priv - private key file\n    - OPTIONAL key_file_pub - public key file [${key_file_priv]\n- `p6_ssh_keys_chmod(key_file_priv)`\n  - Synopsis: Sets secure permissions on the private key and its directory.\n  - Args:\n    - key_file_priv - private key file\n\n#### openssl\n\n##### p6common/lib/openssl/ciphers.sh\n\n- `str str = p6_openssl_ciphers()`\n  - Synopsis: Shows the supported Ciphers\n\n##### p6common/lib/openssl/req.sh\n\n- `p6_openssl_req_csr_create(key_file, csr_file, subject)`\n  - Synopsis: Generates a new RSA key and CSR with the provided subject.\n  - Args:\n    - key_file - name of Key File (created)\n    - csr_file - name of Certificate Signing Request file (created)\n    - subject - I.E:  \"/C=US/ST=Maryland/L=Bowie/O=P6M7G8/OU=Technology/CN=p6m7g8.net\"\n\n##### p6common/lib/openssl/s_client.sh\n\n- `p6_openssl_alias(host, port, ...)`\n  - Synopsis: Prints the certificate alias from a TLS server.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_alt_name(host, port, ...)`\n  - Synopsis: Prints the certificate Subject Alternative Names.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_not_after(host, port, ...)`\n  - Synopsis: Prints the certificate notAfter date from a TLS server.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_not_before(host, port, ...)`\n  - Synopsis: Prints the certificate notBefore date from a TLS server.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_not_purpose(host, port, ...)`\n  - Synopsis: Lists certificate purposes that are marked No.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_purpose(host, port, ...)`\n  - Synopsis: Lists certificate purposes that are marked Yes.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_s_client_connect(host, [port=443], ...)`\n  - Synopsis: Connects to $host on $port and returns the SSL Cert This already redirected to STDOUT Additional openssl options can be passed SSL is not allowed, only TLSv1+ \u003c!-- markdownlint-disable-line MD013 --\u003e\n  - Args:\n    - host - FQDN of the website\n    - OPTIONAL port - TCP port [443]\n    - ... - additional openssl options\n- `p6_openssl_serial(host, port, ...)`\n  - Synopsis: Prints the certificate serial number from a TLS server.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n- `p6_openssl_subject(host, port, ...)`\n  - Synopsis: Prints the certificate subject from a TLS server.\n  - Args:\n    - host - FQDN of the website\n    - port - TCP port\n    - ... - additional openssl options\n\n##### p6common/lib/openssl/s_server.sh\n\n- `p6_openssl_s_server_run(key, crt, ...)`\n  - Synopsis: Default port is 4433 to listen on Responds to a 'GET /' with a status page\n  - Args:\n    - key - path to the key file\n    - crt - path to the certificate file\n    - ... - additional openssl options\n\n##### p6common/lib/openssl/util.sh\n\n- `p6_openssl_certificate_create(key_file, csr_file, subject, [cert_exp=365])`\n  - Synopsis: Geenrates key_file, csr_file, and outputs certificate to stdout\n  - Args:\n    - key_file - name of Key File (created)\n    - csr_file - name of Certificate Signing Request file (created)\n    - subject - I.E:  \"/C=US/ST=Maryland/L=Bowie/O=P6M7G8/OU=Technology/CN=p6m7g8.net\"\n    - OPTIONAL cert_exp - Ceritificate Expiration in days [365]\n\n##### p6common/lib/openssl/version.sh\n\n- `str str = p6_openssl_version()`\n  - Synopsis: Returns the output of `openssl version -a`.\n\n##### p6common/lib/openssl/x509.sh\n\n- `p6_openssl_req_cert_self_signed_create(key_file, csr_file, [cert_exp=365])`\n  - Synopsis: Generates a self-signed certificate from a CSR and key.\n  - Args:\n    - key_file - private key file\n    - csr_file - CSR file\n    - OPTIONAL cert_exp - certificate expiration in days [365]\n\n#### p6common\n\n##### p6common/init.zsh\n\n- `p6df::modules::p6common::gha::ModuleDeps(module)`\n  - Args:\n    - module -\n- `p6df::modules::p6common::init(_module, dir)`\n  - Args:\n    - _module -\n    - dir -\n\n#### p6common/conf/debug\n\n##### p6common/conf/debug/log-debug.sh\n\n- `p6_log(msg)`\n  - Args:\n    - msg -\n- `p6_log_disable()`\n- `p6_log_enable()`\n\n##### p6common/conf/debug/return.sh\n\n- `bool  = p6_return_bool(bool)`\n  - Synopsis: Exactly 0 or 1 No blanks Suitable for use in conditionals\n  - Args:\n    - bool - boolean value (0 or 1)\n- `false  = p6_return_false()`\n  - Synopsis: Suitable for use in conditionals\n- `filter  = p6_return_filter()`\n  - Synopsis: Filters return this for syntaxtic sugar Maintains the filters $? rc code for pipe chain short circuits\n- `float  = p6_return_float(float)`\n  - Synopsis: Any floating point No blanks\n  - Args:\n    - float - floating-point number\n- `int  = p6_return_int(int)`\n  - Synopsis: Any Integer Positive or Negative\n  - Args:\n    - int - integer (positive or negative)\n- `ipv4  = p6_return_ipv4(ip)`\n  - Synopsis: Any IP v4 address\n  - Args:\n    - ip - IPv4 address string\n- `p6_return_code_as_code(rc)`\n  - Synopsis: Validates rc and returns it as the function exit code.\n  - Args:\n    - rc - return code (0..255)\n- `p6_return_code_as_value(rc)`\n  - Synopsis: Validates rc and prints it to stdout.\n  - Args:\n    - rc - return code (0..255)\n- `p6_return_date(date)`\n  - Synopsis: Only the listed dates are allowed Think twice before adding more\n  - Args:\n    - date - date string in accepted formats\n- `p6_return_void()`\n  - Synopsis: The literal absence of a return value Do not use this in conditionals Do not use this in blank string checks Use me when the function simply groups commands for re-use \u003c!-- markdownlint-disable-line MD013 --\u003e\n- `path  = p6_return_path(path)`\n  - Synopsis: Specialized string of well formed simple unix paths Only /, letters, numbers, -, _, @, +, ~, ., ',' NO SPACES, QUOTES etc... \u003c!-- markdownlint-disable-line MD013 --\u003e\n  - Args:\n    - path - unix-like path string\n- `size_t  = p6_return_size_t(size_t)`\n  - Synopsis: Any Positive Integer No blanks\n  - Args:\n    - size_t - non-negative integer\n- `str  = p6_return_str(str)`\n  - Synopsis: Any string BLANKS allowed\n  - Args:\n    - str - string value (blanks allowed)\n- `stream  = p6_return_stream()`\n  - Synopsis: Function emits arbitrary text\n- `true  = p6_return(rv)`\n  - Synopsis: Prints rv to stdout and returns success.\n  - Args:\n    - rv - value to echo\n- `true  = p6_return_true()`\n  - Synopsis: Suitable for use in conditionals\n- `words  = p6_return_words(words)`\n  - Synopsis: A word is a loop item. Words is a collection of words. Words should be split on $IFS \"\" or '' count as a blank word \u003c!-- markdownlint-disable-line MD013 --\u003e\n  - Args:\n    - words - words list (preserves splits)\n\n##### p6common/conf/debug/time-debug.sh\n\n- `p6_time(t0, t1, msg)`\n  - Args:\n    - t0 -\n    - t1 -\n    - msg -\n\n#### p6common/conf/prod\n\n##### p6common/conf/prod/log-prod.sh\n\n- `p6_log(msg)`\n  - Args:\n    - msg -\n- `p6_log_disable()`\n- `p6_log_enable()`\n\n##### p6common/conf/prod/return.sh\n\n- `bool  = p6_return_bool()`\n- `false  = p6_return_false()`\n- `filter  = p6_return_filter()`\n- `float  = p6_return_float()`\n- `int  = p6_return_int()`\n- `ipv4  = p6_return_ipv4()`\n- `p6_return()`\n- `p6_return_code_as_code()`\n- `p6_return_code_as_value()`\n- `p6_return_date()`\n- `p6_return_void()`\n- `path  = p6_return_path()`\n- `size_t  = p6_return_size_t()`\n- `str  = p6_return_str()`\n- `stream  = p6_return_stream()`\n- `true  = p6_return_true()`\n- `words  = p6_return_words()`\n\n##### p6common/conf/prod/time-prod.sh\n\n- `p6_time(t0, t1, msg)`\n  - Args:\n    - t0 -\n    - t1 -\n    - msg -\n\n#### p6common/lib\n\n##### p6common/lib/_bootstrap.sh\n\n- `p6_bootstrap([dir=$P6_DFZ_SRC_P6M7G8_DOTFILES_DIR/p6common], [islocal=])`\n  - Synopsis: Loads p6common library files from dir and adds its bin to PATH.\n  - Args:\n    - OPTIONAL dir - library root to load [$P6_DFZ_SRC_P6M7G8_DOTFILES_DIR/p6common]\n    - OPTIONAL islocal - unused flag for local bootstrap []\n\n#### stdio\n\n##### p6common/lib/stdio/color.sh\n\n- `float 0.0 = p6_color_opacity_factor()`\n  - Synopsis: Return the default opacity factor.\n- `p6_color_ize(color_fg, color_bg, msg)`\n  - Synopsis: Print a message with foreground/background colors (no newline).\n  - Args:\n    - color_fg - foreground color name\n    - color_bg - background color name\n    - msg - message text\n- `p6_color_say(color_fg, color_bg, msg)`\n  - Synopsis: Print a message with foreground/background colors and newline.\n  - Args:\n    - color_fg - foreground color name\n    - color_bg - background color name\n    - msg - message text\n- `size_t channel = p6_color_hext_to_rgb(h)`\n  - Synopsis: Convert a hex byte to a numeric channel value.\n  - Args:\n    - h - hex byte\n- `size_t code = p6_color_to_code(color)`\n  - Synopsis: Convert a color name to a tput color code.\n  - Args:\n    - color - color name\n- `str dr = p6_color_hex_to_d16b(hex, ord)`\n  - Synopsis: Convert hex RGB to a 16-bit channel value.\n  - Args:\n    - hex - hex RGB string\n    - ord - channel selector (r/g/b)\n- `str rgb = p6_color_name_to_rgb(name)`\n  - Synopsis: Convert a color name to a hex RGB string.\n  - Args:\n    - name - color name\n\n##### p6common/lib/stdio/debug.sh\n\n- `p6_debug_load()`\n\n##### p6common/lib/stdio/dir.sh\n\n- `p6_dir_cd(dir)`\n  - Synopsis: Change to a directory with logging.\n  - Args:\n    - dir - directory path\n- `p6_dir_cp(src, dst)`\n  - Synopsis: Copy a directory recursively.\n  - Args:\n    - src - source directory\n    - dst - destination directory\n- `p6_dir_exists(dir)`\n  - Synopsis: Return success when a directory exists.\n  - Args:\n    - dir - directory path\n- `p6_dir_exists_NOT(dir)`\n  - Synopsis: Return success when a directory does NOT exist (inverse of p6_dir_exists).\n  - Args:\n    - dir - directory path\n- `p6_dir_load(dirs)`\n  - Synopsis: Load files from each directory in the list.\n  - Args:\n    - dirs - directory list\n- `p6_dir_mk(dir)`\n  - Synopsis: Create a directory if it does not exist.\n  - Args:\n    - dir - directory path\n- `p6_dir_mv(src, dst)`\n  - Synopsis: Move or rename a directory.\n  - Args:\n    - src - source directory\n    - dst - destination directory\n- `p6_dir_replace_in(dir, from, to)`\n  - Synopsis: Replace text in files under a directory tree.\n  - Args:\n    - dir - directory root (unused in current implementation)\n    - from - pattern to replace\n    - to - replacement text\n- `p6_dir_rmrf(dir)`\n  - Synopsis: Remove a directory tree with safety checks.\n  - Args:\n    - dir - directory path\n- `words children = p6_dir_list(dir)`\n  - Synopsis: List entries in a directory.\n  - Args:\n    - dir - directory path\n- `words descendants = p6_dir_list_recursive(dir)`\n  - Synopsis: List files recursively under a directory.\n  - Args:\n    - dir - directory path\n- `words entries = p6_dirs_list(dirs)`\n  - Synopsis: List entries across multiple directories.\n  - Args:\n    - dirs - directory list\n\n##### p6common/lib/stdio/file.sh\n\n- `bool rv = p6_file_executable(file)`\n  - Synopsis: Return true when a file is executable.\n  - Args:\n    - file - file path\n- `bool rv = p6_file_exists(file)`\n  - Synopsis: Return true when a file is readable.\n  - Args:\n    - file - file path\n- `bool rv = p6_file_exists_NOT(file)`\n  - Synopsis: Return true when a file is NOT readable (inverse of p6_file_exists).\n  - Args:\n    - file - file path\n- `int count = p6_file_lines(file)`\n  - Synopsis: Count the number of lines in a file.\n  - Args:\n    - file - file path\n- `p6_file_append(file, contents)`\n  - Synopsis: Append contents to a file.\n  - Args:\n    - file - file path\n    - contents - content to append\n- `p6_file_contains(pattern, file)`\n  - Synopsis: Select lines from a file matching a pattern.\n  - Args:\n    - pattern - grep pattern\n    - file - file path\n- `p6_file_copy(src, dst)`\n  - Synopsis: Copy a file.\n  - Args:\n    - src - source path\n    - dst - destination path\n- `p6_file_create(file)`\n  - Synopsis: Create an empty file.\n  - Args:\n    - file - file path\n- `p6_file_display(file)`\n  - Synopsis: Output the contents of a file if it exists.\n  - Args:\n    - file - file path\n- `p6_file_line_delete_last(file)`\n  - Synopsis: Delete the last line of a file in place.\n  - Args:\n    - file - file path\n- `p6_file_load(file)`\n  - Synopsis: Source a file, honoring P6_PREFIX when set.\n  - Args:\n    - file - file path\n- `p6_file_ma_sync(from, to)`\n  - Synopsis: Sync modification time from one file to another.\n  - Args:\n    - from - source file\n    - to - target file\n- `p6_file_marker_delete_to_end(file, marker)`\n  - Synopsis: Delete from the marker line to the end of the file.\n  - Args:\n    - file - file path\n    - marker - marker pattern\n- `p6_file_move(src, dst)`\n  - Synopsis: Move or rename a file.\n  - Args:\n    - src - source path\n    - dst - destination path\n- `p6_file_replace(file, sed_cmd, file, sed_cmd)`\n  - Synopsis: Run a sed expression in-place on a file.\n  - Args:\n    - file - file path\n    - sed_cmd - sed expression\n    - file - file path\n    - sed_cmd - sed expression\n- `p6_file_replace(file, sed_cmd, file, sed_cmd)`\n  - Synopsis: Run a sed expression in-place on a file.\n  - Args:\n    - file - file path\n    - sed_cmd - sed expression\n    - file - file path\n    - sed_cmd - sed expression\n- `p6_file_rmf(file)`\n  - Synopsis: Remove a file if it exists.\n  - Args:\n    - file - file path\n- `p6_file_symlink(to, from)`\n  - Synopsis: Create a symbolic link.\n  - Args:\n    - to - link target\n    - from - link path\n- `p6_file_unlink(file)`\n  - Synopsis: Unlink a file.\n  - Args:\n    - file - file path\n- `p6_file_write(file, contents)`\n  - Synopsis: Overwrite a file with the given contents.\n  - Args:\n    - file - file path\n    - contents - content to write\n- `path path/$cmd = p6_file_cascade(cmd, exts, ...)`\n  - Synopsis: Search paths for a command, optionally with extensions.\n  - Args:\n    - cmd - command name\n    - exts - extension list\n    - ... - search paths\n- `size_t modified_epoch_seconds = p6_file_mtime(file)`\n  - Synopsis: Return the file's modification time in epoch seconds.\n  - Args:\n    - file - file path\n- `str line = p6_file_line_first(file)`\n  - Synopsis: Return the first line of a file.\n  - Args:\n    - file - file path\n- `str lines = p6_file_lines_except_first(file)`\n  - Synopsis: Return all lines except the first.\n  - Args:\n    - file - file path\n- `str lines = p6_file_lines_first(file, n)`\n  - Synopsis: Return the first N lines of a file.\n  - Args:\n    - file - file path\n    - n - number of lines\n- `str lines = p6_file_lines_last(file, n)`\n  - Synopsis: Return the last N lines of a file.\n  - Args:\n    - file - file path\n    - n - number of lines\n\n##### p6common/lib/stdio/interactive.sh\n\n- `p6_int_confirm_ask()`\n  - Synopsis: Prompt for Y/n confirmation and exit on \"n\".\n- `str PASSWORD = p6_int_password_read()`\n  - Synopsis: Read a password from stdin without echo.\n\n##### p6common/lib/stdio/io.sh\n\n- `p6_die(code, ...)`\n  - Synopsis: Print a message and exit with the given code.\n  - Args:\n    - code - exit code\n    - ... - message text\n- `p6_echo()`\n  - Synopsis: Echo arguments to stdout.\n- `p6_error(msg)`\n  - Synopsis: Print an error message to stderr.\n  - Args:\n    - msg - message text\n- `p6_h1(header)`\n  - Synopsis: Print a level-1 header.\n  - Args:\n    - header - header text\n- `p6_h2(header)`\n  - Synopsis: Print a level-2 header.\n  - Args:\n    - header - header text\n- `p6_h3(header)`\n  - Synopsis: Print a level-3 header.\n  - Args:\n    - header - header text\n- `p6_h4(header)`\n  - Synopsis: Print a level-4 header.\n  - Args:\n    - header - header text\n- `p6_h5(header)`\n  - Synopsis: Print a level-5 header.\n  - Args:\n    - header - header text\n- `p6_h6(header)`\n  - Synopsis: Print a level-6 header.\n  - Args:\n    - header - header text\n- `p6_msg(msg)`\n  - Synopsis: Print a message with a trailing newline.\n  - Args:\n    - msg - message text\n- `p6_msg_fail()`\n  - Synopsis: Print a failure message with a cross prefix.\n- `p6_msg_h3()`\n  - Synopsis: Print a level-3 header message.\n- `p6_msg_no_nl(msg)`\n  - Synopsis: Print a message without a trailing newline.\n  - Args:\n    - msg - message text\n- `p6_msg_success()`\n  - Synopsis: Print a success message with a checkmark prefix.\n- `p6_vertical(v)`\n  - Synopsis: Print a colon-delimited string vertically, one per line.\n  - Args:\n    - v - colon-delimited string\n\n##### p6common/lib/stdio/verbose.sh\n\n- `p6_verbose(level, ...)`\n  - Synopsis: Print messages when verbosity meets the required level.\n  - Args:\n    - level - minimum verbosity before output\n    - ... - message text\n\n#### stdlib\n\n##### p6common/lib/stdlib/alias.sh\n\n- `p6_alias(from, to)`\n  - Synopsis: Define a shell alias from one name to another command.\n  - Args:\n    - from - alias name\n    - to - alias expansion\n- `p6_alias_cd_dirs(dir)`\n  - Synopsis: Create cd aliases for each subdirectory of a dir.\n  - Args:\n    - dir - directory path\n\n##### p6common/lib/stdlib/ctl.sh\n\n- `p6_ctl_cmd_build(dockerfile)`\n  - Synopsis: Build the module using a Dockerfile.\n  - Args:\n    - dockerfile - Dockerfile path\n- `p6_ctl_cmd_docker_build()`\n  - Synopsis: Prepare docker build environment dependencies.\n- `p6_ctl_cmd_docker_test()`\n  - Synopsis: Run the test suite in a docker environment.\n- `p6_ctl_cmd_install([home=pgollucci/home])`\n  - Synopsis: Install p6 dotfiles into a target home repo.\n  - Args:\n    - OPTIONAL home - GitHub repo for home config [pgollucci/home]\n- `p6_ctl_cmd_test()`\n  - Synopsis: Run the module test suite.\n- `p6_ctl_run(...)`\n  - Synopsis: Parse CLI arguments and dispatch a p6ctl subcommand.\n  - Args:\n    - ... - arguments for p6ctl\n- `p6_ctl_usage([rc=0], [msg=])`\n  - Synopsis: Print usage and exit with the specified code.\n  - Args:\n    - OPTIONAL rc - exit code [0]\n    - OPTIONAL msg - optional message []\n\n##### p6common/lib/stdlib/diag.sh\n\n- `p6_diagnostics()`\n  - Synopsis: Add this to a Jenkins Job to see stuff or kubectl exec -it --rm foo -- /bin/bash into and look around\n\n##### p6common/lib/stdlib/dryrunning.sh\n\n- `bool rv = p6_dryrunning()`\n  - Synopsis: Return true when dry-run mode is enabled.\n\n##### p6common/lib/stdlib/edit.sh\n\n- `p6_edit_editor_run(editor, scratch_file)`\n  - Synopsis: Run an editor command on a scratch file.\n  - Args:\n    - editor - editor command\n    - scratch_file - file to edit\n- `path scratch_file = p6_edit_scratch_file_create(msg)`\n  - Synopsis: Create a scratch file prefilled with a message.\n  - Args:\n    - msg - initial contents\n\n##### p6common/lib/stdlib/env.sh\n\n- `p6_env_export(var, val)`\n  - Synopsis: Export an environment variable with a value.\n  - Args:\n    - var - variable name\n    - val - variable value\n- `p6_env_export_un(var)`\n  - Synopsis: Unset an exported environment variable.\n  - Args:\n    - var - variable name\n- `p6_env_list(glob)`\n  - Synopsis: List environment variables, optionally filtered by a pattern.\n  - Args:\n    - glob - grep pattern\n- `p6_env_list_p6()`\n  - Synopsis: List all P6-related environment variables.\n\n##### p6common/lib/stdlib/lang.sh\n\n- `str prefix = p6_lang_cmd_2_env(cmd)`\n  - Synopsis: Map a language command to its prefix.\n  - Args:\n    - cmd - command name\n- `str rcmd = p6_lang_env_2_cmd(prefix)`\n  - Synopsis: Map a language prefix to its command name.\n  - Args:\n    - prefix - language prefix\n- `str ver = p6_lang_system_version(prefix)`\n  - Synopsis: Return the system version for a language prefix.\n  - Args:\n    - prefix - language prefix\n- `str ver = p6_lang_version(prefix)`\n  - Synopsis: Return the active version for a language prefix.\n  - Args:\n    - prefix - language prefix (py, rb, node, etc.)\n\n##### p6common/lib/stdlib/misc.sh\n\n- `p6_pgs()`\n  - Synopsis: Find and replace a pattern across files under the current tree.\n- `p6_xclean()`\n  - Synopsis: Delete common editor and backup junk files under the tree.\n\n##### p6common/lib/stdlib/os.sh\n\n- `str name = p6_os_name()`\n  - Synopsis: Return the OS kernel release string.\n\n##### p6common/lib/stdlib/path.sh\n\n- `p6_cdpath_current()`\n  - Synopsis: Print the current CDPATH entries vertically.\n- `p6_path_current()`\n  - Synopsis: Print the current PATH entries vertically.\n- `p6_path_default()`\n  - Synopsis: Reset PATH to a standard set of directories.\n- `true  = p6_path_if(dir, [where=append])`\n  - Synopsis: Add a directory to PATH when it exists.\n  - Args:\n    - dir - directory path\n    - OPTIONAL where - append or prepend [append]\n\n##### p6common/lib/stdlib/retry.sh\n\n- `p6_retry_delay_doubling()`\n  - Synopsis: Sleep and double the retry delay.\n- `p6_retry_delay_log()`\n  - Synopsis: Sleep and compute the next delay using log strategy.\n- `size_t i = p6_retry_delay(type, i)`\n  - Synopsis: Sleep for a delay and compute the next delay value.\n  - Args:\n    - type - delay strategy\n    - i - current delay\n\n##### p6common/lib/stdlib/run.sh\n\n- `p6_run_code(code)`\n  - Synopsis: Log and execute a command string.\n  - Args:\n    - code - command string\n- `p6_run_code_and_result(code)`\n  - Synopsis: Eval a command and eval its output into the shell.\n  - Args:\n    - code - command string\n- `p6_run_dir(dir, ...)`\n  - Synopsis: Run a command within a directory and return to the original.\n  - Args:\n    - dir - working directory\n    - ... - command to run\n- `p6_run_if(thing, ...)`\n  - Synopsis: Run a function or command if it exists.\n  - Args:\n    - thing - function or command name\n    - ... - arguments\n- `p6_run_parallel(i, parallel, things, cmd, ...)`\n  - Synopsis: Run a command in parallel over a list of items.\n  - Args:\n    - i - starting index\n    - parallel - max parallel jobs\n    - things - item list\n    - cmd - command to run\n    - ... - command arguments\n- `p6_run_read_cmd(cmd)`\n  - Synopsis: Run a command string (read-style helper).\n  - Args:\n    - cmd - command string\n- `p6_run_retry(stop, fail, func, ...)`\n  - Synopsis: Retry a function until a stop condition is met.\n  - Args:\n    - stop - stop callback\n    - fail - failure callback (unused)\n    - func - function to call\n    - ... - function arguments\n- `p6_run_script(cmd_env, shell, set_flags, cmd, [exts=.sh], arg_list, ...)`\n  - Synopsis: Resolve and run a script with a clean environment.\n  - Args:\n    - cmd_env - env var assignments\n    - shell - shell to execute\n    - set_flags - shell flags\n    - cmd - script base name\n    - OPTIONAL exts - allowed extensions [.sh]\n    - arg_list - argument list string\n    - ... - search paths\n- `p6_run_serial(things, cmd, ...)`\n  - Synopsis: Run a command serially over a list of items.\n  - Args:\n    - things - item list\n    - cmd - command to run\n    - ... - command arguments\n- `p6_run_write_cmd(cmd)`\n  - Synopsis: Run a command string (write-style helper).\n  - Args:\n    - cmd - command string\n- `p6_run_yield(func, ...)`\n  - Synopsis: Invoke a function with arguments and return its status.\n  - Args:\n    - func - function to call\n    - ... - function arguments\n- `true  = p6_run_if_not_in(script, skip_list)`\n  - Synopsis: Return true when a script is found in the skip list.\n  - Args:\n    - script - script name\n    - skip_list - whitespace-separated list\n\n##### p6common/lib/stdlib/template.sh\n\n- `str processed = p6_template_process(infile, ...)`\n  - Synopsis: Apply key/value replacements to a template file.\n  - Args:\n    - infile - template file path\n    - ... - k=v replacements\n\n##### p6common/lib/stdlib/transients.sh\n\n- `p6_transient_delete(dir, [handler_name=])`\n  - Synopsis: Deletes a transient directory unless it is persisted or under cleanup.\n  - Args:\n    - dir - transient directory path\n    - OPTIONAL handler_name - cleanup handler name []\n- `p6_transient_is(dir)`\n  - Synopsis: Returns true when dir exists and is managed as a transient.\n  - Args:\n    - dir - transient directory path\n- `p6_transient_persist(dir)`\n  - Synopsis: Marks a transient directory as persisted.\n  - Args:\n    - dir - transient directory path\n- `p6_transient_persist_is(dir)`\n  - Synopsis: Returns true when a transient directory is marked persisted.\n  - Args:\n    - dir - transient directory path\n- `p6_transient_persist_un(dir)`\n  - Synopsis: Removes the persistence marker from a transient directory.\n  - Args:\n    - dir - transient directory path\n- `path file = p6_transient_create_file(file_name)`\n  - Synopsis: Creates a transient directory and returns a file path inside it.\n  - Args:\n    - file_name - transient file base name\n- `str  = p6_transient_create(dir_name, [len=4])`\n  - Synopsis: Creates a transient directory under P6_DIR_TRANSIENTS and returns its path.\n  - Args:\n    - dir_name - transient dir name\n    - OPTIONAL len - random suffix length [4]\n\n##### p6common/lib/stdlib/unroll.sh\n\n- `p6_unroll_function(function)`\n  - Synopsis: Emits the shell source of a function, optionally filtered for unrolling.\n  - Args:\n    - function - function name\n- `p6_unroll_functions()`\n  - Synopsis: Writes unrolled function definitions into fpath/ for each p6 function.\n- `str s/p6_return_str/echo/g = p6_unroll_filter()`\n  - Synopsis: Rewrites p6 return helpers into plain shell equivalents for filters.\n\n#### string\n\n##### p6common/lib/string/json.sh\n\n- `p6_json_eval(json, ...)`\n  - Synopsis: Run jq against a JSON string.\n  - Args:\n    - json - JSON input\n    - ... - jq filter and options\n- `p6_json_from_file(file)`\n  - Synopsis: Output JSON content from a file.\n  - Args:\n    - file - JSON file path\n\n##### p6common/lib/string/string.sh\n\n- `bool rc = p6_string_eq_0(str)`\n  - Synopsis: Return true when the string equals 0.\n  - Args:\n    - str - string to test\n- `bool rc = p6_string_eq_1(str)`\n  - Synopsis: Return true when the string equals 1.\n  - Args:\n    - str - string to test\n- `bool rc = p6_string_eq_neg_1(str)`\n  - Synopsis: Return true when the string equals -1.\n  - Args:\n    - str - string to test\n- `bool rc = p6_string_ne_0(str)`\n  - Synopsis: Return true when the string does not equal 0.\n  - Args:\n    - str - string to test\n- `bool rv = p6_string_blank(str)`\n  - Synopsis: Return true when the string is empty.\n  - Args:\n    - str - string to test\n- `bool rv = p6_string_blank_NOT(str)`\n  - Synopsis: Return true when the string is not empty.\n  - Args:\n    - str - string to test\n- `bool rv = p6_string_contains(str, needle)`\n  - Synopsis: Return true when the string contains the needle.\n  - Args:\n    - str - haystack string\n    - needle - substring to find\n- `bool rv = p6_string_ends_with(str, suffix)`\n  - Synopsis: Return true when the string ends with the given suffix.\n  - Args:\n    - str - input string\n    - suffix - suffix to match\n- `bool rv = p6_string_eq(str, val)`\n  - Synopsis: Return true when two strings are equal.\n  - Args:\n    - str - left-hand string\n    - val - right-hand string\n- `bool rv = p6_string_eq_any(str, ...)`\n  - Synopsis: Return true when the string matches any of the provided values.\n  - Args:\n    - str - string to test\n    - ... - remaining values to compare\n- `bool rv = p6_string_match_regex(str, re)`\n  - Synopsis: Return true when the string matches the regex pattern.\n  - Args:\n    - str - input string\n    - re - extended regex\n- `bool rv = p6_string_ne(str, val)`\n  - Synopsis: Return true when two strings are different.\n  - Args:\n    - str - left-hand string\n    - val - right-hand string\n- `bool rv = p6_string_starts_with(str, prefix)`\n  - Synopsis: Return true when the string starts with the given prefix.\n  - Args:\n    - str - input string\n    - prefix - prefix to match\n- `size_t len = p6_string_len(str)`\n  - Synopsis: Return the length of the string in characters.\n  - Args:\n    - str - input string\n- `str out = p6_string_default(str, default)`\n  - Synopsis: Return the default when the string is blank.\n  - Args:\n    - str - input string\n    - default - fallback value\n- `str padded = p6_string_zero_pad(str, pad)`\n  - Synopsis: Zero-pad a number to a fixed width.\n  - Args:\n    - str - numeric string\n    - pad - width to pad to\n- `str str_a = p6_string_append(str, add, [sep= ])`\n  - Synopsis: Append a value to a string with a separator.\n  - Args:\n    - str - base string\n    - add - string to append\n    - OPTIONAL sep - separator between parts [ ]\n- `str str_a = p6_string_prepend(str, add, [sep= ])`\n  - Synopsis: Prepend a value to a string with a separator.\n  - Args:\n    - str - base string\n    - add - string to prepend\n    - OPTIONAL sep - separator between parts [ ]\n- `str str_ic = p6_string_init_cap(str)`\n  - Synopsis: Capitalize the first letter of each word.\n  - Args:\n    - str - input string\n- `str str_lc = p6_string_lc(str)`\n  - Synopsis: Lowercase a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_collapse_double_slash(str)`\n  - Synopsis: Collapse repeated slashes to single slashes.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_nodeenv_to_nodenv(str)`\n  - Synopsis: Rewrite nodeenv to nodenv within a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_replace(str, from, to, [flags=g])`\n  - Synopsis: Replace pattern matches with a replacement string.\n  - Args:\n    - str - input string\n    - from - sed pattern to replace\n    - to - replacement string\n    - OPTIONAL flags - sed flags (default: global) [g]\n- `str str_r = p6_string_sanitize_dot_id(str)`\n  - Synopsis: Normalize a dotted identifier by replacing :/.- with underscores.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_sanitize_identifier(str)`\n  - Synopsis: Convert non-identifier characters to underscores.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_slash_to_underscore(str)`\n  - Synopsis: Replace slashes with underscores.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_alum(str)`\n  - Synopsis: Remove all alphanumeric characters from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_alum_and_underscore(str)`\n  - Synopsis: Remove alphanumerics and underscores from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_brackets(str)`\n  - Synopsis: Remove literal square brackets from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_chars(str, chars)`\n  - Synopsis: Remove all occurrences of the given character set.\n  - Args:\n    - str - input string\n    - chars - character class to remove\n- `str str_r = p6_string_strip_double_quote(str)`\n  - Synopsis: Remove double quotes from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_leading_and_trailing_spaces(str)`\n  - Synopsis: Strip both leading and trailing spaces from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_leading_spaces(str)`\n  - Synopsis: Strip leading spaces from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_non_branch_chars(str)`\n  - Synopsis: Remove characters not allowed in branch identifiers.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_prefix(str, prefix)`\n  - Synopsis: Remove a prefix when present.\n  - Args:\n    - str - input string\n    - prefix - prefix to remove\n- `str str_r = p6_string_strip_quotes(str)`\n  - Synopsis: Remove both single and double quotes from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_single_quote(str)`\n  - Synopsis: Remove single quotes from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_spaces(str)`\n  - Synopsis: Remove all spaces from a string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_suffix(str, suffix)`\n  - Synopsis: Remove a suffix when present.\n  - Args:\n    - str - input string\n    - suffix - suffix to remove\n- `str str_r = p6_string_strip_trailing_slash(str)`\n  - Synopsis: Strip a trailing slash from a path-like string.\n  - Args:\n    - str - input string\n- `str str_r = p6_string_strip_trailing_spaces(str)`\n  - Synopsis: Strip trailing spaces from a string.\n  - Args:\n    - str - input string\n- `str str_uc = p6_string_uc(str)`\n  - Synopsis: Uppercase a string.\n  - Args:\n    - str - input string\n\n##### p6common/lib/string/tokens.sh\n\n- `p6_token_encode_base64(string)`\n  - Synopsis: Base64-encode the provided string.\n  - Args:\n    - string - input string\n- `p6_token_sha256(string)`\n  - Synopsis: Print the SHA-256 hash of the provided string.\n  - Args:\n    - string - input string\n- `str hashed = p6_token_hash(string)`\n  - Synopsis: Compute an MD5 hash of the provided string.\n  - Args:\n    - string - input string\n- `str pass = p6_token_passwd(len)`\n  - Synopsis: Generate a random password of the given length.\n  - Args:\n    - len - password length\n- `str token = p6_token_random(len)`\n  - Synopsis: Generate a random alphanumeric token of the given length.\n  - Args:\n    - len - token length\n\n##### p6common/lib/string/uri.sh\n\n- `path name = p6_uri_name(uri)`\n  - Synopsis: Return the basename component of a URI or path.\n  - Args:\n    - uri - input URI/path\n- `path name = p6_uri_path(uri)`\n  - Synopsis: Return the directory component of a URI or path.\n  - Args:\n    - uri - input URI/path\n\n##### p6common/lib/string/word.sh\n\n- `true  = p6_word_in(word, ..., words)`\n  - Synopsis: Return true when a word is found in the list.\n  - Args:\n    - word - word to find\n    - ... - remaining words\n    - words - word list\n- `words result = p6_word_comm_extra(a, b)`\n  - Args:\n    - a - word list A\n    - b - word list B\n- `words result = p6_word_comm_missing(a, b)`\n  - Args:\n    - a - word list A\n    - b - word list B\n- `words result = p6_word_comm_same(a, b)`\n  - Args:\n    - a - word list A\n    - b - word list B\n- `words result = p6_word_not(a, b)`\n  - Synopsis: Return words that appear in A but not in B.\n  - Args:\n    - a - word list A\n    - b - word list B\n- `words words = p6_word_unique(...)`\n  - Synopsis: Return unique words from the provided arguments.\n  - Args:\n    - ... - words to uniquify\n\n##### p6common/lib/string/yml.sh\n\n- `p6_yml_eval(yml, ...)`\n  - Synopsis: Evaluates a yq query against a YAML string.\n  - Args:\n    - yml - YAML content\n    - ... - yq options and query\n- `p6_yml_update_in_file(file, query)`\n  - Args:\n    - file - YAML file path\n    - query - yq query\n- `stream  = p6_yml_from_file(file, query)`\n  - Synopsis: Reads YAML from file and optionally applies a yq query.\n  - Args:\n    - file - YAML file path\n    - query - yq query string (optional)\n\n## Hierarchy\n\n```text\n.\n├── bin\n│   └── p6ctl\n├── conf\n│   ├── debug\n│   │   ├── debug-debug.sh\n│   │   ├── log-debug.sh\n│   │   ├── return.sh\n│   │   ├── time-debug.sh\n│   │   └── trace-debug.sh\n│   └── prod\n│       ├── debug-prod.sh\n│       ├── log-prod.sh\n│       ├── return.sh\n│       ├── time-prod.sh\n│       └── trace-debug.sh\n├── Dockerfile\n├── init.zsh\n├── lib\n│   ├── _bootstrap.sh\n│   ├── cicd\n│   │   ├── build.sh\n│   │   ├── deploy.sh\n│   │   ├── doc.sh\n│   │   ├── release.sh\n│   │   └── test.sh\n│   ├── date\n│   │   ├── convert.sh\n│   │   ├── debug.sh\n│   │   ├── fmt.sh\n│   │   ├── math.sh\n│   │   ├── point.sh\n│   │   └── range.sh\n│   ├── filter\n│   │   ├── aggregate.sh\n│   │   ├── column.sh\n│   │   ├── date.sh\n│   │   ├── debug.sh\n│   │   ├── escape.sh\n│   │   ├── extract.sh\n│   │   ├── kv.sh\n│   │   ├── row.sh\n│   │   ├── sort.sh\n│   │   ├── string.sh\n│   │   ├── strip.sh\n│   │   └── translate.sh\n│   ├── math\n│   │   ├── arithmetic.sh\n│   │   └── range.sh\n│   ├── network\n│   │   ├── download.sh\n│   │   ├── network.sh\n│   │   └── ssh.sh\n│   ├── openssl\n│   │   ├── ciphers.sh\n│   │   ├── debug.sh\n│   │   ├── req.sh\n│   │   ├── s_client.sh\n│   │   ├── s_server.sh\n│   │   ├── util.sh\n│   │   ├── verify.sh\n│   │   ├── version.sh\n│   │   └── x509.sh\n│   ├── prod -\u003e ../conf/prod\n│   ├── stdio\n│   │   ├── color.sh\n│   │   ├── debug.sh\n│   │   ├── dir.sh\n│   │   ├── file.sh\n│   │   ├── interactive.sh\n│   │   ├── io.sh\n│   │   └── verbose.sh\n│   ├── stdlib\n│   │   ├── alias.sh\n│   │   ├── const.sh\n│   │   ├── ctl.sh\n│   │   ├── diag.sh\n│   │   ├── dryrunning.sh\n│   │   ├── edit.sh\n│   │   ├── env.sh\n│   │   ├── lang.sh\n│   │   ├── misc.sh\n│   │   ├── os.sh\n│   │   ├── path.sh\n│   │   ├── retry.sh\n│   │   ├── run.sh\n│   │   ├── template.sh\n│   │   ├── transients.sh\n│   │   └── unroll.sh\n│   ├── string\n│   │   ├── json.sh\n│   │   ├── string.sh\n│   │   ├── tokens.sh\n│   │   ├── uri.sh\n│   │   ├── word.sh\n│   │   └── yml.sh\n│   └── test\n│       ├── api.sh\n│       ├── asserts.sh\n│       ├── harness.sh\n│       ├── run.sh\n│       ├── state.sh\n│       └── tap.sh\n├── README.md\n└── t\n    ├── alias.sh\n    ├── cicd.sh\n    ├── color.sh\n    ├── const.sh\n    ├── ctl.sh\n    ├── date.sh\n    ├── debug.sh\n    ├── diag.sh\n    ├── dir.sh\n    ├── dryrunning.sh\n    ├── edit.sh\n    ├── env.sh\n    ├── file.sh\n    ├── filter.sh\n    ├── inc.sh\n    ├── interactive.sh\n    ├── io.sh\n    ├── json.sh\n    ├── lang.sh\n    ├── math.sh\n    ├── misc.sh\n    ├── openssl.sh\n    ├── os.sh\n    ├── path.sh\n    ├── remote.sh\n    ├── retry.sh\n    ├── return.sh\n    ├── run.sh\n    ├── ssh.sh\n    ├── string.sh\n    ├── template.sh\n    ├── tokens.sh\n    ├── transients.sh\n    ├── unroll.sh\n    ├── uri.sh\n    ├── verbose.sh\n    ├── word.sh\n    └── yml.sh\n\n18 directories, 125 files\n```\n\n## Author\n\nPhilip M. Gollucci \u003cpgollucci@p6m7g8.com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp6m7g8-dotfiles%2Fp6common","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fp6m7g8-dotfiles%2Fp6common","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fp6m7g8-dotfiles%2Fp6common/lists"}