基本语法
Shebang
注释
变量
- 变量名不能包含空格,等号两边也不能有空格
- 引用变量使用
$
符号
|
NAME="world" echo "Hello, $NAME" |
- 使用
readonly
命令将变量设置为只读
- 使用
unset
删除变量
字符串
- 单引号内的内容原样输出,变量不会被解析
- 双引号内的内容会解析变量和转义字符
|
NAME="John" GREETING="Hello, $NAME" |
- 获取字符串长度
- 字符串拼接
|
FULL_GREETING="Hello, "$NAME"!" |
数组
- 定义数组并访问数组元素
|
ARR=(element1 element2 element3) echo ${ARR[0]} # 输出第一个元素 |
- 获取数组长度
条件判断
|
if [ $NAME == "world" ]; then echo "Hello, world" else echo "Hello, someone else" fi |
|
if [ $a > $b ]; then echo "b" elif [ $a < $b ]; then echo "b" else echo "b" fi |
case
|
case $VAR in "1") echo "One" ;; "2") echo "Two" ;; *) echo "Other" ;; esac |
循环
for
|
for i in 1 2 3 4 5; do echo "Number $i" done |
while
|
COUNT=0 while [ $COUNT -lt 5 ]; do echo "Count is $COUNT" COUNT=$((COUNT + 1)) done |
until
|
COUNT=0 until [ $COUNT -ge 5 ]; do echo "Count is $COUNT" COUNT=$((COUNT + 1)) done |
函数
|
function greet() { echo "Hello, $1" } greet "world" |
输入和输出
- 使用
read
命令读取用户输入,使用echo
输出
|
read -p "Enter your name: " USER_NAME echo "Hello, $USER_NAME" |
重定向和管道
- 使用
>
和>>
进行输出重定向,使用
进行管道操作
|
echo "This is a test" > testfile.txt # 覆盖写入 echo "Appending a line" >> testfile.txt # 追加写入 cat testfile.txt | grep "test" |
运算符
- 使用
expr
进行算术运算
- 或者使用双括号
(( ))
- 关系运算符用于比较两个数
|
[ $a = $b ] # 相等 [ $a != $b ] # 不相等 [ $a > $b ] # 大于 [ $a < $b ] # 小于 [ $a >= $b ] # 大于等于 [ $a <= $b ] # 小于等于 |
- 逻辑运算符用于逻辑判断
|
[ $a < 20 -a $b > 100 ] # AND [ $a < 20 -o $b > 100 ] # OR |
进阶内容1
更复杂的变量处理
- 参数替换用于处理变量的默认值、替换值等
|
VAR=${FOO:-"default"} # 如果FOO未定义,则使用"default" VAR=${FOO:="default"} # 如果FOO未定义,则赋值为"default" VAR=${FOO:+$BAR} # 如果FOO已定义,则使用BAR的值 VAR=${FOO:? "Error: Variable FOO is not set"} # 如果FOO未定义,则打印错误信息并退出 |
- 对字符串进行子串提取和替换
|
STR="Hello, world" echo ${STR:7} # 输出"world" echo ${STR:7:5} # 输出"world" echo ${STR/Hello/Hi} # 输出"Hi, world" |
高级控制结构
- 使用
select
创建菜单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
PS3="Please select an option: " select option in "Option 1" "Option 2" "Option 3"; do case $option in "Option 1") echo "You selected Option 1" ;; "Option 2") echo "You selected Option 2" ;; "Option 3") echo "You selected Option 3" ;; *) echo "Invalid option" ;; esac done |
- 复合命令
|
{ echo "Line 1" echo "Line 2" echo "Line 3" } > output.txt |
错误处理
- 使用
$?
检查上一个命令的退出状态
|
cp source.txt destination.txt if [ $? -eq 0 ]; then echo "Copy succeeded" else echo "Copy failed" fi |
trap
用于捕获和处理信号
|
trap 'echo "Caught SIGINT"' SIGINT trap 'echo "Caught SIGTERM"' SIGTERM |
- 使用
set
命令开启错误处理选项
|
set -e # 如果命令失败,则退出脚本 set -u # 如果使用未定义变量,则退出脚本 set -o pipefail # 如果管道中的任何命令失败,则退出脚本 |
调试技巧
- 开启和关闭脚本的调试模式
|
set -x # 开启调试模式,显示每一行命令及其结果 # Your script here set +x # 关闭调试模式 |
- 在脚本中添加
echo
语句来打印变量值和调试信息
|
echo "Debug: Variable VAR is $VAR" |
信号处理
trap
命令用于捕获和处理信号
|
trap 'echo "Script interrupted"; exit' INT TERM # 模拟长时间运行的任务 while true; do sleep 1 done |
子进程和后台作业
- 使用
&
将命令放入后台执行
- 使用
wait
等待后台作业完成
|
long_running_task & PID=$! wait $PID echo "Background task finished" |
性能优化
- 尽量减少使用外部命令以减少子进程的开销
|
# 尽量使用内置命令而非外部命令 for i in {1..1000}; do : # 内置命令,效率高 done # 避免使用外部命令如`expr`、`grep`等 |
- 批量处理数据以减少重复的IO操作
|
# 使用xargs批量处理 cat file.txt | xargs -n 1 -P 4 your_command |
进阶内容2
正则表达式
- 正则表达式在
Shell
中通常用于模式匹配
- 常用的工具有
grep
、sed
和awk
|
# 搜索包含“pattern”的行 grep "pattern" file.txt # 递归搜索目录中包含“pattern”的文件 grep -r "pattern" /path/to/directory |
|
# 替换文件中第一次出现的“old”字符串为“new” sed 's/old/new/' file.txt # 替换文件中所有出现的“old”字符串为“new” sed 's/old/new/g' file.txt |
|
# 打印文件的第二列 awk '{print $2}' file.txt # 计算文件中第二列的总和 awk '{sum += $2} END {print sum}' file.txt |
命令替换
- 命令替换用于将命令的输出作为变量或参数的一部分
- 使用反引号
|
DATE=`date` echo "Current date is $DATE" |
- 使用
$()
|
DATE=$(date) echo "Current date is $DATE" |
环境变量管理
- 环境变量在
Shell
脚本中用于配置和传递信息
- 设置和导出环境变量
- 从环境变量中读取
- 查看所有环境变量
文件描述符操作
- 文件描述符用于管理输入输出流
- 重定向标准输出和标准错误
|
# 将标准输出重定向到文件 command > file.txt # 将标准错误重定向到文件 command 2> error.txt # 将标准输出和标准错误都重定向到文件 command > file.txt 2>&1 |
- 使用文件描述符
|
# 打开文件描述符3用于读写 exec 3<> file.txt # 通过文件描述符3写入文件 echo "Hello, World!" >&3 # 关闭文件描述符3 exec 3>&- |
流程控制
break
和continue
|
# 使用break退出循环 for i in {1..10}; do if [ $i -eq 5 ]; then break fi echo $i done # 使用continue跳过当前循环 for i in {1..10}; do if [ $i -eq 5 ]; then continue fi echo $i done |
eval
用于执行构造的命令
复杂数据处理
- 使用
awk
进行字段处理
|
# 打印第二列和第四列 awk '{print 4}' file.txt |
- 使用
sed
进行多行处理
|
# 删除文件中空行 sed '/^$/d' file.txt |
动态参数处理
- 使用
shift
命令处理脚本中的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
while [ $# -gt 0 ]; do case $1 in -f|--file) FILE=$2 shift 2 ;; -v|--verbose) VERBOSE=1 shift ;; --) shift break ;; *) echo "Unknown option: $1" exit 1 ;; esac done |
学习理解
数据类型相关
- 在
Shell
脚本中,所有变量和数组元素实际上都是字符串
- 即使看起来是整数或其他类型,它们在内部仍然作为字符串处理
- 换句话说,
Shell
脚本中的变量没有明确的类型声明
所以你可以在数组中混合字符串和看起来像整数的值
|
#!/bin/bash # 定义一个混合数组 ARR=("string" 123 "another string" 456) # 输出数组的各个元素 echo "Element 0: ${ARR[0]}" # 输出 "string" echo "Element 1: ${ARR[1]}" # 输出 "123" echo "Element 2: ${ARR[2]}" # 输出 "another string" echo "Element 3: ${ARR[3]}" # 输出 "456" |
- 即使数组中的元素看起来像整数,它们在执行数值操作时仍然需要显式转换
- 例如,使用
$(( ))
来进行算术运算
|
#!/bin/bash # 定义一个混合数组 ARR=("string" 123 "another string" 456) # 将数组中整数相加 SUM=$((ARR[1] + ARR[3])) echo "Sum of integers: $SUM" # 输出 "579" |
- 虽然
Shell
本身不支持类型检查,但你可以使用一些技巧来判断一个变量是否是整数
- 例如,使用正则表达式匹配
|
#!/bin/bash # 定义一个混合数组 ARR=("string" 123 "another string" 456) # 检查数组元素是否是整数 for element in "${ARR[@]}"; do if [[ $element =~ ^[0-9]+$ ]]; then echo "$element is an integer" else echo "$element is a string" fi done |
信号捕获相关
- 通过设置
trap
命令,可以在信号到达时执行指定的命令或动作,而不是终止脚本的默认行为
commands
:指定当捕获到信号时要执行的命令
signal
:指定要捕获的信号名称或编号
- 捕获信号的机制
Shell
会在执行脚本的过程中,检查是否收到了指定的信号
- 如果收到了信号,
Shell
会暂停当前的操作,执行trap
命令中指定的命令,然后继续执行剩余的脚本
- 这是由
Shell
的内部实现机制决定的
关于环境变量
- 局部变量在当前
Shell
会话或脚本中有效,通常在脚本结束时自动销毁
- 这个变量在当前脚本或会话中有效,但不会影响其他脚本或
Shell
会话
|
#!/bin/bash LOCAL_VAR="I am local" echo "Local variable: $LOCAL_VAR" |
- 全局环境变量在所有子
Shell
会话中有效
- 要设置全局环境变量,可以使用
export
命令
- 通过
export
命令将变量导出,使其在当前Shell
会话及其子进程中都可见
|
#!/bin/bash # 设置全局环境变量 export GLOBAL_VAR="I am global" echo "Global variable: $GLOBAL_VAR" |
- 设置全局环境变量影响其他
Shell
脚本
- 如果希望设置的环境变量在整个系统或所有终端会话中都有效,可以将变量定义添加到用户的配置文件中
- 比如
~/.bashrc
或~/.bash_profile
中
|
# 编辑用户的~/.bashrc文件,并添加以下内容: export GLOBAL_VAR="I am globally available" |
|
# 保存并关闭文件后,运行以下命令使更改生效 source ~/.bashrc |
关于动态参数处理
$#
是一个特殊变量,表示传递给脚本的参数数量
$1
表示当前第一个参数
- 从参数列表中第一个
-f|--file)
- 这是一个模式,匹配参数
-f
或--file
-f
:短选项,表示文件
--file
:长选项,表示文件
FILE=$2
- 将下一个参数(即文件名)赋值给变量
FILE
- 从参数列表中第二个
shift 2
- 移动参数位置,将前两个参数从列表中移除
-v|--verbose)
-v
:短选项,表示详细模式
--verbose
:长选项,表示详细模式
VERBOSE=1
- 将
1
赋值给变量VERBOSE
,表示启用详细模式
shift
- 没有参数的
shift
默认移动一个位置
--)
- 这是一个特殊模式,匹配双破折号
--
,表示参数的结束
--
:表示参数列表的结束,后续参数不会再作为选项处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
while [ $# -gt 0 ]; do case $1 in -f|--file) FILE=$2 shift 2 ;; -v|--verbose) VERBOSE=1 shift ;; --) shift break ;; *) echo "Unknown option: $1" exit 1 ;; esac done |
grep
概述
- 一个强大的命令行工具,用于在文件或输入流中搜索匹配特定模式的文本行
grep
的强大之处在于它对正则表达式的支持
基本用法
|
# 在 file.txt 中搜索包含 "hello" 的行 grep "hello" file.txt # 从标准输入读取数据并搜索包含 "hello" 的行 echo -e "hello\nworld" | grep "hello" |
常用选项
-i
:忽略大小写
-v
:反转匹配,显示不包含模式的行
-w
:匹配整个单词
-x
:匹配整行
|
# 在 file.txt 中搜索包含 "hello" 的行,忽略大小写 grep -i "hello" file.txt # 在 file.txt 中搜索不包含 "hello" 的行 grep -v "hello" file.txt # 在 file.txt 中搜索包含整个单词 "hello" 的行 grep -w "hello" file.txt # 在 file.txt 中搜索完全匹配 "hello" 的行 grep -x "hello" file.txt |
输出选项
-n
:显示匹配行的行号
-l
:只显示包含匹配模式的文件名
-c
:只显示匹配的行数
-o
:只显示匹配的部分
|
# 在 file.txt 中搜索包含 "hello" 的行,并显示行号 grep -n "hello" file.txt # 显示包含 "hello" 的文件名 grep -l "hello" file.txt # 显示包含 "hello" 的行数 grep -c "hello" file.txt # 只显示匹配的部分 echo "hello world" | grep -o "hello" |
扩展选项
-E
:使用扩展的正则表达式(等价于 egrep
)
-F
:使用固定字符串匹配(等价于 fgrep
)
-P
:使用Perl兼容的正则表达式
|
# 使用扩展的正则表达式匹配 grep -E "hello|world" file.txt # 使用固定字符串匹配 grep -F "hello" file.txt # 使用Perl兼容的正则表达式匹配 grep -P "\bhello\b" file.txt |
正则表达式
.
:匹配任意单个字符
*
:匹配零个或多个前面的字符
^
:匹配行的开头
$
:匹配行的结尾
[ ]
:匹配括号内的任意一个字符
( )
:分组,用于匹配子模式
\
:或操作符,用于匹配任意一个模式
|
# 匹配以 "hello" 开头的行 grep "^hello" file.txt # 匹配以 "world" 结尾的行 grep "world$" file.txt # 匹配包含任意一个数字的行 grep "[0-9]" file.txt # 匹配 "hello" 或 "world" grep "hello\|world" file.txt |
实战示例
- 查找包含特定单词的文件
|
# 查找当前目录及其子目录中所有包含 "TODO" 的文件 grep -r "TODO" . |
- 统计包含特定模式的行数
|
# 统计 file.txt 中包含 "error" 的行数 grep -c "error" file.txt |
- 提取特定模式的行
|
# 从 system.log 中提取包含 "ERROR" 的行,并显示行号 grep -n "ERROR" system.log |
- 组合使用
- 可以将
grep
与其他命令组合使用,如 sort
、uniq
等
|
# 从 log.txt 中提取包含 "ERROR" 的行,去重后按行数排序 grep "ERROR" log.txt | sort | uniq -c | sort -nr |
sed
概述
- 一个强大的流编辑器,用于对文本进行非交互式的编辑和处理
- 它常用于替换文本、删除文本、插入文本以及执行复杂的文本操作
基本用法
替换文本
- 替换命令是
sed
最常用的命令之一,使用 s
进行替换
|
sed 's/pattern/replacement/' file.txt |
|
# 将 file.txt 中的第一个 "hello" 替换为 "world" sed 's/hello/world/' file.txt # 将 file.txt 中所有的 "hello" 替换为 "world" sed 's/hello/world/g' file.txt # 只替换每行中的第一个 "hello" sed 's/hello/world/1' file.txt # 只替换每行中的第二个 "hello" sed 's/hello/world/2' file.txt |
删除文本
- 删除命令是
d
,用于删除匹配的行
|
# 删除 file.txt 中所有包含 "hello" 的行 sed '/hello/d' file.txt # 删除 file.txt 中的第一行 sed '1d' file.txt # 删除 file.txt 中的第2到第5行 sed '2,5d' file.txt |
插入和追加文本
- 插入命令是
i
,追加命令是 a
|
# 在匹配行之前插入文本 sed '/pattern/i\text' file.txt # 在匹配行之后追加文本 sed '/pattern/a\text' file.txt |
|
# 在 file.txt 中包含 "hello" 的行之前插入 "START" sed '/hello/i\START' file.txt # 在 file.txt 中包含 "hello" 的行之后追加 "END" sed '/hello/a\END' file.txt |
组合命令
- 可以使用
-e
选项组合多个命令
|
sed -e 's/hello/world/' -e 's/foo/bar/' file.txt |
- 或者将命令放在脚本文件中,然后使用
-f
选项
|
# 命令脚本 commands.sed s/hello/world/ s/foo/bar/ # 使用命令脚本 sed -f commands.sed file.txt |
地址范围
sed
命令可以作用于特定的行范围或模式范围
|
# 替换第2到第4行中的 "hello" 为 "world" sed '2,4s/hello/world/' file.txt # 替换从匹配 "start" 到匹配 "end" 范围内的 "hello" 为 "world" sed '/start/,/end/s/hello/world/' file.txt |
高级用法
sed
有两个工作空间:模式空间和保留空间
- 可以使用保留空间进行复杂的文本处理
|
# 将当前行复制到保留空间,删除模式空间中的内容,再次从保留空间读取 sed 'h;d;g' file.txt |
- 使用不同的分隔符代替斜杠
|
# 将路径 "/usr/local" 替换为 "/opt/local" sed 's_/usr/local_/opt/local_' file.txt |
示例
- 替换字符串
|
# 将 file.txt 中所有 "foo" 替换为 "bar" sed 's/foo/bar/g' file.txt |
- 删除空行
|
# 删除 file.txt 中的所有空行 sed '/^$/d' file.txt |
- 在特定行之前插入文本
|
# 在包含 "pattern" 的行之前插入 "INSERTED" sed '/pattern/i\INSERTED' file.txt |
- 从文件中删除特定行范围
|
# 删除 file.txt 中的第10到第20行 sed '10,20d' file.txt |
- 替换特定行范围中的文本
|
# 替换 file.txt 中第3到第6行的 "foo" 为 "bar" sed '3,6s/foo/bar/g' file.txt |
- 从标准输入读取并替换文本
|
# 从标准输入读取文本并将 "foo" 替换为 "bar" echo "foo is here" | sed 's/foo/bar/' |
awk
概述
- 是一个功能强大的文本处理工具,用于在命令行或脚本中对文本文件进行格式化、提取和处理
awk
的强大之处在于它能够基于模式匹配和特定的字段操作来处理数据
基本用法
|
John Doe 30 Jane Smith 25 Sam Brown 22 |
- 打印文件内容
{ print $0 }
:打印整行,$0
代表当前行的所有内容
|
awk '{ print $0 }' data.txt |
- 打印特定字段
{ print $1 }
:打印第一列,$1
代表当前行的第一个字段
|
awk '{ print $1 }' data.txt |
常用模式
- 匹配特定模式的行
/John/
:匹配包含 John
的行
|
awk '/John/ { print $0 }' data.txt |
- 匹配特定条件的行
$3 > 25
:匹配第三个字段大于 25 的行
|
awk '$3 > 25 { print $0 }' data.txt |
内置变量
NR
:当前记录的行号
NF
:当前记录的字段数
FS
:输入字段分隔符,默认为空格或制表符
OFS
:输出字段分隔符,默认为空格
RS
:输入记录分隔符,默认为换行符
ORS
:输出记录分隔符,默认为换行符
|
# 打印行号和内容 awk '{ print NR, $0 }' data.txt |
|
# 打印每行的字段数 awk '{ print NF, $0 }' data.txt |
字符串操作
- 连接字符串
|
awk '{ print 2 }' data.txt |
- 字符串长度
|
awk '{ print length($1) }' data.txt |
数值操作
- 计算每行的总和
|
awk '{ print 2 + $3 }' numbers.txt |
- 计算所有数的总和
|
awk '{ sum += 2 + $3 } END { print sum }' numbers.txt |
条件语句
if
if-else
|
awk '{ if (1; else print $2 }' data.txt |
循环
- 使用
for
循环遍历字段
|
awk '{ for (i = 1; i <= NF; i++) print $i }' data.txt |
自定义函数
- 在
awk
中定义并使用函数
|
awk ' function add(a, b) { return a + b } { print add(2) }' numbers.txt |
示例1
- 处理
CSV
文件
- 假设有一个 CSV 文件
data.csv
,内容如下
|
name,age,city John,30,New York Jane,25,Los Angeles Sam,22,Chicago |
- 打印每个人的名字和年龄
-F,
:指定字段分隔符为逗号
|
awk -F, '{ print 2 }' data.csv |
示例2
- 假设有一个文件
words.txt
,内容如下:
- 查找并打印最长的单词
|
awk '{ if (length($0) > max_length) { max_length = length($0) longest_word = $0 } } END { print longest_word }' words.txt |
gawk
概述
gawk
是 GNU 版本的 awk
,它扩展了标准 awk
的功能,提供了更多的内置函数和特性,使文本处理和数据分析更加强大
gawk
与 awk
的基本用法相同,但增加了许多高级特性
扩展的字符串函数
tolower(str)
:将字符串 str
转换为小写
toupper(str)
:将字符串 str
转换为大写
gsub(regex, replacement, [target])
:全局替换 target
中匹配 regex
的部分为 replacement
</li>
<li>
sub(regex, replacement, [target])
:替换 target
中首次匹配 regex
的部分为 replacement
sprintf(format, expression, ...)
:返回格式化字符串
|
echo "Hello World" | gawk '{ print tolower($0) }' echo "Hello World" | gawk '{ print toupper($0) }' echo "Hello 123 World" | gawk '{ gsub(/[0-9]+/, "number", $0); print }' |
扩展的数值函数
sin(x)
:返回 x
的正弦值
cos(x)
:返回 x
的余弦值
sqrt(x)
:返回 x
的平方根
exp(x)
:返回 e
的 x
次幂
log(x)
:返回 x
的自然对数
|
echo "9" | gawk '{ print sqrt($1) }' echo "2.71828" | gawk '{ print log($1) }' |
数据排序
- 在内存中对数据进行排序
|
echo -e "3\n1\n2" | gawk 'BEGIN { count = 0 } { array[count++] = $0 } END { PROCINFO["sorted_in"] = "@ind_num_asc"; for (i in array) print array[i] }' |
多维数组
- 支持关联数组和多维数组
|
echo -e "1 2\n3 4" | gawk '{ matrix[NR,1] = 2 } END { for (i=1; i<=NR; i++) print matrix[i,1], matrix[i,2] }' |
时间函数
systime()
:返回当前时间的时间戳
strftime(format, [timestamp])
:格式化时间戳
|
gawk 'BEGIN { print strftime("%Y-%m-%d %H:%M:%S", systime()) }' |
使用内置库
- 包含了一些内置库,可以通过
@include
关键字使用
|
gawk '@include "ordchr" BEGIN { print ord("A"); print chr(65) }' |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
Everything will be better.