@hybridlabor-api/aos 4.8.0 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents.md +1 -1
- package/.agents/nodes.json +1 -1
- package/.agents/vendor-manifest.json +23 -1
- package/.claude/agents/godmode-media-eventtech.md +1 -1
- package/.claude/hooks/conventional-commits.mjs +125 -0
- package/.claude/hooks/env-file-protection.mjs +105 -0
- package/.claude/hooks/go-gate.mjs +101 -81
- package/.claude/settings.json +13 -0
- package/.opencode/agents/godmode-media-eventtech.md +1 -1
- package/.opencode/plugins/bdb-aos.js +31 -4
- package/README.de.md +1 -1
- package/README.md +1 -1
- package/README.pt.md +1 -1
- package/THIRD_PARTY_NOTICES.md +88 -0
- package/docs/skills_table.md +1 -1
- package/installer.js +174 -46
- package/package.json +6 -2
- package/packages/aos-cli/README.md +80 -0
- package/packages/aos-cli/bin/aos-cli.mjs +134 -0
- package/packages/aos-cli/core-skills.json +12 -0
- package/packages/aos-cli/extensions/aos.ts +321 -0
- package/packages/aos-cli/package-lock.json +1923 -0
- package/packages/aos-cli/package.json +29 -0
- package/packages/aos-cli/scripts/check-theme.mjs +63 -0
- package/packages/aos-cli/themes/aos.json +97 -0
- package/scripts/build-plugin-manifest.mjs +131 -0
- package/scripts/validate-skills.mjs +81 -6
- package/skills/basic/ao-orchestrator/SKILL.md +116 -0
- package/skills/global_config/agenttrail/SKILL.md +6 -1
- package/skills/global_config/ask-tim/SKILL.md +4 -4
- package/skills/global_config/bash-script-generator/SKILL.md +201 -0
- package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
- package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
- package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
- package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
- package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
- package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
- package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
- package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
- package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
- package/skills/global_config/bash-script-validator/SKILL.md +249 -0
- package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
- package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
- package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
- package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
- package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
- package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
- package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
- package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
- package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
- package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
- package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
- package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
- package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
- package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
- package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
- package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
- package/skills/global_config/ci-pipeline/SKILL.md +135 -0
- package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
- package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
- package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
- package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
- package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
- package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
- package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
- package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
- package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
- package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
- package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
- package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
- package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
- package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
- package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
- package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
- package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
- package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
- package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
- package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
- package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
- package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
- package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
- package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
- package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
- package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
- package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
- package/skills/global_config/github-actions-generator/SKILL.md +353 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
- package/skills/global_config/github-actions-generator/examples/README.md +147 -0
- package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
- package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
- package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
- package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
- package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
- package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
- package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
- package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
- package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
- package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
- package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
- package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
- package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
- package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
- package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
- package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
- package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
- package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
- package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
- package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
- package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
- package/skills/global_config/github-actions-templates/SKILL.md +7 -0
- package/skills/global_config/github-actions-validator/SKILL.md +576 -0
- package/skills/global_config/github-actions-validator/examples/README.md +88 -0
- package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
- package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
- package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
- package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
- package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
- package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
- package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
- package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
- package/skills/global_config/github-actions-validator/references/runners.md +317 -0
- package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
- package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
- package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
- package/skills/global_config/makefile-generator/SKILL.md +614 -0
- package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
- package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
- package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
- package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
- package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
- package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
- package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
- package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
- package/skills/global_config/makefile-validator/SKILL.md +244 -0
- package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
- package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
- package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
- package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
- package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
- package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
- package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
- package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
- package/skills/global_config/requesting-code-review/SKILL.md +98 -0
- package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
- package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
- package/skills/global_config/verification-before-completion/SKILL.md +123 -0
- package/skills/global_config/writing-plans/SKILL.md +126 -46
- package/skills/global_config/writing-plans-legacy/SKILL.md +139 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# Bash Script Patterns
|
|
2
|
+
|
|
3
|
+
Common patterns and templates for bash script generation.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Argument Parsing Patterns](#argument-parsing-patterns)
|
|
8
|
+
2. [Configuration File Handling](#configuration-file-handling)
|
|
9
|
+
3. [Logging Frameworks](#logging-frameworks)
|
|
10
|
+
4. [Parallel Processing](#parallel-processing)
|
|
11
|
+
5. [Lock Files](#lock-files)
|
|
12
|
+
6. [Signal Handling](#signal-handling)
|
|
13
|
+
7. [Retry Logic](#retry-logic)
|
|
14
|
+
|
|
15
|
+
## Argument Parsing Patterns
|
|
16
|
+
|
|
17
|
+
### Simple getopts Pattern
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
#!/usr/bin/env bash
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
usage() {
|
|
24
|
+
cat << EOF
|
|
25
|
+
Usage: ${0##*/} [OPTIONS] FILE
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
-h Show this help
|
|
29
|
+
-v Verbose output
|
|
30
|
+
-f FILE Input file
|
|
31
|
+
-o FILE Output file
|
|
32
|
+
EOF
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
main() {
|
|
36
|
+
local verbose=false
|
|
37
|
+
local input_file=""
|
|
38
|
+
local output_file=""
|
|
39
|
+
|
|
40
|
+
while getopts ":hvf:o:" opt; do
|
|
41
|
+
case ${opt} in
|
|
42
|
+
h) usage; exit 0 ;;
|
|
43
|
+
v) verbose=true ;;
|
|
44
|
+
f) input_file="${OPTARG}" ;;
|
|
45
|
+
o) output_file="${OPTARG}" ;;
|
|
46
|
+
:) echo "Option -${OPTARG} requires an argument" >&2; exit 1 ;;
|
|
47
|
+
\?) echo "Invalid option: -${OPTARG}" >&2; exit 1 ;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
shift $((OPTIND - 1))
|
|
51
|
+
|
|
52
|
+
# Validation
|
|
53
|
+
[[ -n "${input_file}" ]] || { echo "Error: -f required" >&2; exit 1; }
|
|
54
|
+
|
|
55
|
+
# Process
|
|
56
|
+
echo "Processing ${input_file}..."
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main "$@"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Long Options Pattern
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Parse both short and long options
|
|
66
|
+
parse_args() {
|
|
67
|
+
while [[ $# -gt 0 ]]; do
|
|
68
|
+
case "$1" in
|
|
69
|
+
-h|--help)
|
|
70
|
+
usage
|
|
71
|
+
exit 0
|
|
72
|
+
;;
|
|
73
|
+
-v|--verbose)
|
|
74
|
+
VERBOSE=true
|
|
75
|
+
shift
|
|
76
|
+
;;
|
|
77
|
+
-f|--file)
|
|
78
|
+
INPUT_FILE="$2"
|
|
79
|
+
shift 2
|
|
80
|
+
;;
|
|
81
|
+
-o|--output)
|
|
82
|
+
OUTPUT_FILE="$2"
|
|
83
|
+
shift 2
|
|
84
|
+
;;
|
|
85
|
+
--)
|
|
86
|
+
shift
|
|
87
|
+
break
|
|
88
|
+
;;
|
|
89
|
+
-*)
|
|
90
|
+
echo "Unknown option: $1" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
;;
|
|
93
|
+
*)
|
|
94
|
+
break
|
|
95
|
+
;;
|
|
96
|
+
esac
|
|
97
|
+
done
|
|
98
|
+
|
|
99
|
+
# Remaining arguments
|
|
100
|
+
REMAINING_ARGS=("$@")
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Subcommand Pattern
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
#!/usr/bin/env bash
|
|
108
|
+
set -euo pipefail
|
|
109
|
+
|
|
110
|
+
cmd_start() {
|
|
111
|
+
echo "Starting service..."
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
cmd_stop() {
|
|
115
|
+
echo "Stopping service..."
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
cmd_status() {
|
|
119
|
+
echo "Checking status..."
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
usage() {
|
|
123
|
+
cat << EOF
|
|
124
|
+
Usage: ${0##*/} COMMAND [OPTIONS]
|
|
125
|
+
|
|
126
|
+
Commands:
|
|
127
|
+
start Start the service
|
|
128
|
+
stop Stop the service
|
|
129
|
+
status Check service status
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
-h, --help Show this help
|
|
133
|
+
EOF
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main() {
|
|
137
|
+
[[ $# -lt 1 ]] && { usage; exit 1; }
|
|
138
|
+
|
|
139
|
+
local command="$1"
|
|
140
|
+
shift
|
|
141
|
+
|
|
142
|
+
case "${command}" in
|
|
143
|
+
start) cmd_start "$@" ;;
|
|
144
|
+
stop) cmd_stop "$@" ;;
|
|
145
|
+
status) cmd_status "$@" ;;
|
|
146
|
+
-h|--help) usage; exit 0 ;;
|
|
147
|
+
*) echo "Unknown command: ${command}" >&2; usage; exit 1 ;;
|
|
148
|
+
esac
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
main "$@"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Configuration File Handling
|
|
155
|
+
|
|
156
|
+
### Source-based Configuration
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# config.conf file
|
|
160
|
+
CONFIG_VALUE="something"
|
|
161
|
+
MAX_RETRIES=3
|
|
162
|
+
API_URL="https://api.example.com"
|
|
163
|
+
|
|
164
|
+
# In script
|
|
165
|
+
load_config() {
|
|
166
|
+
local config_file="${1:-config.conf}"
|
|
167
|
+
|
|
168
|
+
if [[ -f "${config_file}" ]]; then
|
|
169
|
+
# shellcheck source=/dev/null
|
|
170
|
+
source "${config_file}"
|
|
171
|
+
else
|
|
172
|
+
echo "Warning: Config file not found: ${config_file}" >&2
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
load_config "/etc/myapp/config.conf"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Key-Value Configuration Parser
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# config.conf format:
|
|
183
|
+
# key=value
|
|
184
|
+
# # comments
|
|
185
|
+
|
|
186
|
+
load_config() {
|
|
187
|
+
local config_file="$1"
|
|
188
|
+
|
|
189
|
+
while IFS='=' read -r key value; do
|
|
190
|
+
# Skip empty lines and comments
|
|
191
|
+
[[ -z "${key}" || "${key}" =~ ^[[:space:]]*# ]] && continue
|
|
192
|
+
|
|
193
|
+
# Trim whitespace
|
|
194
|
+
key=$(echo "${key}" | tr -d '[:space:]')
|
|
195
|
+
value=$(echo "${value}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
196
|
+
|
|
197
|
+
# Export as variable
|
|
198
|
+
declare -g "${key}=${value}"
|
|
199
|
+
done < "${config_file}"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### INI-style Configuration Parser
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Parse INI format [section] key=value
|
|
207
|
+
parse_ini() {
|
|
208
|
+
local file="$1"
|
|
209
|
+
local section=""
|
|
210
|
+
|
|
211
|
+
while IFS= read -r line; do
|
|
212
|
+
# Skip empty lines and comments
|
|
213
|
+
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*[#;] ]] && continue
|
|
214
|
+
|
|
215
|
+
# Section header
|
|
216
|
+
if [[ "${line}" =~ ^\[([^]]+)\] ]]; then
|
|
217
|
+
section="${BASH_REMATCH[1]}"
|
|
218
|
+
continue
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
# Key=value
|
|
222
|
+
if [[ "${line}" =~ ^([^=]+)=(.*)$ ]]; then
|
|
223
|
+
local key="${BASH_REMATCH[1]}"
|
|
224
|
+
local value="${BASH_REMATCH[2]}"
|
|
225
|
+
key=$(echo "${key}" | tr -d '[:space:]')
|
|
226
|
+
value=$(echo "${value}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
227
|
+
|
|
228
|
+
# Store in associative array
|
|
229
|
+
config["${section}.${key}"]="${value}"
|
|
230
|
+
fi
|
|
231
|
+
done < "${file}"
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Logging Frameworks
|
|
236
|
+
|
|
237
|
+
### Simple Logging with Levels
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# LOG_LEVEL: 0=DEBUG, 1=INFO (default), 2=WARN, 3=ERROR
|
|
241
|
+
LOG_LEVEL=${LOG_LEVEL:-1}
|
|
242
|
+
|
|
243
|
+
# Use if-form guards — the && short-circuit form returns 1 when the
|
|
244
|
+
# level check fails, which triggers set -e at the call site.
|
|
245
|
+
log_debug() { if [[ ${LOG_LEVEL} -le 0 ]]; then echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; fi; }
|
|
246
|
+
log_info() { if [[ ${LOG_LEVEL} -le 1 ]]; then echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; fi; }
|
|
247
|
+
log_warn() { if [[ ${LOG_LEVEL} -le 2 ]]; then echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; fi; }
|
|
248
|
+
log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### File-based Logging
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
readonly LOG_FILE="${LOG_FILE:-/var/log/myscript.log}"
|
|
255
|
+
|
|
256
|
+
log_to_file() {
|
|
257
|
+
local level="$1"
|
|
258
|
+
shift
|
|
259
|
+
echo "[${level}] $(date '+%Y-%m-%d %H:%M:%S') $*" >> "${LOG_FILE}"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
log_info() {
|
|
263
|
+
local msg="$*"
|
|
264
|
+
echo "[INFO] ${msg}" >&2
|
|
265
|
+
log_to_file "INFO" "${msg}"
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Structured JSON Logging
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
log_json() {
|
|
273
|
+
local level="$1"
|
|
274
|
+
local message="$2"
|
|
275
|
+
local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
|
276
|
+
|
|
277
|
+
cat <<EOF
|
|
278
|
+
{"timestamp":"${timestamp}","level":"${level}","message":"${message}","script":"${SCRIPT_NAME}"}
|
|
279
|
+
EOF
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
log_info() {
|
|
283
|
+
log_json "INFO" "$*" >&2
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Parallel Processing
|
|
288
|
+
|
|
289
|
+
### Using xargs for Parallel Execution
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Process files in parallel
|
|
293
|
+
find . -name "*.txt" -print0 | xargs -0 -P 4 -I {} process_file {}
|
|
294
|
+
|
|
295
|
+
# With function export
|
|
296
|
+
process_file() {
|
|
297
|
+
echo "Processing $1..."
|
|
298
|
+
# ... processing logic
|
|
299
|
+
}
|
|
300
|
+
export -f process_file
|
|
301
|
+
|
|
302
|
+
find . -name "*.txt" | xargs -P 4 -I {} bash -c 'process_file "$@"' _ {}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Using GNU Parallel
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Requires: apt-get install parallel
|
|
309
|
+
|
|
310
|
+
# Simple parallel execution
|
|
311
|
+
parallel process_file ::: file1.txt file2.txt file3.txt
|
|
312
|
+
|
|
313
|
+
# From file list
|
|
314
|
+
cat files.txt | parallel process_file
|
|
315
|
+
|
|
316
|
+
# With progress bar
|
|
317
|
+
parallel --bar process_file ::: *.txt
|
|
318
|
+
|
|
319
|
+
# Control number of jobs
|
|
320
|
+
parallel -j 4 process_file ::: *.txt
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Background Jobs Pattern
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Track background jobs
|
|
327
|
+
pids=()
|
|
328
|
+
|
|
329
|
+
# Start jobs
|
|
330
|
+
for file in *.txt; do
|
|
331
|
+
process_file "${file}" &
|
|
332
|
+
pids+=($!)
|
|
333
|
+
done
|
|
334
|
+
|
|
335
|
+
# Wait for all jobs
|
|
336
|
+
for pid in "${pids[@]}"; do
|
|
337
|
+
if wait "${pid}"; then
|
|
338
|
+
echo "Job ${pid} completed successfully"
|
|
339
|
+
else
|
|
340
|
+
echo "Job ${pid} failed" >&2
|
|
341
|
+
fi
|
|
342
|
+
done
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Lock Files
|
|
346
|
+
|
|
347
|
+
### Simple Lock File
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
readonly LOCK_FILE="/var/lock/myscript.lock"
|
|
351
|
+
|
|
352
|
+
acquire_lock() {
|
|
353
|
+
if [[ -f "${LOCK_FILE}" ]]; then
|
|
354
|
+
echo "Another instance is running (lock file exists)" >&2
|
|
355
|
+
exit 1
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
echo $$ > "${LOCK_FILE}"
|
|
359
|
+
trap 'rm -f "${LOCK_FILE}"' EXIT
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
acquire_lock
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### PID-based Lock with Stale Lock Detection
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
acquire_lock() {
|
|
369
|
+
local lock_file="/var/lock/myscript.lock"
|
|
370
|
+
|
|
371
|
+
if [[ -f "${lock_file}" ]]; then
|
|
372
|
+
local old_pid=$(cat "${lock_file}")
|
|
373
|
+
|
|
374
|
+
# Check if process is still running
|
|
375
|
+
if kill -0 "${old_pid}" 2>/dev/null; then
|
|
376
|
+
echo "Another instance (PID ${old_pid}) is running" >&2
|
|
377
|
+
return 1
|
|
378
|
+
else
|
|
379
|
+
echo "Removing stale lock file" >&2
|
|
380
|
+
rm -f "${lock_file}"
|
|
381
|
+
fi
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
echo $$ > "${lock_file}"
|
|
385
|
+
trap 'rm -f "${lock_file}"' EXIT
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Using flock for Atomic Locking
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# Requires flock command
|
|
393
|
+
|
|
394
|
+
exec 200>/var/lock/myscript.lock
|
|
395
|
+
flock -n 200 || { echo "Another instance is running" >&2; exit 1; }
|
|
396
|
+
|
|
397
|
+
# Script runs exclusively
|
|
398
|
+
# Lock is released when script exits
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Signal Handling
|
|
402
|
+
|
|
403
|
+
### Cleanup on Exit
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
cleanup() {
|
|
407
|
+
local exit_code=$?
|
|
408
|
+
echo "Cleaning up..." >&2
|
|
409
|
+
|
|
410
|
+
# Remove temp files
|
|
411
|
+
[[ -n "${temp_dir:-}" ]] && rm -rf "${temp_dir}"
|
|
412
|
+
|
|
413
|
+
# Release locks
|
|
414
|
+
[[ -f "${lock_file:-}" ]] && rm -f "${lock_file}"
|
|
415
|
+
|
|
416
|
+
exit "${exit_code}"
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
trap cleanup EXIT
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Handling Multiple Signals
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
handle_sigint() {
|
|
426
|
+
echo "Received SIGINT, cleaning up..." >&2
|
|
427
|
+
cleanup
|
|
428
|
+
exit 130 # Standard exit code for SIGINT
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
handle_sigterm() {
|
|
432
|
+
echo "Received SIGTERM, cleaning up..." >&2
|
|
433
|
+
cleanup
|
|
434
|
+
exit 143 # Standard exit code for SIGTERM
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
trap handle_sigint INT
|
|
438
|
+
trap handle_sigterm TERM
|
|
439
|
+
trap cleanup EXIT ERR
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Graceful Shutdown
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
SHUTDOWN=false
|
|
446
|
+
|
|
447
|
+
handle_signal() {
|
|
448
|
+
echo "Shutdown signal received, finishing current work..." >&2
|
|
449
|
+
SHUTDOWN=true
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
trap handle_signal INT TERM
|
|
453
|
+
|
|
454
|
+
# Main processing loop
|
|
455
|
+
while [[ "${SHUTDOWN}" == "false" ]]; do
|
|
456
|
+
process_next_item || break
|
|
457
|
+
done
|
|
458
|
+
|
|
459
|
+
echo "Graceful shutdown complete" >&2
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Retry Logic
|
|
463
|
+
|
|
464
|
+
### Simple Retry with Backoff
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
retry() {
|
|
468
|
+
local max_attempts=3
|
|
469
|
+
local delay=1
|
|
470
|
+
local attempt=1
|
|
471
|
+
|
|
472
|
+
while [[ ${attempt} -le ${max_attempts} ]]; do
|
|
473
|
+
if "$@"; then
|
|
474
|
+
return 0
|
|
475
|
+
else
|
|
476
|
+
echo "Attempt ${attempt} failed, retrying in ${delay}s..." >&2
|
|
477
|
+
sleep "${delay}"
|
|
478
|
+
((attempt++))
|
|
479
|
+
((delay*=2)) # Exponential backoff
|
|
480
|
+
fi
|
|
481
|
+
done
|
|
482
|
+
|
|
483
|
+
echo "All ${max_attempts} attempts failed" >&2
|
|
484
|
+
return 1
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
# Usage
|
|
488
|
+
retry curl -f https://api.example.com/data
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Advanced Retry with Custom Parameters
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
retry_with_backoff() {
|
|
495
|
+
local max_attempts="${1}"
|
|
496
|
+
local delay="${2}"
|
|
497
|
+
local max_delay="${3:-60}"
|
|
498
|
+
shift 3
|
|
499
|
+
local attempt=1
|
|
500
|
+
|
|
501
|
+
while [[ ${attempt} -le ${max_attempts} ]]; do
|
|
502
|
+
if "$@"; then
|
|
503
|
+
return 0
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
if [[ ${attempt} -lt ${max_attempts} ]]; then
|
|
507
|
+
echo "Attempt ${attempt}/${max_attempts} failed" >&2
|
|
508
|
+
echo "Retrying in ${delay}s..." >&2
|
|
509
|
+
sleep "${delay}"
|
|
510
|
+
|
|
511
|
+
# Exponential backoff with max cap
|
|
512
|
+
delay=$((delay * 2))
|
|
513
|
+
[[ ${delay} -gt ${max_delay} ]] && delay=${max_delay}
|
|
514
|
+
fi
|
|
515
|
+
|
|
516
|
+
((attempt++))
|
|
517
|
+
done
|
|
518
|
+
|
|
519
|
+
echo "All ${max_attempts} attempts failed" >&2
|
|
520
|
+
return 1
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
# Usage: retry_with_backoff MAX_ATTEMPTS INITIAL_DELAY MAX_DELAY command args...
|
|
524
|
+
retry_with_backoff 5 1 30 curl -f https://api.example.com/data
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Retry with Jitter
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
retry_with_jitter() {
|
|
531
|
+
local max_attempts="$1"
|
|
532
|
+
local base_delay="$2"
|
|
533
|
+
shift 2
|
|
534
|
+
local attempt=1
|
|
535
|
+
|
|
536
|
+
while [[ ${attempt} -le ${max_attempts} ]]; do
|
|
537
|
+
if "$@"; then
|
|
538
|
+
return 0
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
if [[ ${attempt} -lt ${max_attempts} ]]; then
|
|
542
|
+
# Add random jitter (0-100% of delay)
|
|
543
|
+
local jitter=$((RANDOM % base_delay))
|
|
544
|
+
local delay=$((base_delay + jitter))
|
|
545
|
+
|
|
546
|
+
echo "Attempt ${attempt} failed, retrying in ${delay}s..." >&2
|
|
547
|
+
sleep "${delay}"
|
|
548
|
+
|
|
549
|
+
# Exponential backoff
|
|
550
|
+
((base_delay*=2))
|
|
551
|
+
fi
|
|
552
|
+
|
|
553
|
+
((attempt++))
|
|
554
|
+
done
|
|
555
|
+
|
|
556
|
+
return 1
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## References
|
|
563
|
+
|
|
564
|
+
- [Advanced Bash-Scripting Guide](https://tldp.org/LDP/abs/html/)
|
|
565
|
+
- [Bash Hackers Wiki](https://wiki.bash-hackers.org/)
|
|
566
|
+
- [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)
|