echo:打印

echo "hello"
echo "name: $name"
echo -n "no newline"          # 不加结尾换行
echo -e "tab:\tnewline:\n"    # 启用转义

printf:更精确的打印(推荐)

printf "name: %s age: %d\n" "alice" 30
printf "pi = %.4f\n" 3.14159265
printf "%-10s %s\n" "Name:" "Alice"      # %-10s = 左对齐 10 宽
printf "%5d\n" 42                          # %5d = 右对齐 5 宽

# 多次套用一组格式
printf "%-10s %s\n" Name Alice Age 30 City NY
# Name       Alice
# Age        30
# City       NY

printf 行为统一(不像 echo 在不同 shell 有差异),写正经脚本优先 printf

read:读用户输入

read -p "你叫啥?" name
echo "你好 $name"

read -s -p "密码:" pwd    # -s 不回显
echo
echo "密码长度:${#pwd}"

# 设默认值
read -p "端口 [80]:" port
port=${port:-80}            # 空就用 80

重定向

# 标准输入:fd 0
# 标准输出:fd 1
# 标准错误:fd 2

cmd > file               # stdout 写文件(覆盖)
cmd >> file              # stdout 追加
cmd 2> err.txt           # stderr 写文件
cmd > out.txt 2> err.txt # 分开
cmd > all.txt 2>&1       # stderr 合并到 stdout
cmd &> all.txt           # 简写(bash 4+)
cmd > /dev/null          # 丢掉 stdout
cmd 2> /dev/null         # 丢掉 stderr
cmd &> /dev/null         # 全丢

cmd < input.txt          # 从文件读 stdin
cmd <<< "hello"          # here string:把字符串当 stdin
cmd <<EOF                # here doc:多行
line 1
line 2
EOF

管道 | —— 上一个的 stdout → 下一个的 stdin

ls -l | grep ".txt" | sort | head -5

把 stderr 也送进管道:

cmd 2>&1 | grep error

bash 4+ 简写:

cmd |& grep error

tee:管道里写文件(同时屏幕也看)

ls -l | tee out.txt           # 屏幕和文件都出
ls -l | tee -a out.txt        # 追加
cmd | tee out.txt | grep foo  # 中间存一份

实战:长命令要看输出又要存档:

make 2>&1 | tee build.log

here doc:多行字符串

cat <<EOF
第 1 行
第 2 行 $name
EOF

写文件:

cat <<EOF > config.ini
[server]
port = 8080
host = $HOST
EOF

不展开变量(保持字面):

cat <<'EOF' > literal.txt
这里 $name 不会被展开
EOF

实战:脚本日志双写

#!/bin/bash
exec > >(tee -a /var/log/myapp.log) 2>&1
echo "脚本启动 $(date)"
# ... 任何后续输出都同时进日志和屏幕

下一篇:条件判断。