用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Texarkanine/.cursor-rules --skill bash-style命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Basic PR Review - looks for critical blocking issues and a decent attempt to find other high-impact but non-blocking issues near and in the changed code.
Niko Memory Bank System - Preflight Phase - Pre-Build Plan Validation
Niko Memory Bank System - Niko Phase - Initialization & Entry Point
基于 SOC 职业分类
正在显示 SKILL.md
| name | bash-style |
| description | Required style guidelines for writing shell scripts where POSIX-compliance is not an explicit requirement |
Shell should only be used for small utilities or simple wrapper scripts:
The complexity threshold is about maintainability by people other than the author.
IMPORTANT - LACK OF POSIX COMPLIANCE: This rule only applies when POSIX-compatibility is NOT a requirement. These guidelines are specifically for bash-based shell scripts that can leverage bash-specific features and "bashisms."
Always use bash for executable scripts:
#!/bin/bash
# Minimal flags approach - use 'set' for shell options instead
Use 'set' for shell options:
#!/bin/bash
set -euo pipefail # Exit on error, undefined variables, pipe failures
.bash extension OR no extension
.bash if build rules will rename the file.bash extension and should NOT be executableEvery file must start with a description:
#!/bin/bash
#
# Backup utility for PostgreSQL databases
# Performs incremental backups and uploads to S3
#
# Usage: backup_postgres.sh [database_name]
#
# Copyright 2024 Company Name
# Author: developer@company.com
Use tabs for initial indentation:
if [[ "${1}" == "start" ]]; then
echo "Starting service..."
if systemctl start myservice; then
echo "Service started successfully"
else
echo "Failed to start service" >&2
return 1
fi
fi
Use spaces for subsequent indentation:
This is rare; usually only used for visual alignment.
if [[ "${1}" == "start" ]]; then
echo "Starting service..."
if systemctl start myservice; then
# the following lines align function inputs w/ spaces
report_start "myservice"
log "myservice started"
monitor_service "myservice"
else
echo "Failed to start service" >&2
return 1
fi
fi
ALWAYS use spaces for indentation within multi-line comments!
if [[ "${1}" == "start" ]]; then
echo "Starting service..."
if systemctl start myservice; then
echo "Service started successfully"
else
echo "Failed to start service" >&2
# we aren't sure why it failed b/c the service
# doesn't integrate w/ systemctl properly. Could be
# - bad config
# - network error
# - intermittent i/o
return 1
fi
fi
Keep lines under 80 characters when possible:
Break lines logically, not arbitrarily at 80 characters:
# Good - break at logical points
if [[ "${enable_logging}" == "true" ]] && [[ -w "${log_directory}" ]]; then
echo "Logging enabled to ${log_directory}"
fi
# Bad - breaking in the middle of a logical condition
if [[ "${enable_logging}" == "true" ]] && [[ -w "${log_directory}" \
]]; then
echo "Logging enabled to ${log_directory}"
fi
Prefer slightly longer lines over awkwardly short continuation lines:
# Good - let it go a bit over 80 rather than create a short second line
echo "Processing configuration file: ${config_file} with options: ${options}"
# Bad - creates an awkwardly short second line
echo "Processing configuration file: ${config_file} with options: \
${options}"
For long commands, use line continuation with proper indentation:
# Good - logical breaks with consistent indentation
command \
--option1 value1 \
--option2 value2 \
--option3 value3
# Bad - breaks at arbitrary character limits
command --option1 value1 --option2 value2 \
--option3 value3
For long strings, use here documents:
# Good - here document for multi-line content
cat <<EOF
This is a long message that would exceed the 80 character limit
if written on one line.
EOF
# Bad - awkward line continuation for strings
echo "This is a long message that would exceed \
the 80 character limit if written on one line."
Break sentences at natural boundaries:
# Good - break at sentence boundaries using heredoc
cat <<EOF
Starting backup process for database ${db_name}.
This may take several minutes depending on database size.
EOF
# Bad - break mid-sentence at character limit
cat <<EOF
Starting backup process for database ${db_name}. This may take
several minutes depending on database size.
EOF
Put pipelines on separate lines when they become long:
# Short pipeline - single line is fine
ps aux | grep nginx
# Long pipeline - break it up
command1 \
| command2 \
| command3 \
| command4
Use proper spacing and alignment:
# if statements
if [[ "${condition}" ]]; then
# code
elif [[ "${other_condition}" ]]; then
# code
else
# code
fi
# for loops
for file in "${files[@]}"; do
process_file "${file}"
done
# while loops
while read -r line; do
echo "Processing: ${line}"
done < "${input_file}"
Align and indent consistently:
case "${1}" in
start)
start_service
;;
stop)
stop_service
;;
restart)
stop_service
start_service
;;
*)
echo "Usage: ${0} {start|stop|restart}" >&2
exit 1
;;
esac
Always use braces for variable expansion:
# Good
echo "Hello ${name}!"
echo "File: ${file}.backup"
# Bad
echo "Hello $name!"
echo "File: $file.backup"
Quote variables to prevent word splitting:
# Good
if [[ -f "${config_file}" ]]; then
cp "${config_file}" "${backup_dir}/"
fi
# Bad - can break with spaces in filenames
if [[ -f $config_file ]]; then
cp $config_file $backup_dir/
fi
Quote all strings except in specific contexts:
# Good
echo "Starting process: ${process_name}"
grep "pattern" "${file}"
# Arithmetic context doesn't need quotes
(( count = count + 1 ))
if (( count > 10 )); then
echo "Count exceeded limit"
fi
Use lowercase with underscores (snake_case):
# Single function
process_file() {
local file="${1}"
# implementation
}
Use consistent formatting with comprehensive documentation:
Any function that is not both obvious and short must have a function header comment. All functions in libraries must have a function header comment regardless of length or complexity.
All function header comments must describe the intended API behavior using these required sections (always present, even if not applicable):
# Processes a log file and extracts error messages
#
# Globals:
# LOG_LEVEL - Used to determine verbosity (read-only)
# ERROR_COUNT - Modified to track total errors found
# Arguments:
# $1 - Path to log file (required)
# $2 - Output format: 'json' or 'text' (optional, defaults to 'text')
# Outputs:
# Error messages to STDOUT
# Error details and warnings to STDERR
# Returns:
# 0 on success
# 1 on file not found
# 2 on invalid format
process_log_file() {
local log_file="${1}"
local format="${2:-text}"
# Validate input
if [[ ! -f "${log_file}" ]]; then
echo "Error: Log file '${log_file}' not found" >&2
return 1
fi
if [[ "${format}" != "text" && "${format}" != "json" ]]; then
echo "Error: Invalid format '${format}'. Use 'text' or 'json'" >&2
return 2
fi
# Process the file
case ""
json)
grep \
| sed \
| -u \
| jq -R
;;
text)
grep \
| sed \
| -u
;;
0
}
() {
message=
}
Always use local variables in functions:
process_data() {
local input_file="${1}"
local output_file="${2}"
local temp_file
# Separate declaration and assignment for command substitution
temp_file="$(mktemp)"
# Process data
sort "${input_file}" > "${temp_file}"
mv "${temp_file}" "${output_file}"
}
Use lowercase with underscores (snake_case):
user_name="john_doe"
config_file="/etc/myapp/config.conf"
temp_directory="/tmp/myapp_$$"
Use uppercase with underscores:
readonly CONFIG_DIR="/etc/myapp"
readonly MAX_RETRIES=3
declare -xr LOG_LEVEL="INFO"
Name descriptively:
# Good
for zone in "${availability_zones[@]}"; do
deploy_to_zone "${zone}"
done
# Bad
for i in "${availability_zones[@]}"; do
deploy_to_zone "${i}"
done
Always check command return values:
# Using if statement
if ! mv "${source_file}" "${dest_dir}/"; then
echo "Error: Unable to move ${source_file} to ${dest_dir}" >&2
exit 1
fi
# Using $? variable
cp "${file}" "${backup_location}"
if (( $? != 0 )); then
echo "Error: Failed to backup ${file}" >&2
exit 1
fi
Use PIPESTATUS for pipeline error checking:
tar -czf - "${directory}" | ssh user@host "cat > backup.tar.gz"
if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
echo "Error: Backup pipeline failed" >&2
exit 1
fi
# For complex pipelines, capture PIPESTATUS immediately
long_command | filter_command | output_command
return_codes=( "${PIPESTATUS[@]}" )
if (( return_codes[0] != 0 )); then
echo "Error: long_command failed" >&2
elif (( return_codes[1] != 0 )); then
echo "Error: filter_command failed" >&2
fi
Send errors to STDERR with timestamps:
# Error reporting function
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
# Usage
if ! create_backup "${database}"; then
err "Failed to create backup for database: ${database}"
exit 1
fi
When returning status from a function, use numerical return codes:
# Good - status uses exit codes
is_even() {
# Checks if the input number is even
if (( ${1} % 2 == 0 )); then
return 0 # it's even
else
return 1 # it's not even
fi
}
# Bad - unnecessary string use for status
file_exists() {
# Checks if the given file exists
if [[ -f "${1}" ]]; then
echo "true" # it exists
else
echo "false" # it doesn't exist
fi
}
When returning strings from a function with stdout, ensure that the function only ever returns the proper string:
# Good - redirect useful output from stdout to stderr
mentions_cursor() {
git fetch --all >&2
if grep -q -lr "cursor"; then
echo "true"
else
echo "false"
fi
}
# Good - redirect unneeded stdout to /dev/null
mentions_cursor() {
if grep -lr "cursor" . >/dev/null; then
echo "true"
else
echo "false"
fi
}
# Bad - silence output with a flag (cannot guarantee ALL stdout is silenced)
mentions_cursor() {
if grep -q -lr "cursor"; then
echo "true"
else
echo "false"
fi
}
# Bad - output will contain extraneous content, not just the function's intended return value
mentions_cursor() {
if grep -lr "cursor"; then
echo "true"
else
echo "false"
fi
}
Use $(...) instead of backticks:
# Good
current_date="$(date '+%Y-%m-%d')"
file_count="$(find "${dir}" -type f | wc -l)"
# Bad
current_date=`date '+%Y-%m-%d'`
file_count=`find "${dir}" -type f | wc -l`
Prefer [[ ]] over [ ]:
# Use [[ ]] for bash-specific features
if [[ "${file}" =~ \.txt$ ]]; then
echo "Text file detected"
fi
if [[ -n "${variable}" && "${variable}" != "default" ]]; then
process_variable "${variable}"
fi
# String testing examples
if [[ -z "${string}" ]]; then # Empty string
echo "String is empty"
fi
if [[ "${string1}" == "${string2}" ]]; then # String equality
echo "Strings are equal"
fi
# File testing
if [[ -f "${file}" ]]; then # File exists and is regular file
echo "File exists"
fi
if [[ -d "${directory}" ]]; then # Directory exists
echo "Directory exists"
fi
Use (( )) for arithmetic operations:
# Arithmetic evaluation
(( total = count * price ))
(( i += 1 ))
# Arithmetic conditions
if (( count > threshold )); then
echo "Threshold exceeded"
fi
# Avoid external tools for simple arithmetic
# Good: (( result = 10 * 5 ))
# Bad: result="$(expr 10 \* 5)"
Use bash arrays when appropriate:
# Declare and populate arrays
declare -a files
files=( "/path/one" "/path/two" "/path/three" )
# Better: direct assignment
files=(
"/path/one"
"/path/two"
"/path/three"
)
# Iterate over arrays
for file in "${files[@]}"; do
echo "Processing: ${file}"
done
# Array length
echo "Total files: ${#files[@]}"
Use main function for excecutable scripts with multiple functions:
#!/bin/bash
# Function definitions
setup_environment() {
# Setup code
}
process_arguments() {
# Argument processing
}
cleanup() {
# Cleanup code
}
# Main function
main() {
setup_environment
process_arguments "$@"
# Main script logic here
cleanup
}
# IMPORTANT - Only run main if script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
Never use SUID/SGID on shell scripts:
# Use sudo for elevated access instead
if ! sudo systemctl restart nginx; then
echo "Error: Failed to restart nginx" >&2
exit 1
fi
Always validate and sanitize inputs:
validate_input() {
local input="${1}"
# Check if input is provided
if [[ -z "${input}" ]]; then
echo "Error: Input required" >&2
return 1
fi
# Validate format (example: alphanumeric only)
if [[ ! "${input}" =~ ^[a-zA-Z0-9_]+$ ]]; then
echo "Error: Invalid input format" >&2
return 1
fi
return 0
}
Prefer bash built-ins over external commands:
# Good - using bash built-ins
string_length="${#variable}"
substring="${variable:0:10}"
replacement="${variable/pattern/replacement}"
# Avoid external commands when built-ins work
# Good: (( result = x + y ))
# Bad: result="$(expr "${x}" + "${y}")"
# Good: if [[ "${string}" =~ pattern ]]; then
# Bad: if echo "${string}" | grep -q pattern; then
Be careful with filename expansion:
# Good - explicit globbing with safeguards
for file in /path/to/files/*.txt; do
[[ -f "${file}" ]] || continue # Skip if no matches
process_file "${file}"
done
# Good - disable globbing when not needed
set -f # Disable globbing
echo "This * will not expand"
set +f # Re-enable globbing
# Avoid unquoted expansion in dangerous contexts
# Bad: rm ${files} # Could expand unexpectedly
# Good: rm "${files[@]}" # Array expansion
Use process substitution instead of pipes to while:
# Good - using process substitution
while read -r line; do
echo "Processing: ${line}"
done < <(some_command)
# Avoid - pipe to while (creates subshell)
some_command | while read -r line; do
echo "Processing: ${line}"
# Variables set here won't persist outside the loop
done
Use here documents for multi-line strings:
# Here document
cat <<EOF
This is a multi-line
string that can contain
variable substitutions: ${variable}
EOF
# Here document with no substitution
cat <<'EOF'
This text is literal:
${variable} will not be expanded
EOF
# Here string (single line)
grep "pattern" <<<"${string_to_search}"
# Bad: using ls
for file in $(ls *.txt); do
process_file "${file}"
done
# Good: using globs
for file in *.txt; do
[[ -f "${file}" ]] || continue # Skip if no matches
process_file "${file}"
done
Write testable shell scripts:
#!/bin/bash
# calculator.sh - Example of testable shell script
add() {
echo $(( $1 + $2 ))
}
subtract() {
echo $(( $1 - $2 ))
}
main() {
case "${1}" in
add) add "${2}" "${3}" ;;
sub) subtract "${2}" "${3}" ;;
*) echo "Usage: ${0} {add|sub} num1 num2" >&2; exit 1 ;;
esac
}
# Only run main if executed directly, not when sourced for testing
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
Example test file (using bats or similar):
#!/usr/bin/env bats
# test_calculator.bats
# Source the script under test
source "$(dirname "$BATS_TEST_FILENAME")/calculator.sh"
@test "add function works correctly" {
result="$(add 5 3)"
[ "$result" -eq 8 ]
}
@test "subtract function works correctly" {
result="$(subtract 10 4)"
[ "$result" -eq 6 ]
}
Always use ShellCheck for static analysis:
# Install ShellCheck
# Ubuntu/Debian: apt-get install shellcheck
# macOS: brew install shellcheck
# Or use online at: https://www.shellcheck.net/
# Run ShellCheck on your scripts
shellcheck myscript.sh
# Disable specific warnings when justified
# shellcheck disable=SC2034 # Unused variable
readonly UNUSED_VAR="value"
# Disable for entire file (use sparingly)
# shellcheck disable=SC1091 # Can't follow source
Common ShellCheck fixes:
# SC2086: Quote variables to prevent word splitting
# Bad:
cp $file $destination
# Good:
cp "${file}" "${destination}"
# SC2155: Declare and assign separately
# Bad:
local var="$(command_that_might_fail)"
# Good:
local var
var="$(command_that_might_fail)"