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语法检查。
相关推荐
Larry_Yanan1 小时前
Qt线程使用(一)直接继承QThread类
开发语言·c++·qt·ui
Yu_Lijing1 小时前
【个人项目】C++基于websocket的多用户网页五子棋(上)
开发语言·c++·websocket
脏脏a1 小时前
【初阶数据结构】栈与队列:定义、核心操作与代码解析
c语言·开发语言
济宁雪人1 小时前
Java安全基础——序列化/反序列化
java·开发语言
q***01771 小时前
Java进阶--IO流
java·开发语言
lsx2024061 小时前
C语言中的枚举(enum)
开发语言
csbysj20201 小时前
PHP Math
开发语言
小画家~1 小时前
第三十四:golang 原生 pgsql 对应操作
android·开发语言·golang
ulias2121 小时前
初步了解STL和string
开发语言·c++·mfc