$(())与$()还有${}的差别

bash shell 中的命令替换与变量操作
本文详细介绍了bash shell 中的命令替换与变量操作的相关知识,包括如何使用 $() 和 `` 进行命令替换,以及如何使用 ${}

在bash shell中,$()与``(反引号)都是用来做命令替换(command substitution)的。

*完成引号里的命令行,然后将其结果替换出阿里,再重组命令行。

例如:

$ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)

${}会比较精确的界定变量名称的范围。${}的一些特异功能如下例说明。

例:

假设定义一个变量为:

file=/dir1/dir2/dir3/my.file.txt

我们可以用${}分别替换获得不同值

${file#*/}:拿掉第一条/及其左边的字串:dir1/dir2/dir3/my.file.txt

${file##*/}:拿掉最后一条/及其左边的字串:my.file.txt

${file#*.}:拿掉第一个.及其左边的字串:file.txt

${file##*.}:拿掉最后一个.及其左边的字串:txt

${file%/*}:拿掉最后一条/及其右边的字串:/dir1/dir2/dir3

${file%%/*}:拿掉第一条/及其右边的字串:(空值)

${file%.*}:拿掉最后一个.及其右边的字串:/dir1/dir2/dir3/my.file

${file%%.*}:拿掉第一个.及其右边的字串:/dir1/dir2/dir3/my

记忆的方法为:

#是去掉左边(在键盘上#在$的左边)

%是去掉右边(在键盘上%在$的右边)

单一符号是最小匹配;两给符号是最大匹配。


${file:0:5}:提取最左边的5个字节:/dir1

${file:5:5}:提取第5个字节右边的连续5个字节:/dir2


我们一颗一对变量值里的字串作替换:

${file/dir/path}:将第一个dir替换为path:/path1/dir2/dir3/my.file.txt

${file//dir/path}:将全部dir替换为path:/path1/path2/path3/my.file.txt


利用${}还可以针对不同的变数状态复制(没设定、空值、非空值、):
${file-my.file.txt}:假如$file没有设定,则使用my.file.txt作传回值。(空值及非空值时不作处理)

${file:-my.file.txt}:假如$file没有设定或为空值,则使用my.file.txt作传回值。(非空值不作处理)

${file+my.file.txt}:假如$file为空值或非空值,均使用my.file.txt作传回值。(没设定时不作处理)

${file:+my.file.txt}:若$file为非空值,则使用my.file.txt作传回值。(没设定及空值时不作处理)

${file=my.file.txt}:若$file没设定,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt。(空值及非空值时不作处理)

${file:=my.file.txt}:若$file没设定或为空值,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt。(非空值时不作处理)

${file?my.file.txt}:若$file没设定,则将my.file.txt输出至STDERR。(空值及非空值不作处理)

${file:?my.file.txt}:若$file没设定或为空值,则将my.file.txt输出至STDERR。(非空值时不作处理)


以上的理解:一般而言,:与null有关,若不带:的话,null不受影响,若带:则连null也受影响。


${#var}:可计算出变量值的长度:

${#file}:可得到27,以为/dir1/dir2/dir3/my.file.txt刚好是27个字节


bash中的数组(array)的处理方法。

一般而言,A="a b c def"这样的变量知识将$A替换为一个单一的字串,

但是改为A=(a b c def),则是将$A定义为数组...

bash的数组替换方法可参考如下方法:

${A[@]} 或 ${A[*]}

可得到a b c def(全部元素)

${A[0]}

可得到a(第一个元素),${A[1]}则为第二个元素

${#A[@]}或${#A[*]}

可得到4(全部元素数量)

${#A[0]}

可得到1(即第一个元素(a)的长度),${#A[3]}可得到3(第四个元素(def)的长度)

A[3]=xyz

则是将第四个元素重新定义为xyz


$(())的用途:它是用来作整数运算的。

在bash中,$(())的整数原酸符号大致有如下这些:

+ - * /:分别为“加、减、乘、除“。

%:余数运算

& | ^ ! :分别为“AND、OR、XOR、NOT”运算。

$ a=5; b=7; c=2
$ echo $(( a+b*c ))
19
$ echo $(( (a+b)/c ))
6
$ echo $(( (a*b)%c))
1

在$(())中的变量名称,可在其前面加$符号來替换,也可以不用,如:

$(($a + $b * $c))也可得到19的结果

注:在$(())中,变量与运算符号之间是否有空格都是合法的。


此外,$(())还可以作不同进位(如二进制、八进制、十六进制)作运算,只是输出结果皆为十进制。

echo $((16#2a))结果为42(十六进制转为十进制)

例:假如当前的umask是022,那么新建文件的权限即为:

$ umask 022
$ echo "obase=8;$(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
644

事实上,单纯用(())也可以重定义变量值,或作testing:

a=5;((a++))可将$a重定义为6

a=5;((a--))则为a=4

a=5;b=7;((a<b))会得到0(true)的返回值

常见的用于(())的测试符号有如下这些:

<:小于

>:大于

<=:小于或等于

>=:大于或等于

==:等于

!=:不等于


注使用(())作整数测试时,不要跟[]的整数测试搞混乱了。

<think> 我们正在讨论Shell脚本中的特殊变量`$$`。根据引用内容,`$$`表示当前Shell脚本运行的进程ID(PID)。这是一个只读变量,用于获取当前脚本执行时的进程ID。 ### 核心知识点 1. **基本含义**:`$$` 表示当前Shell脚本的进程ID(PID),每次运行脚本时都会生成一个唯一的PID。 2. **使用场景**: - 创建临时文件或目录时,为避免文件名冲突,常用`$$`生成唯一后缀。 - 调试脚本时,可通过PID跟踪脚本的执行情况。 - 在后台运行脚本时,可通过`$$`获取进程ID以便后续操作(如终止进程)。 3. **特性**:`$$`的值在脚本启动时确定,在整个脚本执行过程中保持不变。 ### 示例演示 创建一个脚本`test_pid.sh`: ```bash #!/bin/bash echo "当前脚本的进程ID (PID) 是: $$" # 创建临时文件 TMP_FILE="/tmp/tempfile_$$.txt" echo "这是一个临时文件" > $TMP_FILE echo "临时文件已创建: $TMP_FILE" # 显示临时文件内容 cat $TMP_FILE # 清理(可选) # rm -f $TMP_FILE ``` 运行脚本: ```bash $ chmod +x test_pid.sh $ ./test_pid.sh ``` 输出示例: ``` 当前脚本的进程ID (PID) 是: 12345 临时文件已创建: /tmp/tempfile_12345.txt 这是一个临时文件 ``` ### 重要注意事项 - **子Shell中的`$$`**:在子Shell(如`( )`或`bash -c`)中,`$$`会继承父Shell的PID,而非子Shell自身的PID。如需获取子Shell的PID,可使用`$BASHPID`(Bash特有): ```bash echo "父Shell PID: $$" ( echo "子Shell继承的PID: $$"; echo "子Shell实际PID: $BASHPID" ) ``` ### 其他特殊变量对比 | 变量 | 含义 | 示例用法 | |------|-------------------------------|------------------------------| | `$$` | 当前脚本的进程ID | `echo "PID: $$"` | | `$!` | 最后一个后台命令的进程ID | `nohup command & echo $!` | | `$?` | 上一条命令的退出状态码 | `if [ $? -eq 0 ]; then ...` | | `$#` | 脚本参数的个数 | `if [ $# -lt 2 ]; then ...` | ### 常见问题解答 **Q1: 如何确保临时文件的唯一性?** > 仅用`$$`可能不足(同一脚本多次运行PID可能重复),可结合`date`或`mktemp`命令: > ```bash > TMP_FILE="/tmp/$(date +%s)_$$.txt" # 时间戳+PID > # 或使用系统工具 > TMP_FILE=$(mktemp /tmp/tempfile_XXXXXX.txt) > ``` **Q2: 如何终止当前脚本进程?** > 使用`kill -9 $$`可在脚本内部终止自身(谨慎使用): > ```bash > echo "即将终止脚本..." > kill -9 $$ > ``` **Q3: 子Shell中如何获取真实PID?** > 在Bash中,优先使用`$BASHPID`获取子Shell的真实PID: > ```bash > ( sleep 10 & echo "子Shell后台进程PID: $BASHPID" ) > ``` ### 相关应用场景 1. **进程管理**:编写守护进程脚本时,通过`$$`记录PID以便后续控制。 2. **日志跟踪**:在日志中记录PID,便于多进程环境下的问题排查。 3. **资源隔离**:使用PID命名管道(FIFO)、共享内存等资源。 §§ 相关问题 §§ 1. Shell中`$!`和`$$`有什么区别? 2. 如何获取子Shell的真实进程ID? 3. 为什么临时文件命名推荐使用`mktemp`而不是`$$`? </think> ### Shell 中 `$$` 的含义和用法详解 在 Shell 脚本中,**`$$` 表示当前 Shell 进程的进程 ID (PID)**。这是一个特殊的只读变量,用于获取当前正在运行的脚本或命令的进程标识符。以下是详细解析: #### 一、核心特性 | **特性** | **说明** | |-----------------|--------------------------------------------------------------------------| | **只读变量** | 值由系统自动分配,脚本无法修改 | | **实时获取** | 每次执行脚本时都会生成新的 PID | | **全局唯一性** | 同一时刻系统中每个进程的 PID 唯一 | | **会话绑定** | 子进程会继承父进程的 PID(需区分 `$$` 和 `$BASHPID`)[^5] | #### 二、典型用法示例 ##### 1. 获取当前脚本 PID ```bash #!/bin/bash echo "当前脚本进程 ID: $$" # 输出如:当前脚本进程 ID: 12345 ``` ##### 2. 创建唯一临时文件 ```bash temp_file="/tmp/data_$$.tmp" # 生成如 /tmp/data_12345.tmp echo "内容" > "$temp_file" ``` ##### 3. 进程管理 ```bash #!/bin/bash # 记录自身 PID echo $$ > /var/run/myscript.pid # 后续可通过该 PID 管理进程 # kill -9 $(cat /var/run/myscript.pid) ``` ##### 4. 调试多进程脚本 ```bash #!/bin/bash echo "父进程 PID: $$" ( echo "子进程 PID: $$ (继承父进程)" # 注意:子Shell中 $$ 不变 echo "真实子进程 PID: $BASHPID" # 需用 $BASHPID 获取实际 PID ) ``` #### 三、技术细节解析 1. **PID 分配机制** - 由内核在进程创建时分配 - 取值范围:`1 ~ 32768`(可通过 `sysctl kernel.pid_max` 查看上限) - 回收机制:进程结束后 PID 可重用 2. **父子进程差异** ```bash # 父进程脚本 echo "父进程 PID: $$" bash -c 'echo "子进程 PID: $$"' # 子进程继承 $$ ``` 3. **相关变量对比** | **变量** | **含义** | **示例** | |----------|---------------------------|----------------| | `$$` | 当前 Shell 的 PID | `12345` | | `$!` | 最后后台进程的 PID | `12346` | | `$BASHPID` | 当前 Bash 进程的真实 PID | `12347` (子进程) | | `$PPID` | 父进程 PID | `12344` | #### 四、实际应用场景 1. **进程互斥锁** ```bash LOCKFILE="/tmp/myapp.lock" if [ -e $LOCKFILE ]; then echo "另一个实例正在运行(PID: $(cat $LOCKFILE))" exit 1 fi echo $$ > $LOCKFILE ``` 2. **日志追踪** ```bash log() { echo "[$$ $(date '+%F %T')] $@" >> /var/log/app.log } log "服务启动" ``` 3. **临时资源管理** ```bash WORK_DIR=$(mktemp -d /tmp/workdir_$$_XXX) # 创建唯一目录 trap "rm -rf $WORK_DIR" EXIT # 退出时自动清理 ``` #### 五、注意事项 1. **子进程陷阱** ```bash # ❌ 错误:子Shell继承相同 $$ ( echo "Subshell PID: $$" ) # ✅ 正确:使用 $BASHPID ( echo "真实 PID: $BASHPID" ) ``` 2. **PID 重用风险** - 长时间运行的脚本不应依赖 PID 作为唯一标识 - 建议组合使用时间戳:`id_$(date +%s)_$$` 3. **系统限制** - PID 最大值:`/proc/sys/kernel/pid_max` - 查看进程信息:`ps -p $$ -o pid,ppid,cmd` --- ### 相关问题 1. 如何在 Shell 脚本中获取子进程的真实 PID? 2. `$$` 和 `$BASHPID` 在什么场景下会显示不同的值? 3. 如何防止 Shell 脚本被重复运行(实现单例模式)? 4. 除了 `$$`,还有哪些特殊变量可以用于进程管理?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值