少写引号会发生什么
一句话总结
Shell 在变量展开后进行分词。不加引号会把一个值变成多个参数,使脚本执行完全不同的操作。
安全传参
变量、数组和脚本参数都必须保留原有边界。文件名可以包含空格甚至换行,因此文件列表应使用 NUL 分隔,或直接让 find 执行动作。命令的双横线用于结束选项解析,避免以连字符开头的文件名被当成选项。
短选项应交给 getopts,而不是手写脆弱的解析器。用户输入只能作为值传给命令,绝不能交给 eval 重新解释;路径也必须验证不能越出预期目录。ShellCheck 能检查常见引号、read 和无用管道问题。
原文命令与标识符
OLD=$(find /data -name '*.tmp' -mtime +7)
rm $OLD
f="my report.txt"
rm $f # rm my report.txt → 인자 2개
rm "$f" # rm "my report.txt" → 인자 1개 ✅
args=(-l -a "my dir")
ls "${args[@]}" # 각 원소가 하나의 인자로 ✅
ls ${args[@]} # 다시 쪼개진다 ✗
# 안전
find /data -name '*.tmp' -mtime +7 -print0 | xargs -0 rm --
# 더 안전 (xargs 도 필요 없다)
find /data -name '*.tmp' -mtime +7 -delete
# 루프가 필요하면
while IFS= read -r -d '' f; do
process "$f"
done < <(find /data -name '*.tmp' -print0)
rm -- "$f"
grep -- "$pattern" "$file"
verbose=0; out=""
while getopts ":vo:" opt; do
case $opt in
v) verbose=1 ;;
o) out=$OPTARG ;;
:) echo "-$OPTARG 에 값이 필요합니다" >&2; exit 2 ;;
\?) echo "알 수 없는 옵션: -$OPTARG" >&2; exit 2 ;;
esac
done
shift $((OPTIND - 1))
# 남은 것이 위치 인자
# 절대 금지
eval "grep $user_input file.txt"
# 값으로만 쓴다
grep -- "$user_input" file.txt
/data/my report.tmp
rm
/data/my
report.tmp
/data/my
"$@"
"$*"
"$@"
"$*"
"$@"
IFS= read -r
IFS=
-r
--
-rf
rm $file
--
$1
case
getopts
":vo:"
:
\?
eval
; rm -rf /
../../etc/passwd
shellcheck
read
-r
cat
"$@"
$*
getopts
-vo
eval