@hybridlabor-api/aos 4.7.2 → 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 +309 -101
- 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,729 @@
|
|
|
1
|
+
# Bash Scripting Guide
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Introduction](#introduction)
|
|
6
|
+
2. [Bash vs POSIX sh](#bash-vs-posix-sh)
|
|
7
|
+
3. [Strict Mode and Error Handling](#strict-mode-and-error-handling)
|
|
8
|
+
4. [Variables and Parameter Expansion](#variables-and-parameter-expansion)
|
|
9
|
+
5. [Functions and Scope](#functions-and-scope)
|
|
10
|
+
6. [Arrays and Associative Arrays](#arrays-and-associative-arrays)
|
|
11
|
+
7. [Control Structures](#control-structures)
|
|
12
|
+
8. [Process and Command Substitution](#process-and-command-substitution)
|
|
13
|
+
9. [Best Practices](#best-practices)
|
|
14
|
+
10. [Common Pitfalls](#common-pitfalls)
|
|
15
|
+
|
|
16
|
+
## Introduction
|
|
17
|
+
|
|
18
|
+
Bash (Bourne Again Shell) is a powerful Unix shell and command language. This guide covers modern bash scripting practices and patterns for creating robust, maintainable scripts.
|
|
19
|
+
|
|
20
|
+
## Bash vs POSIX sh
|
|
21
|
+
|
|
22
|
+
### Key Differences
|
|
23
|
+
|
|
24
|
+
**Bash-specific features (not in POSIX sh):**
|
|
25
|
+
- Arrays: `arr=(one two three)`
|
|
26
|
+
- Associative arrays: `declare -A map=([key]=value)`
|
|
27
|
+
- `[[` conditional expressions
|
|
28
|
+
- `$(( ))` arithmetic expansion with more operators
|
|
29
|
+
- `${var//pattern/replacement}` parameter expansion
|
|
30
|
+
- Process substitution: `<(command)`
|
|
31
|
+
- `select` keyword for menus
|
|
32
|
+
- `**` recursive globbing with `shopt -s globstar`
|
|
33
|
+
|
|
34
|
+
**POSIX sh compatible:**
|
|
35
|
+
- Basic variable assignment and substitution
|
|
36
|
+
- `[` test command (single brackets)
|
|
37
|
+
- `case` statements
|
|
38
|
+
- Basic parameter expansion
|
|
39
|
+
- Command substitution with `$()`
|
|
40
|
+
- Functions (with different syntax)
|
|
41
|
+
|
|
42
|
+
### When to Choose
|
|
43
|
+
|
|
44
|
+
**Use Bash when:**
|
|
45
|
+
- Script runs on modern Linux/macOS systems
|
|
46
|
+
- Need arrays or associative arrays
|
|
47
|
+
- Want advanced string manipulation
|
|
48
|
+
- Targeting bash-specific environments
|
|
49
|
+
|
|
50
|
+
**Use POSIX sh when:**
|
|
51
|
+
- Maximum portability required
|
|
52
|
+
- Running on minimal systems (embedded, containers)
|
|
53
|
+
- Need to run on different Unix variants
|
|
54
|
+
- Following strict POSIX compliance requirements
|
|
55
|
+
|
|
56
|
+
## Strict Mode and Error Handling
|
|
57
|
+
|
|
58
|
+
### Essential: set -euo pipefail
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
#!/usr/bin/env bash
|
|
62
|
+
set -euo pipefail
|
|
63
|
+
IFS=$'\n\t'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Explanation:**
|
|
67
|
+
- `set -e` (errexit): Exit immediately if a command exits with non-zero status
|
|
68
|
+
- `set -u` (nounset): Treat unset variables as an error
|
|
69
|
+
- `set -o pipefail`: Return value of pipeline is status of last command to exit with non-zero status
|
|
70
|
+
- `IFS=$'\n\t'`: Set Internal Field Separator to newline and tab only (prevents word splitting issues)
|
|
71
|
+
|
|
72
|
+
### When to Disable Strict Mode Temporarily
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Disable errexit for commands that are expected to fail
|
|
76
|
+
set +e
|
|
77
|
+
command_that_might_fail
|
|
78
|
+
exit_code=$?
|
|
79
|
+
set -e
|
|
80
|
+
|
|
81
|
+
# Or use || true for single commands
|
|
82
|
+
command_that_might_fail || true
|
|
83
|
+
|
|
84
|
+
# Or handle error explicitly
|
|
85
|
+
if ! command_that_might_fail; then
|
|
86
|
+
echo "Command failed, but continuing..."
|
|
87
|
+
fi
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Signal Handling with trap
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Cleanup function
|
|
94
|
+
cleanup() {
|
|
95
|
+
local exit_code=$?
|
|
96
|
+
echo "Cleaning up..." >&2
|
|
97
|
+
rm -f "${temp_file}"
|
|
98
|
+
exit "${exit_code}"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Set traps
|
|
102
|
+
trap cleanup EXIT # Always run cleanup on exit
|
|
103
|
+
trap cleanup ERR # Run cleanup on error
|
|
104
|
+
trap cleanup INT TERM # Run cleanup on interrupt or termination
|
|
105
|
+
|
|
106
|
+
# Create temp file
|
|
107
|
+
temp_file=$(mktemp)
|
|
108
|
+
|
|
109
|
+
# Rest of script...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Error Handling Patterns
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Pattern 1: Die function
|
|
116
|
+
die() {
|
|
117
|
+
echo "ERROR: $*" >&2
|
|
118
|
+
exit 1
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[[ -f "${file}" ]] || die "File not found: ${file}"
|
|
122
|
+
|
|
123
|
+
# Pattern 2: Check function return values
|
|
124
|
+
if ! do_something; then
|
|
125
|
+
echo "do_something failed" >&2
|
|
126
|
+
return 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Pattern 3: Command substitution with error handling
|
|
130
|
+
output=$(command 2>&1) || {
|
|
131
|
+
echo "Command failed: ${output}" >&2
|
|
132
|
+
exit 1
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Pattern 4: Validate prerequisites
|
|
136
|
+
check_command() {
|
|
137
|
+
command -v "$1" &> /dev/null || die "Required command not found: $1"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
check_command "jq"
|
|
141
|
+
check_command "curl"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Variables and Parameter Expansion
|
|
145
|
+
|
|
146
|
+
### Variable Naming Conventions
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Constants - uppercase with readonly
|
|
150
|
+
readonly MAX_RETRIES=3
|
|
151
|
+
readonly CONFIG_FILE="/etc/myapp/config.conf"
|
|
152
|
+
|
|
153
|
+
# Environment variables - uppercase
|
|
154
|
+
export PATH="${HOME}/bin:${PATH}"
|
|
155
|
+
export LOG_LEVEL="INFO"
|
|
156
|
+
|
|
157
|
+
# Local variables - lowercase
|
|
158
|
+
local counter=0
|
|
159
|
+
local temp_file=""
|
|
160
|
+
|
|
161
|
+
# Function names - lowercase with underscores
|
|
162
|
+
process_data() {
|
|
163
|
+
local input="$1"
|
|
164
|
+
# ...
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Always Quote Variables
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Good - properly quoted
|
|
172
|
+
rm "${file}"
|
|
173
|
+
cp "${source}" "${destination}"
|
|
174
|
+
echo "Value: ${variable}"
|
|
175
|
+
|
|
176
|
+
# Bad - unquoted (prone to word splitting and globbing)
|
|
177
|
+
rm $file
|
|
178
|
+
cp $source $destination
|
|
179
|
+
echo "Value: $variable"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Parameter Expansion
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Default values
|
|
186
|
+
${var:-default} # Use default if var is unset or empty
|
|
187
|
+
${var:=default} # Set var to default if unset or empty
|
|
188
|
+
${var:?error message} # Exit with error message if var is unset or empty
|
|
189
|
+
${var:+alternative} # Use alternative if var is set
|
|
190
|
+
|
|
191
|
+
# String manipulation
|
|
192
|
+
${var#pattern} # Remove shortest match from beginning
|
|
193
|
+
${var##pattern} # Remove longest match from beginning
|
|
194
|
+
${var%pattern} # Remove shortest match from end
|
|
195
|
+
${var%%pattern} # Remove longest match from end
|
|
196
|
+
${var/pattern/replacement} # Replace first match
|
|
197
|
+
${var//pattern/replacement} # Replace all matches
|
|
198
|
+
${var^} # Uppercase first character
|
|
199
|
+
${var^^} # Uppercase all characters
|
|
200
|
+
${var,} # Lowercase first character
|
|
201
|
+
${var,,} # Lowercase all characters
|
|
202
|
+
|
|
203
|
+
# Length and substring
|
|
204
|
+
${#var} # Length of var
|
|
205
|
+
${var:offset} # Substring from offset to end
|
|
206
|
+
${var:offset:length} # Substring from offset with length
|
|
207
|
+
|
|
208
|
+
# Examples
|
|
209
|
+
file="/path/to/file.txt"
|
|
210
|
+
${file##*/} # file.txt (basename)
|
|
211
|
+
${file%.*} # /path/to/file (remove extension)
|
|
212
|
+
${file##*.} # txt (extension only)
|
|
213
|
+
${file%/*} # /path/to (dirname)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Functions and Scope
|
|
217
|
+
|
|
218
|
+
### Function Definition
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# POSIX style (portable)
|
|
222
|
+
function_name() {
|
|
223
|
+
# function body
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Bash-specific (not portable to sh)
|
|
227
|
+
function function_name {
|
|
228
|
+
# function body
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# Recommended: POSIX style with local variables
|
|
232
|
+
process_file() {
|
|
233
|
+
local input_file="$1"
|
|
234
|
+
local output_file="$2"
|
|
235
|
+
|
|
236
|
+
# Process file
|
|
237
|
+
grep "pattern" "${input_file}" > "${output_file}"
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Variable Scope
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Global variable
|
|
245
|
+
GLOBAL_VAR="global"
|
|
246
|
+
|
|
247
|
+
my_function() {
|
|
248
|
+
# Local variable - only visible in function
|
|
249
|
+
local local_var="local"
|
|
250
|
+
|
|
251
|
+
# Modifying global variable
|
|
252
|
+
GLOBAL_VAR="modified"
|
|
253
|
+
|
|
254
|
+
# Function parameter access
|
|
255
|
+
local param1="$1"
|
|
256
|
+
local param2="$2"
|
|
257
|
+
|
|
258
|
+
echo "Params: ${param1} ${param2}"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
my_function "arg1" "arg2"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Return Values
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# Functions return exit status (0-255)
|
|
268
|
+
check_file() {
|
|
269
|
+
local file="$1"
|
|
270
|
+
[[ -f "${file}" ]] && return 0 || return 1
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Use function return status
|
|
274
|
+
if check_file "data.txt"; then
|
|
275
|
+
echo "File exists"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# Return data via stdout
|
|
279
|
+
get_value() {
|
|
280
|
+
echo "computed value"
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
# Capture output
|
|
284
|
+
result=$(get_value)
|
|
285
|
+
|
|
286
|
+
# Return data via variable (using nameref in bash 4.3+)
|
|
287
|
+
get_data() {
|
|
288
|
+
local -n result_var=$1
|
|
289
|
+
result_var="computed value"
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
get_data my_result
|
|
293
|
+
echo "${my_result}"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Arrays and Associative Arrays
|
|
297
|
+
|
|
298
|
+
### Indexed Arrays (Bash-specific)
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# Array creation
|
|
302
|
+
arr=() # Empty array
|
|
303
|
+
arr=(one two three) # Initialize with values
|
|
304
|
+
arr[0]="first" # Assign to specific index
|
|
305
|
+
|
|
306
|
+
# Array operations
|
|
307
|
+
arr+=("four") # Append
|
|
308
|
+
${arr[0]} # Access element
|
|
309
|
+
${arr[@]} # All elements (as separate words)
|
|
310
|
+
${arr[*]} # All elements (as single word)
|
|
311
|
+
${#arr[@]} # Number of elements
|
|
312
|
+
${!arr[@]} # Indices
|
|
313
|
+
|
|
314
|
+
# Iterating over array
|
|
315
|
+
for item in "${arr[@]}"; do
|
|
316
|
+
echo "${item}"
|
|
317
|
+
done
|
|
318
|
+
|
|
319
|
+
# Iterating with indices
|
|
320
|
+
for i in "${!arr[@]}"; do
|
|
321
|
+
echo "Index $i: ${arr[i]}"
|
|
322
|
+
done
|
|
323
|
+
|
|
324
|
+
# Array slicing
|
|
325
|
+
${arr[@]:offset:length} # Slice array
|
|
326
|
+
|
|
327
|
+
# Remove element
|
|
328
|
+
unset 'arr[1]' # Remove specific element
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Associative Arrays (Bash 4.0+)
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Declaration required
|
|
335
|
+
declare -A map
|
|
336
|
+
|
|
337
|
+
# Assignment
|
|
338
|
+
map[key1]="value1"
|
|
339
|
+
map[key2]="value2"
|
|
340
|
+
|
|
341
|
+
# Or initialize
|
|
342
|
+
declare -A map=([key1]="value1" [key2]="value2")
|
|
343
|
+
|
|
344
|
+
# Access
|
|
345
|
+
${map[key1]} # Get value
|
|
346
|
+
${map[@]} # All values
|
|
347
|
+
${!map[@]} # All keys
|
|
348
|
+
${#map[@]} # Number of elements
|
|
349
|
+
|
|
350
|
+
# Check if key exists
|
|
351
|
+
if [[ -v map[key1] ]]; then
|
|
352
|
+
echo "key1 exists"
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
# Iterate over keys and values
|
|
356
|
+
for key in "${!map[@]}"; do
|
|
357
|
+
echo "${key}: ${map[${key}]}"
|
|
358
|
+
done
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### POSIX Alternative to Arrays
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Use positional parameters
|
|
365
|
+
set -- one two three
|
|
366
|
+
|
|
367
|
+
# Access
|
|
368
|
+
echo "$1" # one
|
|
369
|
+
echo "$2" # two
|
|
370
|
+
echo "$#" # count: 3
|
|
371
|
+
|
|
372
|
+
# Iterate
|
|
373
|
+
for item in "$@"; do
|
|
374
|
+
echo "${item}"
|
|
375
|
+
done
|
|
376
|
+
|
|
377
|
+
# Add item
|
|
378
|
+
set -- "$@" "four"
|
|
379
|
+
|
|
380
|
+
# Remove first item
|
|
381
|
+
shift
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Control Structures
|
|
385
|
+
|
|
386
|
+
### Conditional Expressions
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
# Bash [[ ... ]] (recommended for bash)
|
|
390
|
+
if [[ -f "${file}" ]]; then
|
|
391
|
+
echo "File exists"
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
if [[ "${var}" == "value" ]]; then
|
|
395
|
+
echo "Match"
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
if [[ "${var}" =~ ^[0-9]+$ ]]; then
|
|
399
|
+
echo "Numeric"
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
# POSIX [ ... ] (portable)
|
|
403
|
+
if [ -f "${file}" ]; then
|
|
404
|
+
echo "File exists"
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
# File tests
|
|
408
|
+
[[ -e file ]] # Exists
|
|
409
|
+
[[ -f file ]] # Regular file
|
|
410
|
+
[[ -d file ]] # Directory
|
|
411
|
+
[[ -L file ]] # Symbolic link
|
|
412
|
+
[[ -r file ]] # Readable
|
|
413
|
+
[[ -w file ]] # Writable
|
|
414
|
+
[[ -x file ]] # Executable
|
|
415
|
+
[[ -s file ]] # Not empty
|
|
416
|
+
|
|
417
|
+
# String tests
|
|
418
|
+
[[ -z "${var}" ]] # Empty string
|
|
419
|
+
[[ -n "${var}" ]] # Non-empty string
|
|
420
|
+
[[ "${a}" == "${b}" ]] # Equal
|
|
421
|
+
[[ "${a}" != "${b}" ]] # Not equal
|
|
422
|
+
[[ "${a}" < "${b}" ]] # Lexicographically less (bash only)
|
|
423
|
+
|
|
424
|
+
# Numeric tests
|
|
425
|
+
[[ "${a}" -eq "${b}" ]] # Equal
|
|
426
|
+
[[ "${a}" -ne "${b}" ]] # Not equal
|
|
427
|
+
[[ "${a}" -lt "${b}" ]] # Less than
|
|
428
|
+
[[ "${a}" -le "${b}" ]] # Less than or equal
|
|
429
|
+
[[ "${a}" -gt "${b}" ]] # Greater than
|
|
430
|
+
[[ "${a}" -ge "${b}" ]] # Greater than or equal
|
|
431
|
+
|
|
432
|
+
# Logical operators
|
|
433
|
+
[[ condition1 && condition2 ]] # AND
|
|
434
|
+
[[ condition1 || condition2 ]] # OR
|
|
435
|
+
[[ ! condition ]] # NOT
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### case Statements
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
case "${var}" in
|
|
442
|
+
pattern1)
|
|
443
|
+
# commands
|
|
444
|
+
;;
|
|
445
|
+
pattern2|pattern3)
|
|
446
|
+
# Multiple patterns
|
|
447
|
+
;;
|
|
448
|
+
*)
|
|
449
|
+
# Default case
|
|
450
|
+
;;
|
|
451
|
+
esac
|
|
452
|
+
|
|
453
|
+
# Example with patterns
|
|
454
|
+
case "${file}" in
|
|
455
|
+
*.txt)
|
|
456
|
+
echo "Text file"
|
|
457
|
+
;;
|
|
458
|
+
*.jpg|*.png)
|
|
459
|
+
echo "Image file"
|
|
460
|
+
;;
|
|
461
|
+
*)
|
|
462
|
+
echo "Unknown type"
|
|
463
|
+
;;
|
|
464
|
+
esac
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Loops
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# while loop
|
|
471
|
+
while condition; do
|
|
472
|
+
# commands
|
|
473
|
+
done
|
|
474
|
+
|
|
475
|
+
# until loop
|
|
476
|
+
until condition; do
|
|
477
|
+
# commands
|
|
478
|
+
done
|
|
479
|
+
|
|
480
|
+
# for loop (C-style, bash only)
|
|
481
|
+
for ((i=0; i<10; i++)); do
|
|
482
|
+
echo "${i}"
|
|
483
|
+
done
|
|
484
|
+
|
|
485
|
+
# for loop (iterating over values)
|
|
486
|
+
for item in one two three; do
|
|
487
|
+
echo "${item}"
|
|
488
|
+
done
|
|
489
|
+
|
|
490
|
+
# for loop (iterating over files)
|
|
491
|
+
for file in *.txt; do
|
|
492
|
+
echo "${file}"
|
|
493
|
+
done
|
|
494
|
+
|
|
495
|
+
# for loop (iterating over command output)
|
|
496
|
+
while IFS= read -r line; do
|
|
497
|
+
echo "${line}"
|
|
498
|
+
done < file.txt
|
|
499
|
+
|
|
500
|
+
# Or with command substitution (avoid for large output)
|
|
501
|
+
for file in $(find . -name "*.txt"); do
|
|
502
|
+
echo "${file}"
|
|
503
|
+
done
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Process and Command Substitution
|
|
507
|
+
|
|
508
|
+
### Command Substitution
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
# Recommended: $( ... )
|
|
512
|
+
result=$(command)
|
|
513
|
+
result=$(command arg1 arg2)
|
|
514
|
+
|
|
515
|
+
# Nested command substitution
|
|
516
|
+
outer=$(echo "Inner: $(echo "value")")
|
|
517
|
+
|
|
518
|
+
# Not recommended: backticks (legacy)
|
|
519
|
+
result=`command`
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Process Substitution (Bash-specific)
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
# <( ... ) creates a named pipe/file descriptor
|
|
526
|
+
# Treat command output as a file
|
|
527
|
+
|
|
528
|
+
# Compare output of two commands
|
|
529
|
+
diff <(ls dir1) <(ls dir2)
|
|
530
|
+
|
|
531
|
+
# Use multiple inputs
|
|
532
|
+
paste <(cut -f1 file1) <(cut -f2 file2)
|
|
533
|
+
|
|
534
|
+
# Output redirection with process substitution
|
|
535
|
+
command > >(tee stdout.log) 2> >(tee stderr.log >&2)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## Best Practices
|
|
539
|
+
|
|
540
|
+
### Script Structure
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
#!/usr/bin/env bash
|
|
544
|
+
set -euo pipefail
|
|
545
|
+
IFS=$'\n\t'
|
|
546
|
+
|
|
547
|
+
# ============================================================================
|
|
548
|
+
# Script Name: example.sh
|
|
549
|
+
# Description: Brief description
|
|
550
|
+
# Author: Your Name
|
|
551
|
+
# Created: 2025-01-23
|
|
552
|
+
# ============================================================================
|
|
553
|
+
|
|
554
|
+
# Constants
|
|
555
|
+
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
556
|
+
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
|
|
557
|
+
|
|
558
|
+
# Global variables
|
|
559
|
+
VERBOSE=false
|
|
560
|
+
DRY_RUN=false
|
|
561
|
+
|
|
562
|
+
# Functions
|
|
563
|
+
usage() {
|
|
564
|
+
# ...
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
cleanup() {
|
|
568
|
+
# ...
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
main() {
|
|
572
|
+
# ...
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
# Signal handlers
|
|
576
|
+
trap cleanup EXIT ERR INT TERM
|
|
577
|
+
|
|
578
|
+
# Execute main
|
|
579
|
+
main "$@"
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Always Use Quotes
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
# Good
|
|
586
|
+
echo "${variable}"
|
|
587
|
+
cp "${source}" "${dest}"
|
|
588
|
+
[[ -f "${file}" ]]
|
|
589
|
+
|
|
590
|
+
# Bad (unsafe)
|
|
591
|
+
echo $variable
|
|
592
|
+
cp $source $dest
|
|
593
|
+
[[ -f $file ]]
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Use readonly for Constants
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
readonly MAX_RETRIES=3
|
|
600
|
+
readonly CONFIG_FILE="/etc/config"
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Prefer $() Over Backticks
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
# Good
|
|
607
|
+
output=$(command)
|
|
608
|
+
result=$(first $(second))
|
|
609
|
+
|
|
610
|
+
# Bad
|
|
611
|
+
output=`command`
|
|
612
|
+
result=`first \`second\`` # Hard to read
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### Check Command Existence
|
|
616
|
+
|
|
617
|
+
```bash
|
|
618
|
+
if ! command -v required_cmd &> /dev/null; then
|
|
619
|
+
echo "Error: required_cmd not found" >&2
|
|
620
|
+
exit 1
|
|
621
|
+
fi
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Validate Inputs
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
# Check argument count
|
|
628
|
+
if [[ $# -lt 1 ]]; then
|
|
629
|
+
echo "Usage: $0 <file>" >&2
|
|
630
|
+
exit 1
|
|
631
|
+
fi
|
|
632
|
+
|
|
633
|
+
# Validate file exists
|
|
634
|
+
[[ -f "${file}" ]] || { echo "File not found: ${file}" >&2; exit 1; }
|
|
635
|
+
|
|
636
|
+
# Validate numeric input
|
|
637
|
+
[[ "${count}" =~ ^[0-9]+$ ]] || { echo "Count must be numeric" >&2; exit 1; }
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Common Pitfalls
|
|
641
|
+
|
|
642
|
+
### Word Splitting
|
|
643
|
+
|
|
644
|
+
```bash
|
|
645
|
+
# Problem: Filename with spaces
|
|
646
|
+
file="my file.txt"
|
|
647
|
+
rm $file # Tries to remove "my" and "file.txt"
|
|
648
|
+
|
|
649
|
+
# Solution: Quote variables
|
|
650
|
+
rm "${file}" # Correctly removes "my file.txt"
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Globbing
|
|
654
|
+
|
|
655
|
+
```bash
|
|
656
|
+
# Problem: Pattern in variable
|
|
657
|
+
pattern="*.txt"
|
|
658
|
+
echo $pattern # Expands to list of .txt files
|
|
659
|
+
|
|
660
|
+
# Solution: Quote to prevent globbing
|
|
661
|
+
echo "${pattern}" # Prints "*.txt"
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Useless Use of Cat (UUOC)
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
# Bad: Unnecessary cat
|
|
668
|
+
cat file.txt | grep "pattern"
|
|
669
|
+
|
|
670
|
+
# Good: Direct input
|
|
671
|
+
grep "pattern" file.txt
|
|
672
|
+
|
|
673
|
+
# Bad: cat in loop
|
|
674
|
+
cat file.txt | while read line; do
|
|
675
|
+
echo "${line}"
|
|
676
|
+
done
|
|
677
|
+
|
|
678
|
+
# Good: redirect to while
|
|
679
|
+
while read -r line; do
|
|
680
|
+
echo "${line}"
|
|
681
|
+
done < file.txt
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### Not Handling Spaces in Filenames
|
|
685
|
+
|
|
686
|
+
```bash
|
|
687
|
+
# Bad: Will break on filenames with spaces
|
|
688
|
+
for file in $(find . -name "*.txt"); do
|
|
689
|
+
process "${file}"
|
|
690
|
+
done
|
|
691
|
+
|
|
692
|
+
# Good: Use while read
|
|
693
|
+
find . -name "*.txt" -print0 | while IFS= read -r -d '' file; do
|
|
694
|
+
process "${file}"
|
|
695
|
+
done
|
|
696
|
+
|
|
697
|
+
# Or use globbing
|
|
698
|
+
for file in ./**/*.txt; do
|
|
699
|
+
process "${file}"
|
|
700
|
+
done
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Ignoring Command Exit Status
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
# Bad: Ignoring failure
|
|
707
|
+
command_that_might_fail
|
|
708
|
+
next_command
|
|
709
|
+
|
|
710
|
+
# Good: Check exit status
|
|
711
|
+
if command_that_might_fail; then
|
|
712
|
+
next_command
|
|
713
|
+
else
|
|
714
|
+
echo "Command failed" >&2
|
|
715
|
+
exit 1
|
|
716
|
+
fi
|
|
717
|
+
|
|
718
|
+
# Or with errexit
|
|
719
|
+
command_that_might_fail || { echo "Failed" >&2; exit 1; }
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
---
|
|
723
|
+
|
|
724
|
+
## References
|
|
725
|
+
|
|
726
|
+
- [GNU Bash Manual](https://www.gnu.org/software/bash/manual/bash.html)
|
|
727
|
+
- [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)
|
|
728
|
+
- [ShellCheck](https://www.shellcheck.net/) - Script analysis tool
|
|
729
|
+
- [Bash Guide for Beginners](https://tldp.org/LDP/Bash-Beginners-Guide/html/)
|