Bash One-Liners 学习精要指南

为什么学习Bash one-liners?因为它们简洁、高效,且充分利用了Bash的内置特性(如参数扩展、重定向和历史扩展),减少了对外部工具的依赖。通过掌握Bash的单行命令(one-liners),你可以显著提高工作效率,避免编写冗长的脚本,同时培养出一种"壳思维"------用最少的代码实现最多的功能。

学习参考:

Bash One-Liners Explained, Part I: Working with files https://catonmat.net/bash-one-liners-explained-part-one

Bash One-Liners Explained, Part II: Working with strings https://catonmat.net/bash-one-liners-explained-part-two

Bash One-Liners Explained, Part III: All about redirections https://catonmat.net/bash-one-liners-explained-part-three

Bash One-Liners Explained, Part IV: Working with history https://catonmat.net/bash-one-liners-explained-part-four

Bash One-Liners Explained, Part V: Navigating around https://catonmat.net/bash-one-liners-explained-part-five

Bash命令行历史的权威指南 附录https://catonmat.net/the-definitive-guide-to-bash-command-line-history

Bash Reference Manual https://tiswww.case.edu/php/chet/bash/bashref.html

Part I: 文件操作

文件操作是Bash脚本的基础。Bash提供内置的读写机制、重定向和参数扩展,让你无需外部工具如catsed就能完成许多任务。以下one-liners聚焦于高效的文件处理,参考Catonmat系列Part I。

1. 清空文件(截断为0字节)

bash 复制代码
> file.txt

这个命令使用输出重定向>打开file.txt进行写入。如果文件存在,它会被清空;如果不存在,则创建空文件。由于没有实际内容被重定向,文件保持为空。根据Bash手册,重定向>等价于1> file,其中1是stdout的文件描述符(FD 1)。这是一个原子操作,避免了竞争条件。

要写入内容:

bash 复制代码
echo "Hello, Bash!" > file.txt

2. 向文件追加字符串

bash 复制代码
echo "追加的内容" >> file.txt

>>是追加模式,等价于1>> file。如果文件不存在,会创建它。echo默认添加换行符。要避免换行,使用-n选项:

bash 复制代码
echo -n "无换行追加" >> file.txt

Bash手册强调,追加模式不会截断现有内容,确保数据完整性。

3. 从文件读取第一行到变量

bash 复制代码
read -r line < file.txt

read从stdin读取一行,-r防止反斜杠转义。< file.txt将文件重定向到stdin。默认IFS(内部字段分隔符,默认为空格、制表符和换行)会修剪前后空白。要保留空白:

bash 复制代码
IFS= read -r line < file.txt

备选方案使用命令替换:

bash 复制代码
line=$(head -1 file.txt)

$(...)捕获head输出,比反引号...更现代,支持嵌套。

4. 逐行读取文件

bash 复制代码
while read -r line; do
    echo "处理: $line"
done < file.txt

这是一个while循环,read每次读取一行,直到EOF。输入重定向< file.txt确保高效。保留空白:

bash 复制代码
while IFS= read -r line; do
    echo "处理: $line"
done < file.txt

管道版本(较慢,因子壳):

bash 复制代码
cat file.txt | while read -r line; do
    echo "处理: $line"
done

Bash手册指出,while循环中的重定向是高效的,因为它在父壳中执行。

5. 读取文件中的随机一行

bash 复制代码
read -r random_line < <(shuf file.txt)

过程替换<(...)shuf(GNU核心utils的随机打乱工具)输出作为read的输入。shuf随机排列行,第一行被读取。

备选使用sort

bash 复制代码
read -r random_line < <(sort -R file.txt)

或命令替换:

bash 复制代码
random_line=$(shuf -n 1 file.txt)

这些方法利用Bash 4+的过程替换,创建匿名管道。

6. 读取文件的前三个字段到变量

bash 复制代码
while read -r field1 field2 field3 _; do
    echo "字段1: $field1, 2: $field2, 3: $field3"
done < file.txt

read基于IFS拆分行,前三个字段赋值给变量,剩余到_(占位符)。如果行正好三个字段,省略_

示例:解析wc输出:

bash 复制代码
read lines words chars _ < <(wc file.txt)
echo "行数: $lines, 单词: $words, 字符: $chars"

这里字符串<<<用于字符串拆分:

bash 复制代码
read packets _ _ time _ <<< "20 packets in 10 seconds"
echo "数据包: $packets, 时间: $time"

Bash手册解释,read -a可将字段存入数组:IFS=' ' read -ra fields <<< "$str"

7. 获取文件大小到变量

bash 复制代码
size=$(wc -c < file.txt)

wc -c计数字节,< file.txt重定向输入,避免输出文件名。结果如1024,不含文件名。

8. 从路径提取文件名

bash 复制代码
path="/dir/sub/file.txt"
filename=${path##*/}
echo "$filename"  # 输出: file.txt

参数扩展${var##pattern}移除开头最长匹配的*/(最后一个斜杠)。

9. 从路径提取目录名

bash 复制代码
dirname=${path%/*}
echo "$dirname"  # 输出: /dir/sub

${var%pattern}移除末尾最短匹配的/*

10. 快速复制文件

bash 复制代码
cp /path/to/file{,_copy}

大括号扩展{a,b}生成file file_copy。类似移动:

bash 复制代码
mv file{,_backup}

这些one-liners展示了Bash的简洁性:重定向、参数扩展和扩展让文件操作如行云流水。实践这些,你会发现外部工具如cp在简单场景下可被内置替换。

Part II: 字符串处理

字符串是Bash的核心数据类型。Part II聚焦于生成、连接、拆分和替换,使用printfread和参数扩展。Bash手册的"参数扩展"部分是这些技巧的基础。

1. 生成a-z字母表

bash 复制代码
echo {a..z}

大括号序列{a..z}生成a到z,按字典序展开。输出:a b c ... z

2. 无空格的a-z字母表

bash 复制代码
printf "%c" {a..z}

printf "%c"打印字符,无换行。输出:abcdefghijklmnopqrstuvwxyz。添加换行:

bash 复制代码
printf "%c" {a..z} $'\n'

$'\n'是ANSI-C引述的换行。每个字母新行:

bash 复制代码
printf "%c\n" {a..z}

存入变量(Bash 4+):

bash 复制代码
printf -v alphabet "%c" {a..z}
echo "$alphabet"

数字序列:

bash 复制代码
echo {1..10}
# 或 seq 1 10

3. 填充0-9的前导零

bash 复制代码
printf "%02d " {0..9}

%02d零填充到2位整数。输出:00 01 ... 09。Bash 4+直接:

bash 复制代码
echo {00..09}

4. 生成30个英文单词

bash 复制代码
echo {w,t,}h{e{n{,ce{,forth}},re{,in,fore,with{,al}}},ither,at}

嵌套大括号生成排列,如whenwhence等。展示了扩展的组合威力。

5. 复制字符串10次

bash 复制代码
echo foo{,,,,,,,,,,}

10个空字符串与foo组合,生成10个foo

6. 连接两个字符串

bash 复制代码
x="Hello"; y=", World!"
echo "$x$y"  # 输出: Hello, World!

引号防止词拆分。无引号危险:

bash 复制代码
x="-n"; y=" foo"
echo $x$y  # 输出: foo(-n被echo视为选项)

赋值:var=$x$y(无需引号)。

7. 用给定字符拆分字符串

bash 复制代码
str="foo-bar-baz"
IFS=- read -r x y z <<< "$str"
echo "$x $y $z"  # foo bar baz

IFS=-设置分隔符,<<<是这里字符串。存入数组:

bash 复制代码
IFS=- read -ra parts <<< "$str"
echo "${parts[0]}"  # foo

8. 逐字符处理字符串

bash 复制代码
str="hello"
while IFS= read -rn1 c; do
    echo "$c"
done <<< "$str"

-rn1读取一个字符,-r原始模式。

9. 在字符串中替换"foo"为"bar"

bash 复制代码
str="foo foo bar"
echo ${str/foo/bar}  # 替换第一个: foo bar bar
echo ${str//foo/bar}  # 替换所有: bar bar bar

参数扩展${var/pattern/replacement}。Bash手册详述://全局替换。

10. 检查字符串匹配模式

bash 复制代码
file="archive.zip"
if [[ $file = *.zip ]]; then
    echo "是ZIP文件"
fi

[[ ]]支持glob:*任意字符串,?单字符,[a-z]范围。忽略大小写:

bash 复制代码
shopt -s nocasematch
if [[ $answer = [Yy]* ]]; then
    echo "是"
fi

11. 检查字符串匹配正则表达式

bash 复制代码
str="1.23"
if [[ $str =~ [0-9]+\.[0-9]+ ]]; then
    echo "是数字"
fi

=~是正则运算符,匹配man 3 regex语法。捕获组在BASH_REMATCH数组。

12. 获取字符串长度

bash 复制代码
str="hello"
echo ${#str}  # 5

${#var}返回长度。

13. 从字符串提取子串

bash 复制代码
str="hello world"
echo ${str:6}  # world(从索引6开始,0基)
echo ${str:7:2}  # or(从7取2字符)

负偏移从末尾:${str: -5}world

14. 大写字符串

Bash 4+:

bash 复制代码
str="foo bar"
echo ${str^^}  # FOO BAR

declare -u var; var="$str"; echo "$var"

15. 小写字符串

bash 复制代码
echo ${str,,}  # foo bar

declare -l var

这些字符串技巧利用Bash的内置扩展,避免了sedawk。在脚本中,参数扩展比外部工具更快。

Part III: 重定向

重定向是Bash的I/O心脏,管理文件描述符(FD:0=stdin, 1=stdout, 2=stderr)。Part III详解从基本到高级的重定向,Bash手册的"重定向"章节是权威来源。

1. 重定向stdout到文件

bash 复制代码
ls > list.txt

>截断文件,等价于1> list.txt。FD 1指向文件。

2. 重定向stderr到文件

bash 复制代码
ls nonexist 2> errors.log

2> errors.log捕获错误。

3. 重定向stdout和stderr到文件

bash 复制代码
ls nonexist &> output.log

&>合并,等价于> output.log 2>&1。顺序重要:2>&1 > file只重定向stdout。

4. 丢弃输出

bash 复制代码
ls nonexist > /dev/null 2>&1

/dev/null吞噬数据。或&> /dev/null

5. 从文件重定向到stdin

bash 复制代码
read line < input.txt

FD 0指向文件。

6. 通过here-document重定向多行文本

bash 复制代码
cat <<EOF
行1
行2
EOF

直到EOF结束。缩进用<<- EOF(允许前导Tab)。

7. 通过here-string重定向单行

bash 复制代码
read line <<< "单行输入"

等价于echo "单行" | read line,但更高效。

8. 使用exec全局重定向stderr

bash 复制代码
exec 2> errors.log
ls nonexist  # 错误到log
exec 2>&1    # 恢复

exec持久化重定向。

9. 用自定义FD打开文件读取

bash 复制代码
exec 3< input.txt
read line <&3
exec 3<&-  # 关闭

FD 3从文件读取。

10. 用自定义FD打开文件写入

bash 复制代码
exec 4> output.txt
echo "数据" >&4
exec 4>&-

11. 双向打开文件

bash 复制代码
exec 5<> file.txt
echo "追加" >&5
read line <&5
exec 5>&-

<>允许读写。

12. 多命令输出到文件

bash 复制代码
(ls; pwd) > combined.txt

子壳( )分组。

13. 通过命名管道(FIFO)执行命令

bash 复制代码
mkfifo mypipe
exec < mypipe  # Shell 1
exec 3> mypipe; echo "ls" >&3  # Shell 2

FIFO在文件系统中创建管道。

14. 通过/dev/tcp访问网站

bash 复制代码
exec 3<>/dev/tcp/www.example.com/80
echo -e "GET / HTTP/1.1\r\n\r\n" >&3
cat <&3
exec 3>&-

Bash扩展,支持TCP/UDP。

15. 防止覆盖文件(noclobber)

bash 复制代码
set -o noclobber
ls > existing.txt  # 错误
ls >| existing.txt  # 强制

16. 复制输出到文件和stdout

bash 复制代码
ls | tee list.txt

tee分流。

17. 管道stdout

bash 复制代码
ls | grep txt

标准管道。

18. 管道stdout和stderr

bash 复制代码
ls nonexist |& grep error  # Bash 4+
# 或 ls nonexist 2>&1 | grep error

19. 命名文件描述符(Bash 4.1+)

bash 复制代码
exec {log}> errors.log
echo "错误" >&$log
exec {log}>&-

{var}分配FD。

20. 重定向顺序灵活

bash 复制代码
echo hello > file.txt
# 等价于 echo > file.txt hello

21. 交换stdout和stderr

bash 复制代码
ls nonexist 3>&1 1>&2 2>&3 3>&-

临时FD 3交换1和2。

22. stdout和stderr到不同进程

bash 复制代码
ls > >(grep txt) 2> >(grep error >&2)

过程替换>(cmd)创建FIFO。

23. 获取管道所有命令的退出码

bash 复制代码
cmd1 | cmd2 | cmd3
echo ${PIPESTATUS[@]}  # [0, 0, 1] 等

PIPESTATUS数组记录每个管道的退出码。

重定向的威力在于FD管理:Bash从0-255支持FD,关闭未用FD避免泄漏。

Part IV: 历史记录

历史记录让Bash"记住"过去命令。Part IV介绍基本操作,Bash手册的"历史"部分详述变量如HISTSIZE

1. 擦除所有历史

bash 复制代码
rm ~/.bash_history

删除~/.bash_history。注销后,rm本身会被记录。

2. 停止本会话记录历史

bash 复制代码
unset HISTFILE
# 或 HISTFILE=/dev/null

HISTFILE控制文件。

3. 不记录当前命令

bash 复制代码
 command  # 以空格开头

设置HISTIGNORE="[ ]*"忽略空格开头。作者设置:HISTIGNORE="&:[ ]*"(忽略重复和空格)。

4. 更改历史文件

bash 复制代码
HISTFILE=~/my_history

后续命令保存到新文件。

5. 添加时间戳

bash 复制代码
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
history

strftime格式显示时间。

6. 显示历史

bash 复制代码
history

带编号显示所有命令。

7. 显示最近50条

bash 复制代码
history 50

8. 显示前10个最常用命令

bash 复制代码
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -10

管道分析频率(简化版)。

9. 执行上一个命令

bash 复制代码
!!

快速重跑,如sudo !!

10. 执行最近以字符串开头的命令

bash 复制代码
!ls

匹配最近ls命令。

11. 在编辑器中打开上一个命令

bash 复制代码
fc

fc(fix command)打开默认编辑器编辑并执行。

历史扩展!是Bash的杀手锏,详见下一扩展部分。

Part V: 导航技巧

Bash使用readline库支持Emacs-style编辑。Part V列出快捷键,提高命令行效率。

基本移动

  • Ctrl+a:行首
  • Ctrl+e:行尾
  • Ctrl+b / Ctrl+f:左/右一字符
  • Esc+b / Esc+f:左/右一词

删除与粘贴

  • Ctrl+w:删除前一词(杀掉,存kill ring)
  • Ctrl+y:粘贴杀掉的词
  • Ctrl+u:删除整行
  • Ctrl+h / Ctrl+d:删左/右字符
  • Ctrl+t:交换相邻字符
  • Esc+t:交换相邻词

历史搜索

  • Ctrl+r:反向搜索历史(输入部分命令)
  • Ctrl+s:正向搜索(需stty stop undef防冻结)

转换

  • Esc+u:词尾大写
  • Esc+l:词尾小写
  • Esc+c:词首大写

其他

  • Ctrl+v:插入原始字符(如Tab)
  • Esc+#:注释当前行
  • Ctrl+x Ctrl+e:在编辑器打开命令
  • Esc+.:插入上命令最后一个参数
  • Ctrl+l:清屏
  • Ctrl+x Ctrl+u:增量撤销

切换Vi模式:set -o vi

这些快捷键让命令行如IDE般流畅。

扩展: Bash命令行历史的权威指南

基于Catonmat的指南,深度探索历史。

键盘快捷键

Emacs:Ctrl+p/n上/下历史;Ctrl+r反搜。

Vi:Esc命令模式,k/j上/下;/搜索。

事件设计符

  • !!:上命令
  • !str:最近以str开头
  • !?str?:包含str
  • !n:第n条
  • ^old^new^:替换old为new

词设计符:!!:1第一参数;!!:$最后一个;!!:*所有参数。

修饰符::p打印不执行;:r移除后缀;:h头目录;:s/old/new/替换。

配置

变量:

  • HISTSIZE=1000:内存大小
  • HISTFILESIZE=2000:文件大小
  • HISTIGNORE="ls:cd:exit":忽略模式
  • HISTCONTROL=ignoreboth:忽略重复和空格

shopt:

  • shopt -s histappend:追加历史
  • shopt -s histverify:验证扩展

自定义提示:PS1='\u@\h:\w\!\$ '显示历史号。

管理

history -c清空;history -d n删第n条。

这个指南让历史成为生产力工具。

参考与最佳实践

从Bash手册:

  • 扩展:优先参数扩展而非外部命令。
  • 引号:始终双引号变量防拆分。
  • IFS :小心设置,默认为<space><tab>\n
  • shoptshopt -s extglob启用扩展glob。
  • 安全 :用[[ ]]测试,避免[的转义地狱。

最佳实践:

  • printf代替echo(更可移植)。
  • 管道用PIPESTATUS检查错误。
  • 历史用&忽略重复。
  • 测试:bash -n script.sh语法检查。
相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
Yana.nice2 小时前
Linux 只保留 30 天内日志(find命令删除日志文件)
linux·运维·chrome
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜6 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript