The Agent Skills Directory
npx skills add https://smithery.ai/skills/bradreaves/shell-script-installer此技能帮助您创建 Shell 脚本并将其安装到用户的 PATH 中。
当用户出现以下情况时,激活此技能:
默认使用 Fish shell 来创建所有新脚本,除非:
将所有脚本安装到:~/bin/scripts
重要提示: ~/bin/scripts 是一个 git 仓库。所有脚本的创建和编辑都必须使用正确的 git 工作流程。
按顺序遵循以下步骤:
cd ~/bin/scriptsgit checkout -b add-<script-name>add-backup-tool、add-git-helper)~/bin/scripts/<script-name>#!/usr/bin/env fish#!/usr/bin/env bashchmod +x ~/bin/scripts/<script-name><script-name> 吗?"git add <script-name>git checkout maingit merge add-<script-name>git branch -d add-<script-name>
检查 ~/bin/scripts 是否在用户的 PATH 中
如果不在,通知用户并提供添加说明:
# 对于 Fish:添加到 ~/.config/fish/config.fish
fish_add_path $HOME/bin/scripts
# 对于 bash:添加到 ~/.bashrc 或 ~/.bash_profile
# 对于 zsh:添加到 ~/.zshrc
export PATH="$HOME/bin/scripts:$PATH"
which <script-name>关键: 所有脚本必须使用 logger 命令实现结构化日志记录。
logger 命令scriptname[PID](在 bash 中使用 $$ 表示 PID,在 fish 中使用 $fish_pid)$fish_pid 获取进程 ID,不要使用 $$ 或 %self将脚本事件映射到适当的日志级别:
user.info:处理状态、文件操作、成功事件
user.warning:不支持的格式、非关键问题、可恢复的错误
user.error:缺少依赖项、失败、退出条件
user.debug:文件创建、跳过的文件、详细操作
Fish:
logger -t (basename (status filename))"[$fish_pid]" -p user.info "action=start status=processing"
logger -t (basename (status filename))"[$fish_pid]" -p user.error "action=fail error=\"missing dependency\""
Bash:
logger -t "$(basename "$0")[$$]" -p user.info "action=start status=processing"
logger -t "$(basename "$0")[$$]" -p user.error "action=fail error=\"missing dependency\""
关键: 处理数据(读取输入、转换、写入输出)的脚本必须遵循这些 I/O 标准。
当未提供文件参数时,默认使用 stdin:
set input_file "-"
if test (count $argv) -gt 0
set input_file $argv[1]
end
# 验证文件是否存在(如果不是 stdin)
if test "$input_file" != "-" -a ! -f "$input_file"
echo "Error: Input file not found: $input_file" >&2
log_error "action=read_input status=not_found file=\"$input_file\""
exit 2
end
默认使用 stdout,但提供 -o/--output 标志用于文件输出:
set output_file "-"
set append_mode 0
argparse 'o/output=' 'a/append' -- $argv
if set -q _flag_output
set output_file $_flag_output
end
if set -q _flag_append
set append_mode 1
end
# 验证:追加模式需要输出文件
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
stdout = 数据,stderr = 消息
# 错误 - 混合数据和消息
echo "Processing 100 items..."
echo "$result_data"
# 正确 - 分离流
echo "Processing 100 items..." >&2 # 进度信息到 stderr
echo "$result_data" # 数据到 stdout
原因: 管道仅捕获 stdout。stderr 上的消息会显示给用户,但不会污染数据流。
对于结构化数据,首选 TSV(制表符分隔值):
# TSV 格式(无标题行,易于管道处理)
echo -e "$url\t$title\t$format\t$notes"
优点:
cut、awk、sort 处理对于迭代处理多个项目或执行长时间运行操作的脚本:
# 自动检测交互模式与批处理模式
set progress_mode 0
if isatty stderr
set progress_mode 1 # 交互模式 - 默认显示进度
end
# 解析标志(在 argparse 中)
argparse 'progress' 'no-progress' -- $argv
# 显式标志覆盖自动检测
if set -q _flag_progress
set progress_mode 1
end
if set -q _flag_no_progress
set progress_mode 0
end
# 在处理循环中:
set current 0
set total (count $items)
for item in $items
set current (math $current + 1)
if test $progress_mode -eq 1
# 非滚动的原地更新(\r 返回到行首)
printf "\r[%d/%d] %s" $current $total "$item" >&2
end
# ... 执行工作 ...
end
# 最终换行以完成进度行
if test $progress_mode -eq 1
printf "\n" >&2
end
# 摘要统计信息输出到 stderr(即使是无进度模式也始终显示)
echo "Results: $valid valid, $invalid invalid" >&2
关键点:
printf "\r..." 进行非滚动的原地更新# 示例:在相关工具之间启用管道
cat urls.txt | extract-urls | validate-urls | download-videos
# 链中的每个脚本:
# - 从 stdin 或文件读取
# - 将数据写入 stdout
# - 将进度/错误写入 stderr
# - 使用一致的格式(每行一个或 TSV)
将这些添加到您的 usage() 函数中:
echo "Examples:"
echo " $SCRIPT_NAME input.txt -o output.txt"
echo " cat input.txt | $SCRIPT_NAME"
echo " $SCRIPT_NAME < input.txt > output.txt"
echo " $SCRIPT_NAME input.txt | other-tool"
--help / -h 参数 以及使用信息--version / -v 参数 显示脚本版本--test 参数 运行代码的单元测试和回归测试--fish-completions 参数 将 fish shell 标签补全安装到 ~/.config/fish/completions/<script-name>.fish
处理输入数据并产生输出的脚本还必须包含:
-o / --output FILE - 将输出写入 FILE 而不是 stdout(默认:stdout)-a / --append - 追加到输出文件而不是覆盖(需要 -o)处理多个项目或执行长时间运行操作的脚本必须包含:
--progress - 强制进行原地进度更新(即使在批处理模式下)--no-progress - 抑制进度更新(即使在交互模式下)isatty stderr 自动检测(交互模式 = 进度开启,批处理模式 = 进度关闭)# Installation: Copy to ~/bin/scripts# Installation: Copy to ~/.config/fish/functions# Installation: Source from ~/.config/fish/config.fish or ~/.bashrc关键: Fish shell 具有与 bash/zsh 不同的特定行为。有关详细规则、示例和本项目中的错误历史记录,请参阅 ~/fish-shell-rules.md 中的综合指南。
变量作用域: 对于跨函数访问的变量,使用 set -g(而不是 set -l)
# 错误:set -l urls "..." # 在被调用的函数中将是空的
# 正确:set -g urls "..." # 到处可见
目录更改: Fish 的 (cd dir && cmd) 不会 创建子 shell - 始终保存/恢复 $PWD
# 错误:(cd "$temp" && process) # 更改了父目录!
# 正确:set orig $PWD; cd "$temp"; process; cd "$orig"
多行输出: 使用 | string collect 在命令替换中保留换行符
# 错误:set output (command) # 将换行符折叠为空格
# 正确:set output (command | string collect)
有关完整规则以及显示这些规则存在原因的提交历史记录,请参阅 ~/fish-shell-rules.md。
#!/usr/bin/env fish 以提高可移植性--help 和 --version 标志包含使用/帮助信息#!/usr/bin/env bash 以提高可移植性--help 和 --version 标志包含使用/帮助信息set -e 或适当的错误检查添加错误处理cd 命令: 当脚本更改目录时,后续使用相对路径的操作可能会中断set origin_dir (pwd)(fish)或 origin_dir=$(pwd)(bash)#!/usr/bin/env fish
# Script: example-script
# Version: 1.0.0
# Description: What this script does
# Installation: Copy to ~/bin/scripts
set VERSION "1.0.0"
set SCRIPT_NAME (basename (status filename))
function log_info
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.info $argv
end
function log_error
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.error $argv
end
function log_debug
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.debug $argv
end
function show_version
echo "$SCRIPT_NAME version $VERSION"
exit 0
end
function run_tests
log_info "action=test status=starting"
# Add unit and regression tests here
echo "Running unit tests..."
# Example test placeholder
# Replace with actual test logic
echo "All tests passed!"
log_info "action=test status=complete"
exit 0
end
function install_fish_completions
set -l completions_file ~/.config/fish/completions/$SCRIPT_NAME.fish
# Check if file already exists
if test -f "$completions_file"
echo "Error: Completions file already exists: $completions_file" >&2
echo "Remove it first if you want to regenerate completions." >&2
exit 1
end
# Ensure directory exists
mkdir -p ~/.config/fish/completions
# Generate and write completions
echo "# Fish completions for $SCRIPT_NAME
# Generated by $SCRIPT_NAME --fish-completions
# Complete flags
complete -c $SCRIPT_NAME -s h -l help -d 'Show help message'
complete -c $SCRIPT_NAME -s v -l version -d 'Show version information'
complete -c $SCRIPT_NAME -l test -d 'Run unit and regression tests'
complete -c $SCRIPT_NAME -l fish-completions -d 'Install fish shell completions'
# Add script-specific completions here
" > "$completions_file"
and begin
echo "Fish completions installed to: $completions_file"
echo ""
echo "Completions will be available in new fish shell sessions."
echo "To use them immediately in this session, run:"
echo " source $completions_file"
end
or begin
echo "Error: Failed to write completions file" >&2
exit 1
end
exit 0
end
function usage
echo "Usage: $SCRIPT_NAME [options]"
echo ""
echo "Description: What this script does"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
exit 0
end
function main
log_info "action=start status=processing"
# Your script logic here
echo "Hello from example Fish script!"
log_info "action=complete status=success"
end
# Parse arguments
argparse 'h/help' 'v/version' 'test' 'fish-completions' -- $argv
or begin
usage
end
if set -q _flag_help
usage
end
if set -q _flag_version
show_version
end
if set -q _flag_test
run_tests
end
if set -q _flag_fish_completions
install_fish_completions
end
log_debug "action=init args=\"$argv\""
main
#!/usr/bin/env bash
set -e
# Script: example-script
# Version: 1.0.0
# Description: What this script does
# Installation: Copy to ~/bin/scripts
VERSION="1.0.0"
SCRIPT_NAME="$(basename "$0")"
log_info() {
logger -t "${SCRIPT_NAME}[$$]" -p user.info "$@"
}
log_error() {
logger -t "${SCRIPT_NAME}[$$]" -p user.error "$@"
}
log_debug() {
logger -t "${SCRIPT_NAME}[$$]" -p user.debug "$@"
}
show_version() {
echo "$SCRIPT_NAME version $VERSION"
exit 0
}
run_tests() {
log_info "action=test status=starting"
# Add unit and regression tests here
echo "Running unit tests..."
# Example test placeholder
# Replace with actual test logic
echo "All tests passed!"
log_info "action=test status=complete"
exit 0
}
install_fish_completions() {
local completions_file=~/.config/fish/completions/$SCRIPT_NAME.fish
# Check if file already exists
if [[ -f "$completions_file" ]]; then
echo "Error: Completions file already exists: $completions_file" >&2
echo "Remove it first if you want to regenerate completions." >&2
exit 1
fi
# Ensure directory exists
mkdir -p ~/.config/fish/completions
# Generate and write completions
cat > "$completions_file" << 'EOF'
# Fish completions for $SCRIPT_NAME
# Generated by $SCRIPT_NAME --fish-completions
# Complete flags
complete -c $SCRIPT_NAME -s h -l help -d 'Show help message'
complete -c $SCRIPT_NAME -s v -l version -d 'Show version information'
complete -c $SCRIPT_NAME -l test -d 'Run unit and regression tests'
complete -c $SCRIPT_NAME -l fish-completions -d 'Install fish shell completions'
# Add script-specific completions here
EOF
if [[ $? -eq 0 ]]; then
echo "Fish completions installed to: $completions_file"
echo ""
echo "Completions will be available in new fish shell sessions."
echo "To use them immediately in this session, run:"
echo " source $completions_file"
else
echo "Error: Failed to write completions file" >&2
exit 1
fi
exit 0
}
usage() {
echo "Usage: $SCRIPT_NAME [options]"
echo ""
echo "Description: What this script does"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
exit 0
}
main() {
log_info "action=start status=processing"
# Your script logic here
echo "Hello from example bash script!"
log_info "action=complete status=success"
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-v|--version)
show_version
;;
--test)
run_tests
;;
--fish-completions)
install_fish_completions
;;
*)
echo "Unknown option: $1"
usage
;;
esac
shift
done
log_debug "action=init args=\"$*\""
main
对于处理输入并产生输出(尤其是多项目处理)的脚本,请使用此增强模板:
#!/usr/bin/env fish
# Script: process-items
# Version: 1.0.0
# Description: Process items from input and output results
# Installation: Copy to ~/bin/scripts
set VERSION "1.0.0"
set SCRIPT_NAME (basename (status filename))
# Logging functions
function log_info
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.info $argv
end
function log_error
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.error $argv
end
function show_version
echo "$SCRIPT_NAME version $VERSION"
exit 0
end
function usage
echo "Usage: $SCRIPT_NAME [options] [input-file]"
echo ""
echo "Process items from input and output results."
echo ""
echo "Arguments:"
echo " input-file File containing items (default: stdin)"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " -o, --output FILE Output file (default: stdout)"
echo " -a, --append Append to existing file instead of overwriting"
echo " --progress Force progress updates (even in batch mode)"
echo " --no-progress Suppress progress updates (even in interactive mode)"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
echo ""
echo "Examples:"
echo " $SCRIPT_NAME input.txt -o output.txt"
echo " cat input.txt | $SCRIPT_NAME"
echo " $SCRIPT_NAME input.txt | other-tool"
exit 0
end
function main
# I/O configuration
set input_file "-"
set output_file "-"
set append_mode 0
# Progress configuration (auto-detect TTY)
set progress_mode 0
if isatty stderr
set progress_mode 1
end
# Parse arguments
argparse 'h/help' 'v/version' 'o/output=' 'a/append' 'progress' 'no-progress' 'test' 'fish-completions' -- $argv
or begin
usage
exit 2
end
# Handle flags
if set -q _flag_help; usage; end
if set -q _flag_version; show_version; end
if set -q _flag_output; set output_file $_flag_output; end
if set -q _flag_append; set append_mode 1; end
if set -q _flag_progress; set progress_mode 1; end
if set -q _flag_no_progress; set progress_mode 0; end
# Get input file from remaining arguments
if test (count $argv) -gt 0
set input_file $argv[1]
if test "$input_file" != "-" -a ! -f "$input_file"
echo "Error: Input file not found: $input_file" >&2
log_error "action=read_input status=not_found file=\"$input_file\""
exit 2
end
end
# Validate append requires output file
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
log_info "action=start input=\"$input_file\" output=\"$output_file\""
# Read input items
if test "$input_file" = "-"
set items (cat)
else
set items (cat $input_file)
end
# Process items with progress
set current 0
set total (count $items)
set results
for item in $items
set current (math $current + 1)
# Show progress (non-scrolling, in-place update)
if test $progress_mode -eq 1
printf "\r[%d/%d] Processing: %s" $current $total "$item" >&2
end
# Process item (replace with actual logic)
set result (process_item $item)
set -a results $result
end
# Complete progress line
if test $progress_mode -eq 1
printf "\n" >&2
end
# Summary to stderr (always show)
echo "Processed $total items" >&2
# Write output (data to stdout or file)
if test "$output_file" = "-"
printf "%s\n" $results
else
if test $append_mode -eq 1
printf "%s\n" $results >> "$output_file"
else
printf "%s\n" $results > "$output_file"
end
end
log_info "action=complete status=success count=$total"
end
function process_item
# Replace with actual processing logic
echo "processed: $argv[1]"
end
# Run main
main $argv
注意: 对于 Fish 特定错误(变量作用域、目录更改、stdin 检测等),请参阅 ~/fish-shell-rules.md,其中记录了本项目历史中的重复性错误。
错误:
echo "Processing file..." # 输出到 stdout
echo "$result" # 也输出到 stdout
正确:
echo "Processing file..." >&2 # 消息输出到 stderr
echo "$result" # 数据输出到 stdout
错误: 仅接受文件参数
cat $argv[1] # 如果未指定文件则失败
正确: 默认使用 stdin/stdout
set input_file "-"
if test (count $argv) -gt 0
set input_file $argv[1]
end
cd 后使用相对路径错误:
cd /some/directory
cat config.txt # 这个文件现在在哪里?
正确:
# 存储原始目录或使用绝对路径
set origin_dir (pwd)
cd /some/directory
# ... 执行工作 ...
cd $origin_dir
cat config.txt
错误:
curl $url > data.json
process_file data.json # 如果 curl 失败了怎么办?
正确:
curl $url > data.json
if test $status -ne 0
log_error "action=download status=failed url=\"$url\""
exit 1
end
错误:
if not command -v tool
exit 1 # 用户不知道发生了什么
end
正确:
if not command -v tool
echo "Error: 'tool' is not installed" >&2
echo "Install with: brew install tool" >&2
log_error "action=dependency_check status=missing tool=tool"
exit 3
end
错误: 允许 --append 而不指定 --output
# 应该验证追加模式需要一个文件
正确:
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
错误: 在数据处理脚本中请求用户输入
read -P "Continue? (y/n): " answer
正确: 对所有选项使用标志,避免在支持管道的脚本中使用提示
chmod +x~/bin/scripts 在 PATH 中git merge --abort 中止git stash 暂存它们git branch -D add-<script-name> 删除旧分支,或使用不同的名称add-<script-name>git checkout main && git branch -D add-<script-name>重要提示: 使用详细、结构化的提交消息,格式如下:
Add <script-name>: <brief one-line description>
Features:
- Feature 1 with details
- Feature 2 with details
- Feature 3 with details
Technical details:
- Implementation detail 1
- Implementation detail 2
- Configuration or dependency notes
Co-Authored-By: Claude <noreply@anthropic.com>
指南:
示例:
Add backup-tool: Automated backup script with compression and rotation
Features:
- Automatic backup of specified directories
- Gzip compression with configurable level
- Rotation policy (keeps last N backups)
- Email notifications on completion or failure
- Dry-run mode for testing
Technical details:
- Implemented in Fish shell with structured logging
- Uses rsync for efficient file copying
- Logger integration for syslog monitoring
- Configuration via environment variables
- Requires: rsync, gzip, mail utilities
Co-Authored-By: Claude <
This skill helps you create and install shell scripts to the user's PATH.
Activate this skill when the user:
Default to Fish shell for all new scripts unless:
Install all scripts to: ~/bin/scripts
IMPORTANT: ~/bin/scripts is a git repository. All script creation and editing must use proper git workflow.
Follow these steps in order:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
stdin 检测: 在读取 stdin 之前始终检查参数是否为空
# 错误:if not isatty stdin # 重定向时会出现误报
# 正确:if test (count $argv) -eq 0; and not isatty stdin
数组迭代: 使用直接迭代(永远不要使用 echo/split)
# 错误:for x in (echo "$array" | string split \n)
# 正确:for x in $array
字符串操作: 使用 Fish 内置的 string 而不是 grep/sed/awk
# 错误:echo "$text" | grep "pattern"
# 正确:string match "*pattern*" $text
cd ~/bin/scriptsgit checkout -b add-<script-name>add-backup-tool, add-git-helper)~/bin/scripts/<script-name>#!/usr/bin/env fish#!/usr/bin/env bashchmod +x ~/bin/scripts/<script-name><script-name>?"git add <script-name>git checkout maingit merge add-<script-name>git branch -d add-<script-name>
Check if ~/bin/scripts is in the user's PATH
If not, inform the user and provide instructions to add it:
# For Fish: add to ~/.config/fish/config.fish
fish_add_path $HOME/bin/scripts
# For bash: add to ~/.bashrc or ~/.bash_profile
export PATH="$HOME/bin/scripts:$PATH"
which <script-name>CRITICAL: All scripts must implement structured logging using the logger command.
logger command with structured key=value formatscriptname[PID] (use $$ for PID in bash, $fish_pid in fish)$fish_pid to get the process ID, NOT $$ or %selfMap script events to appropriate log levels:
user.info : Process status, file operations, success events
user.warning : Unsupported formats, non-critical issues, recoverable errors
user.error : Missing dependencies, failures, exit conditions
user.debug : File creation, skipped files, detailed operations
Fish:
logger -t (basename (status filename))"[$fish_pid]" -p user.info "action=start status=processing"
logger -t (basename (status filename))"[$fish_pid]" -p user.error "action=fail error=\"missing dependency\""
Bash:
logger -t "$(basename "$0")[$$]" -p user.info "action=start status=processing"
logger -t "$(basename "$0")[$$]" -p user.error "action=fail error=\"missing dependency\""
CRITICAL: Scripts that process data (read input, transform, write output) MUST follow these I/O standards.
Default to stdin when no file argument is provided:
set input_file "-"
if test (count $argv) -gt 0
set input_file $argv[1]
end
# Validate file exists (if not stdin)
if test "$input_file" != "-" -a ! -f "$input_file"
echo "Error: Input file not found: $input_file" >&2
log_error "action=read_input status=not_found file=\"$input_file\""
exit 2
end
Default to stdout , but provide -o/--output flag for file output:
set output_file "-"
set append_mode 0
argparse 'o/output=' 'a/append' -- $argv
if set -q _flag_output
set output_file $_flag_output
end
if set -q _flag_append
set append_mode 1
end
# Validate: append requires output file
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
stdout = data, stderr = messages
# WRONG - mixes data and messages
echo "Processing 100 items..."
echo "$result_data"
# CORRECT - separates streams
echo "Processing 100 items..." >&2 # Progress to stderr
echo "$result_data" # Data to stdout
Why: Pipes capture stdout only. Messages on stderr appear to user but don't pollute the data stream.
For structured data, prefer TSV (tab-separated values):
# TSV format (no header, easy to pipe)
echo -e "$url\t$title\t$format\t$notes"
Benefits:
cut, awk, sortFor scripts that iterate over multiple items or perform long-running operations:
# Auto-detect interactive vs batch mode
set progress_mode 0
if isatty stderr
set progress_mode 1 # Interactive - show progress by default
end
# Parse flags (in argparse)
argparse 'progress' 'no-progress' -- $argv
# Explicit flags override auto-detection
if set -q _flag_progress
set progress_mode 1
end
if set -q _flag_no_progress
set progress_mode 0
end
# In processing loop:
set current 0
set total (count $items)
for item in $items
set current (math $current + 1)
if test $progress_mode -eq 1
# Non-scrolling in-place update (\r returns to start of line)
printf "\r[%d/%d] %s" $current $total "$item" >&2
end
# ... do work ...
end
# Final newline to complete the progress line
if test $progress_mode -eq 1
printf "\n" >&2
end
# Summary stats to stderr (always show, even in no-progress mode)
echo "Results: $valid valid, $invalid invalid" >&2
Key Points:
printf "\r..." for non-scrolling in-place updates# Example: Enable piping between related tools
cat urls.txt | extract-urls | validate-urls | download-videos
# Each script in the chain:
# - Reads from stdin OR file
# - Writes data to stdout
# - Writes progress/errors to stderr
# - Uses consistent format (one-per-line or TSV)
Add these to your usage() function:
echo "Examples:"
echo " $SCRIPT_NAME input.txt -o output.txt"
echo " cat input.txt | $SCRIPT_NAME"
echo " $SCRIPT_NAME < input.txt > output.txt"
echo " $SCRIPT_NAME input.txt | other-tool"
--help / -h argument with usage information--version / -v argument showing script version--test argument that runs unit and regression tests of the code--fish-completions argument that installs fish shell tab completions to ~/.config/fish/completions/<script-name>.fish
Scripts that process input data and produce output MUST also include:
-o / --output FILE - Write output to FILE instead of stdout (default: stdout)-a / --append - Append to output file instead of overwriting (requires -o)Scripts that process multiple items or perform long-running operations MUST include:
--progress - Force in-place progress updates (even in batch mode)--no-progress - Suppress progress updates (even in interactive mode)isatty stderr (interactive = progress on, batch = progress off)# Installation: Copy to ~/bin/scripts# Installation: Copy to ~/.config/fish/functions# Installation: Source from ~/.config/fish/config.fish or ~/.bashrcCRITICAL: Fish shell has specific behaviors that differ from bash/zsh. See the comprehensive guide at ~/fish-shell-rules.md for detailed rules, examples, and bug history from this project.
Variable Scoping : Use set -g (not set -l) for variables accessed across functions
# CORRECT: set -g urls "..." # Visible everywhere
Directory Changes : Fish (cd dir && cmd) does NOT create subshell - always save/restore $PWD
# CORRECT: set orig $PWD; cd "$temp"; process; cd "$orig"
Multi-line Output : Use | string collect to preserve newlines in command substitution
# CORRECT: set output (command | string collect)
stdin Detection : Always check for empty args before reading stdin
# CORRECT: if test (count $argv) -eq 0; and not isatty stdin
Array Iteration : Use direct iteration (never echo/split)
# CORRECT: for x in $array
String Operations : Use Fish's string built-in instead of grep/sed/awk
# CORRECT: string match "*pattern*" $text
See~/fish-shell-rules.md for complete rules and the commit history showing why these rules exist.
#!/usr/bin/env fish for portability--help and --version flags#!/usr/bin/env bash for portability--help and --version flagsset -e or appropriate error checkscd commands: When scripts change directories, subsequent operations with relative paths may breakset origin_dir (pwd) (fish) or origin_dir=$(pwd) (bash)#!/usr/bin/env fish
# Script: example-script
# Version: 1.0.0
# Description: What this script does
# Installation: Copy to ~/bin/scripts
set VERSION "1.0.0"
set SCRIPT_NAME (basename (status filename))
function log_info
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.info $argv
end
function log_error
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.error $argv
end
function log_debug
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.debug $argv
end
function show_version
echo "$SCRIPT_NAME version $VERSION"
exit 0
end
function run_tests
log_info "action=test status=starting"
# Add unit and regression tests here
echo "Running unit tests..."
# Example test placeholder
# Replace with actual test logic
echo "All tests passed!"
log_info "action=test status=complete"
exit 0
end
function install_fish_completions
set -l completions_file ~/.config/fish/completions/$SCRIPT_NAME.fish
# Check if file already exists
if test -f "$completions_file"
echo "Error: Completions file already exists: $completions_file" >&2
echo "Remove it first if you want to regenerate completions." >&2
exit 1
end
# Ensure directory exists
mkdir -p ~/.config/fish/completions
# Generate and write completions
echo "# Fish completions for $SCRIPT_NAME
# Generated by $SCRIPT_NAME --fish-completions
# Complete flags
complete -c $SCRIPT_NAME -s h -l help -d 'Show help message'
complete -c $SCRIPT_NAME -s v -l version -d 'Show version information'
complete -c $SCRIPT_NAME -l test -d 'Run unit and regression tests'
complete -c $SCRIPT_NAME -l fish-completions -d 'Install fish shell completions'
# Add script-specific completions here
" > "$completions_file"
and begin
echo "Fish completions installed to: $completions_file"
echo ""
echo "Completions will be available in new fish shell sessions."
echo "To use them immediately in this session, run:"
echo " source $completions_file"
end
or begin
echo "Error: Failed to write completions file" >&2
exit 1
end
exit 0
end
function usage
echo "Usage: $SCRIPT_NAME [options]"
echo ""
echo "Description: What this script does"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
exit 0
end
function main
log_info "action=start status=processing"
# Your script logic here
echo "Hello from example Fish script!"
log_info "action=complete status=success"
end
# Parse arguments
argparse 'h/help' 'v/version' 'test' 'fish-completions' -- $argv
or begin
usage
end
if set -q _flag_help
usage
end
if set -q _flag_version
show_version
end
if set -q _flag_test
run_tests
end
if set -q _flag_fish_completions
install_fish_completions
end
log_debug "action=init args=\"$argv\""
main
#!/usr/bin/env bash
set -e
# Script: example-script
# Version: 1.0.0
# Description: What this script does
# Installation: Copy to ~/bin/scripts
VERSION="1.0.0"
SCRIPT_NAME="$(basename "$0")"
log_info() {
logger -t "${SCRIPT_NAME}[$$]" -p user.info "$@"
}
log_error() {
logger -t "${SCRIPT_NAME}[$$]" -p user.error "$@"
}
log_debug() {
logger -t "${SCRIPT_NAME}[$$]" -p user.debug "$@"
}
show_version() {
echo "$SCRIPT_NAME version $VERSION"
exit 0
}
run_tests() {
log_info "action=test status=starting"
# Add unit and regression tests here
echo "Running unit tests..."
# Example test placeholder
# Replace with actual test logic
echo "All tests passed!"
log_info "action=test status=complete"
exit 0
}
install_fish_completions() {
local completions_file=~/.config/fish/completions/$SCRIPT_NAME.fish
# Check if file already exists
if [[ -f "$completions_file" ]]; then
echo "Error: Completions file already exists: $completions_file" >&2
echo "Remove it first if you want to regenerate completions." >&2
exit 1
fi
# Ensure directory exists
mkdir -p ~/.config/fish/completions
# Generate and write completions
cat > "$completions_file" << 'EOF'
# Fish completions for $SCRIPT_NAME
# Generated by $SCRIPT_NAME --fish-completions
# Complete flags
complete -c $SCRIPT_NAME -s h -l help -d 'Show help message'
complete -c $SCRIPT_NAME -s v -l version -d 'Show version information'
complete -c $SCRIPT_NAME -l test -d 'Run unit and regression tests'
complete -c $SCRIPT_NAME -l fish-completions -d 'Install fish shell completions'
# Add script-specific completions here
EOF
if [[ $? -eq 0 ]]; then
echo "Fish completions installed to: $completions_file"
echo ""
echo "Completions will be available in new fish shell sessions."
echo "To use them immediately in this session, run:"
echo " source $completions_file"
else
echo "Error: Failed to write completions file" >&2
exit 1
fi
exit 0
}
usage() {
echo "Usage: $SCRIPT_NAME [options]"
echo ""
echo "Description: What this script does"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
exit 0
}
main() {
log_info "action=start status=processing"
# Your script logic here
echo "Hello from example bash script!"
log_info "action=complete status=success"
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-v|--version)
show_version
;;
--test)
run_tests
;;
--fish-completions)
install_fish_completions
;;
*)
echo "Unknown option: $1"
usage
;;
esac
shift
done
log_debug "action=init args=\"$*\""
main
For scripts that process input and produce output (especially multi-item processing), use this enhanced template:
#!/usr/bin/env fish
# Script: process-items
# Version: 1.0.0
# Description: Process items from input and output results
# Installation: Copy to ~/bin/scripts
set VERSION "1.0.0"
set SCRIPT_NAME (basename (status filename))
# Logging functions
function log_info
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.info $argv
end
function log_error
logger -t "$SCRIPT_NAME[$fish_pid]" -p user.error $argv
end
function show_version
echo "$SCRIPT_NAME version $VERSION"
exit 0
end
function usage
echo "Usage: $SCRIPT_NAME [options] [input-file]"
echo ""
echo "Process items from input and output results."
echo ""
echo "Arguments:"
echo " input-file File containing items (default: stdin)"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --version Show version information"
echo " -o, --output FILE Output file (default: stdout)"
echo " -a, --append Append to existing file instead of overwriting"
echo " --progress Force progress updates (even in batch mode)"
echo " --no-progress Suppress progress updates (even in interactive mode)"
echo " --test Run unit and regression tests"
echo " --fish-completions Install fish shell completions"
echo ""
echo "Examples:"
echo " $SCRIPT_NAME input.txt -o output.txt"
echo " cat input.txt | $SCRIPT_NAME"
echo " $SCRIPT_NAME input.txt | other-tool"
exit 0
end
function main
# I/O configuration
set input_file "-"
set output_file "-"
set append_mode 0
# Progress configuration (auto-detect TTY)
set progress_mode 0
if isatty stderr
set progress_mode 1
end
# Parse arguments
argparse 'h/help' 'v/version' 'o/output=' 'a/append' 'progress' 'no-progress' 'test' 'fish-completions' -- $argv
or begin
usage
exit 2
end
# Handle flags
if set -q _flag_help; usage; end
if set -q _flag_version; show_version; end
if set -q _flag_output; set output_file $_flag_output; end
if set -q _flag_append; set append_mode 1; end
if set -q _flag_progress; set progress_mode 1; end
if set -q _flag_no_progress; set progress_mode 0; end
# Get input file from remaining arguments
if test (count $argv) -gt 0
set input_file $argv[1]
if test "$input_file" != "-" -a ! -f "$input_file"
echo "Error: Input file not found: $input_file" >&2
log_error "action=read_input status=not_found file=\"$input_file\""
exit 2
end
end
# Validate append requires output file
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
log_info "action=start input=\"$input_file\" output=\"$output_file\""
# Read input items
if test "$input_file" = "-"
set items (cat)
else
set items (cat $input_file)
end
# Process items with progress
set current 0
set total (count $items)
set results
for item in $items
set current (math $current + 1)
# Show progress (non-scrolling, in-place update)
if test $progress_mode -eq 1
printf "\r[%d/%d] Processing: %s" $current $total "$item" >&2
end
# Process item (replace with actual logic)
set result (process_item $item)
set -a results $result
end
# Complete progress line
if test $progress_mode -eq 1
printf "\n" >&2
end
# Summary to stderr (always show)
echo "Processed $total items" >&2
# Write output (data to stdout or file)
if test "$output_file" = "-"
printf "%s\n" $results
else
if test $append_mode -eq 1
printf "%s\n" $results >> "$output_file"
else
printf "%s\n" $results > "$output_file"
end
end
log_info "action=complete status=success count=$total"
end
function process_item
# Replace with actual processing logic
echo "processed: $argv[1]"
end
# Run main
main $argv
NOTE: For Fish-specific errors (variable scoping, directory changes, stdin detection, etc.), see ~/fish-shell-rules.md which documents recurring bugs from this project's history.
WRONG:
echo "Processing file..." # Goes to stdout
echo "$result" # Also goes to stdout
CORRECT:
echo "Processing file..." >&2 # Messages to stderr
echo "$result" # Data to stdout
WRONG: Only accepting file arguments
cat $argv[1] # Fails if no file specified
CORRECT: Default to stdin/stdout
set input_file "-"
if test (count $argv) -gt 0
set input_file $argv[1]
end
cdWRONG:
cd /some/directory
cat config.txt # Where is this file now?
CORRECT:
# Store original directory or use absolute paths
set origin_dir (pwd)
cd /some/directory
# ... work ...
cd $origin_dir
cat config.txt
WRONG:
curl $url > data.json
process_file data.json # What if curl failed?
CORRECT:
curl $url > data.json
if test $status -ne 0
log_error "action=download status=failed url=\"$url\""
exit 1
end
WRONG:
if not command -v tool
exit 1 # User has no idea what happened
end
CORRECT:
if not command -v tool
echo "Error: 'tool' is not installed" >&2
echo "Install with: brew install tool" >&2
log_error "action=dependency_check status=missing tool=tool"
exit 3
end
WRONG: Allowing --append without --output
# Should validate that append mode requires a file
CORRECT:
if test $append_mode -eq 1 -a "$output_file" = "-"
echo "Error: --append requires --output to specify a file" >&2
exit 2
end
WRONG: Asking for user input in a data-processing script
read -P "Continue? (y/n): " answer
CORRECT: Use flags for all options, avoid prompts in pipe-friendly scripts
chmod +x was applied to the script~/bin/scripts is in PATHgit merge --abortgit stashgit branch -D add-<script-name> or use a different nameadd-<script-name>git checkout main && git branch -D add-<script-name>IMPORTANT: Use detailed, structured commit messages with the following format:
Add <script-name>: <brief one-line description>
Features:
- Feature 1 with details
- Feature 2 with details
- Feature 3 with details
Technical details:
- Implementation detail 1
- Implementation detail 2
- Configuration or dependency notes
Co-Authored-By: Claude <noreply@anthropic.com>
Guidelines:
Example:
Add backup-tool: Automated backup script with compression and rotation
Features:
- Automatic backup of specified directories
- Gzip compression with configurable level
- Rotation policy (keeps last N backups)
- Email notifications on completion or failure
- Dry-run mode for testing
Technical details:
- Implemented in Fish shell with structured logging
- Uses rsync for efficient file copying
- Logger integration for syslog monitoring
- Configuration via environment variables
- Requires: rsync, gzip, mail utilities
Co-Authored-By: Claude <noreply@anthropic.com>
Weekly Installs
–
Source
First Seen
–
Azure Data Explorer (Kusto) 查询技能:KQL数据分析、日志遥测与时间序列处理
107,900 周安装