shell 基础 $(cd `dirname $0`;pwd)

本文深入探讨了Shell脚本中的高级技巧,包括特殊变量用法、条件控制、循环结构、命令替换等内容,帮助读者提高脚本编写水平。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在命令行状态下单纯执行 $ cd `dirname $0` 是毫无意义的。因为他返回当前路径的"."。
这个命令写在脚本文件里才有作用,他返回这个脚本文件放置的目录,并可以根据这个目录来定位所要运行程序的相对位置(绝对位置除外)。
在/home/admin/test/下新建test.sh内容如下:
  1. cd `dirname $0`
  2. echo `pwd`
然后返回到/home/admin/执行
  1. sh test/test.sh
运行结果:
  1. /home/admin/test
这样就可以知道一些和脚本一起部署的文件的位置了,只要知道相对位置就可以根据这个目录来定位,而可以不用关心绝对位置。这样脚本的可移植性就提高了,扔到任何一台服务器,(如果是部署脚本)都可以执行。

PWD%/* shell变量的一些特殊用法

BASH使用基础

 

 

关于命令及命令类型

对于使用的命令BASH使用hash表,以加速下次的查找,为添加一个经常使用的命令,可以使用hash cmd
BASH在执行命令时对路径中找到的同名命令按以下的类型顺序执行:别名 keywords 函数 内置命令 可执行文件或脚本
BASH在执行用户输入的一条指令时,首先要判断命令的类型,可以使用type cmd来查看cmd是类型。
enable命令用来决定是否开启某个内置的命令,可以用enable -n cmd来禁用某个内置命令。
command命令用来消除别名和函数的查找。
builtin命令将只查找内置命令,而忽略函数和可执行文件。

 

作业控制:jobs %1 fg bg kill stop等
别名:alias dir='dir -l' unalisa dir
操作目录栈:dirs  pushd popd
文件名替换:dir d[1-3]  ls .bash{rc,profile}  支持[]对数字及{}对字符串的集合

 

变量
变量声明:declare -a 数组 -f 函数 -i 整数 -r 只读 -x 导出变量
本地变量只在其所声明的shell中有效,只读变量不能被修改,除非重新声明其属性
常见的环境变量:
BASH_VERSION DIRSTACK EUID EDITOR GROUPS HISTFILE HISTSIZE HOME LANG PWD OLDPWD PATH
PPID PS1-4 RANDOM SHELL UID 
export var = value -f 导出的变量为函数 -n 将全局转为局部变量 -p只打印导出的变量
非只读变量可以通过unset清除、

 

变量替换
${var:-word}  如果变量var已经设置且非空,结果为var的值,否则结果为word
${var:=word}  如果变量var已经设置且非空,结果为var的值,否则设置var为word
${var:+word}  如果变量var已经设置且非空,则设置var的值为word;否则不替换
${var:?word}  如果变量var已经设置且非空,则替换为word,否则退出shell。
${var:n}      替换为从n开始的子串
${var:n:len}  替换为从n开始长len的子串
变量扩展

${var%pattern}      去掉最小匹配的后缀   echo ${PWD%/*}   显示当前的父目录路径
${var%%pattern}     去掉最大匹配的后缀
${var#pattern}      去掉最小匹配的前缀   
${var##pattern}     去掉最大匹配的前缀   echo ${PWD##*/}  显示当前目录名称
${#var}             替换为变量字符个数
特殊变量
$ 当前SHELL的PID
? 前一个命令的退出状态
! 后台执行的上一个工作的PID

读取用户输入
read                        从终端读取输入存入内置变量REPLY
read var                  从终端读取输入存入变量var    
read first .. last       从终端读取多个变量依次存入,若输入较多,则最后一个变量成为包含多个值的字串
read -a array          从终端读取多个值存入数组中
read -p pmtstring var   向终端输出提示串,然后读入输入到var中
read -r line              读取一行,并允许/

变量类型转换
如果变量在声明时没确定类型,则变量根据赋值情况可以动态转换类型,但如果声明时变量类型已经确定,若赋值为其他类型,将导致原有数据丢失或出错。除非重新声明类型。
对于整数类型的变量,则在命令行支持算法扩展,如num=3*4,支持"",不支持空格。
进制

var=16#abc   var为16进制数abc

输出
printf "The number is %.2f/n" 100
echo -ne "hello/nworld/n"  -e 表示需要解析转义字符,-n 表示不自动添加换行符


位置参数
$0 1-0 ${10}
$#  求值位置参数个数
$*  求值所有位置参数
"$*"
$@
"$@"

 

引用
() 命令组,创建子SHELL执行
{} 命令组,不创建子SHELL
' ' 保护所有的元字符不被解析,想打印',必须放在双引号内,或者使用/转义
" " 只允许变量和命令替换,保护其余的元字符不被解析

 

命令替换
`cmd`
$(cmd)
二者结果都是一个字符串,如果加"",则保留换行,否则丢失换行。

 

数学表达式扩展
$[ exp ]
$(( exp ))

f

数组

declare -a array=(item1 item2 ...)
数组元素的引用 ${array[i]} 
引用整个数组 ${array[*]} 
unset array

 

函数定义
function f()
{
cmd;cmd;
}

 

I/O重定向
find . name /*.c print > foundit 2>&1

 

 

命令行参数

set可用来设置位置参数,使用set --将清除所有位置参数
$*与$@的区别只在于" "时,当$*放在""内时,参数表成为单个字符串,而$@放在""内时,每个参数都被引号括住。

 

表达式

 

评估表达式

expr $[3+4] $[ 3+4 ]  $(( 3+4 ))

let算术扩展
let i=i+1 支持任何C类型的运算符,但只支持整形数运算
bash不支持小数运行,因此需要在bc或者awk中进行相应的运行,再把结果回。由于bash没有浮点型,所以小数是以字符串表示。

测试表达式
测试可以使用test或者单纯的[ expr1 cmp expr2],结果保存于?变量中,即test与[]等价
字符串测试
[ str1 = str2 ]    str1与str2相同或==
[ str1 != str2 ]   st1与str2不相同
[ str ]            str非空
[ -z str ]         str的长度为0
[ -n str ]    str的长度非0
[ -l str ]     str的长度
[ str1 -a str2 ]   and 
       -o     or
!     not
复合条件测试
[[ p1 && p2 ]]     p1与p2均为真
[[ p1 || p2 ]]
[[ !p2 ]]
整数测试
[ num1 -eq num2 ]
[ numb1 -ne num2 ]
gt ge lt le 
文件属性测试
[ file1 nt file2 ]  文件1比文件2新
[ file1 ot file2 ]
[ file1 ef file2 ]
[-d file]           file为目录
-b  -c  -p -L -S    块文件,字符文件,管道,链接,socket
-e  -f              文件存在,常规文件
-G      文件存在且拥有有效GID
-O                  文件存在且拥有有效UID
-g       设置GID
-k                  设置stick位
-r -w -x            文件可读,可写,可执行的
-s                  文件大小非0
-t        文件fd在终端打开
-u                  setUID位设置

测试表达可以与let的运算扩展及(( ))中的C型运算扩展是等价的,后者也许更容易理解,C-like。

 

条件控制
if command
then
command
command
fi

 

if test expression
then
command
fi

 

if [ string/numeric expression ] then
command
fi

 

if [[ string expression ]] then
command
fi

 

if (( numeric expression ))


if command
then
command(s)
else
command(s)
fi

if command
then
command(s)
elif command
then
commands(s)
elif command
then
command(s)
else
command(s)
fi

 

空命令  :

 

分支跳转
case variable in 
value1)
command(s)
;;
value2)
command(s)
;;
*)
command(s)
;;
esac

 

循环
for variable in word_list
do
command(s)
done

while condition
do
command(s)
done

until command
do
     command(s)
done

 

构建菜单的select
select program in 'ls -F' pwd date
 do
 $program
 done

 

中断循环
break [n]     从第n层循环中跳出
continue [n]  继续第n层循环

 

捕获信号
信号列表

1) SIGHUP 9) SIGKILL 17) SIGCHLD 25) SIGXFSZ
2) SIGINT 10) SIGUSR1 18) SIGCONT 26) SIGVTALRM
3) SIGQUIT 11) SIGSEGV 19) SIGSTOP 27) SIGPROF
4) SIGILL 12) SIGUSR2 20) SIGTSTP 28) SIGWINCH
5) SIGTRAP 13) SIGPIPE 21) SIGTTIN 29) SIGIO
6) SIGABRT 14) SIGALRM 22) SIGTTOU 30) SIGPWR
7) SIGBUS 15) SIGTERM 23) SIGURG
8) SIGFPE 16) SIGSTKFLT 24) SIGXCPU
trap 'command; command' signal-number-list
trap 'command; command' signal-name-list  处理信号,当收到singal-list中的信号后,执行 ''中的命令
trap singal   重置信号处理函数
trap          列出已经设置信号处理

 

调试脚本
bash -x   script    显示命令执行过程,及结果
bash -v   script    显示脚本中的各行
bash -n   script    解释但不执行

 

附录:

常用命令
script myfile  将终端交互信息保存在myfile中,使用control+d退出
fuser -n tcp 22 获得打开tcp 22的进程
lsof            获得进程打开的文件

摘自:

http://hi.baidu.com/lianhuxu/blog/item/d059b8b569271ec337d3ca5a.html

http://hi.baidu.com/lixinxinhit/item/9d95758a39324756e63d1909

<think>我们注意到用户的问题是关于解释并运行一个特定的shell命令:`cd 'dirname $0'; pwd`。但是,根据引用内容,实际上常见的写法是`$(cd $(dirname $0); pwd)``cd \`dirname $0\`; pwd`。用户给出的命令中使用了单引号,这会导致命令执行不正确。 首先,我们需要理解这个命令的意图:获取当前执行脚本所在的绝对路径。 分解命令: 1. `$0`:代表当前脚本的文件名(可能包含相对路径或绝对路径)。 2. `dirname $0`:获取该文件名的目录部分。例如,如果脚本是`/home/user/scripts/test.sh`,那么`dirname $0`将输出`/home/user/scripts`;如果脚本是`./test.sh`,那么`dirname $0`将输出`.`(当前目录)。 3. `cd 'dirname $0'`:这里有一个问题,单引号内的内容会被视为字符串,而不是命令执行的结果。所以,如果直接运行`cd 'dirname $0'`,会尝试进入一个名为“dirname $0”的目录,这显然不是我们想要的。 正确的做法应该是使用反引号或$()来执行命令替换,将`dirname $0`的执行结果作为cd的目标目录。因此,常见的写法是: cd `dirname $0` # 使用反引号 或者 cd $(dirname $0) # 使用$() 然后执行pwd,显示切换后的当前工作目录。 所以,用户给出的命令`cd 'dirname $0'; pwd`是错误的,应该修改为: cd $(dirname $0); pwdcd `dirname $0`; pwd 为什么需要这样组合? 引用[3]中解释了:直接使用pwd获取的是当前工作目录,而脚本执行时的工作目录可能不是脚本所在的目录(比如在其他目录调用该脚本)。而使用`cd $(dirname $0); pwd`可以确保我们切换到脚本所在的目录,然后输出该目录的绝对路径。 执行步骤: 1. 先通过`dirname $0`得到脚本所在的目录(相对路径或绝对路径)。 2. 使用cd命令进入这个目录。 3. 然后使用pwd打印出当前工作目录,即脚本所在目录的绝对路径。 注意:这个命令组合通常用于在脚本中获取脚本所在目录的绝对路径,并赋值给一个变量,如: script_dir=$(cd $(dirname $0); pwd) 这样,变量`script_dir`就存储了脚本所在目录的绝对路径。 示例: 假设我们有一个脚本`/home/user/scripts/test.sh`,其内容为: #!/bin/bash script_dir=$(cd $(dirname $0); pwd) echo "Script directory: $script_dir" 无论当前工作目录在哪里,执行这个脚本,都会输出: Script directory: /home/user/scripts 但是,如果我们在其他目录执行,比如在`/tmp`下执行: /home/user/scripts/test.sh 那么: $0 的值是 `/home/user/scripts/test.sh` dirname $0 的结果是 `/home/user/scripts` cd 进入这个目录,然后pwd显示的就是`/home/user/scripts` 如果使用单引号,如用户所写的命令,那么: cd 'dirname $0' 会尝试进入一个名为"dirname $0"的目录(字面意思),而这个目录通常不存在,所以会报错。 因此,我们解释并运行正确的命令:`cd $(dirname $0); pwd` 如何运行? 我们可以写一个简单的shell脚本来测试: 步骤: 1. 创建一个测试脚本,比如test.sh,内容如下: #!/bin/bash echo "Original working directory: $(pwd)" cd $(dirname $0) echo "After cd, working directory: $(pwd)" # 或者使用命令组合:直接输出切换后的pwd echo "Using command substitution: $(cd $(dirname $0); pwd)" 2. 给脚本执行权限:chmod +x test.sh 3. 在脚本所在目录运行:./test.sh 4. 也可以在其他目录运行:/path/to/test.sh 但是注意:在脚本中,`cd $(dirname $0)`会改变脚本的当前工作目录(只影响脚本后续命令,不影响终端)。而`$(cd $(dirname $0); pwd)`只是在一个子shell中执行,所以不会改变脚本的工作目录,但可以获取到路径字符串。 因此,更常见的做法是使用变量保存路径: script_dir=$(cd $(dirname $0); pwd) 这样,脚本的后续命令可以继续使用这个绝对路径来访问同目录的其他文件。 总结: 用户问题中的命令`cd 'dirname $0'; pwd`是错误的,应该改为`cd $(dirname $0); pwd`(或使用反引号)。这个命令组合的作用是获取当前执行脚本所在的绝对路径。 我们写一个简单的例子来演示: 创建test.sh: #!/bin/bash echo "Step 1: current directory is $(pwd)" echo "Step 2: dirname \$0 is $(dirname $0)" cd $(dirname $0) echo "Step 3: after cd, current directory is $(pwd)" # 另一种方式:使用命令替换获取,不改变当前目录 original_dir=$(pwd) target_dir=$(cd $(dirname $0); pwd) echo "Step 4: we are back in original directory? $(pwd)" echo "The script's directory is: $target_dir" 执行: $ cd /tmp $ /path/to/test.sh 输出示例(假设脚本在/home/user/scripts/test.sh): Step 1: current directory is /tmp Step 2: dirname $0 is /home/user/scripts Step 3: after cd, current directory is /home/user/scripts Step 4: we are back in original directory? /home/user/scripts # 注意:上一步cd已经改变了当前目录,所以这里还是/home/user/scripts The script's directory is: /home/user/scripts 注意:在脚本中执行cd会改变后续命令的当前目录。如果我们不想改变脚本的当前工作目录,应该使用子shell的方式(即命令替换)来获取路径,然后将其保存到变量中,而不执行cd。实际上,在命令替换中执行cd,由于是在子shell中,所以不会影响父shell(即脚本主体)的当前目录。 修正上面的例子,避免改变当前目录: #!/bin/bash echo "Step 1: current directory is $(pwd)" target_dir=$(cd $(dirname $0); pwd) echo "Step 2: target_dir is $target_dir" echo "Step 3: current directory is still $(pwd)" 输出: Step 1: current directory is /tmp Step 2: target_dir is /home/user/scripts Step 3: current directory is still /tmp 因此,推荐使用`$(cd $(dirname $0); pwd)`来获取脚本所在目录的绝对路径,并赋值给变量,这样不会改变脚本的当前工作目录。 所以,最终我们解释并运行的是:`$(cd $(dirname $0); pwd)`(注意,这个命令本身是在一个子shell中执行,所以不会改变当前环境的工作目录)。 运行方法:在shell脚本中,将这个命令赋值给一个变量,如: script_dir=$(cd $(dirname $0); pwd) 然后就可以使用`$script_dir`来引用脚本所在目录的绝对路径了。 注意:如果脚本是从符号链接执行的,`dirname $0`可能不会得到真正的脚本所在目录。这时可以使用更复杂的方法,比如`dirname $(readlink -f $0)`,但这不是本次问题的重点。 根据引用[5],我们也可以看到实际应用:`shell_dir=$(cd \`dirname $0\`; pwd)` 因此,我们纠正用户的命令,并解释正确的用法。</think>### 解释并执行 Shell 命令:`cd 'dirname $0'; pwd` #### 命令解析(错误用法) 您提供的命令 `cd 'dirname $0'; pwd` **存在语法错误**,会导致执行失败: 1. **单引号问题**:`'dirname $0'` 中的单引号会使 Shell 将其视为普通字符串而非命令 2. **缺少命令替换**:没有使用反引号 `` ` ```$()` 执行命令替换 3. **错误效果**:实际会尝试进入名为 `"dirname $0"` 的目录(通常不存在) ```bash # 错误执行示例(会报错): $ cd 'dirname $0'; pwd -bash: cd: dirname $0: No such file or directory ``` --- #### 正确用法与解释 实际应使用命令替换语法,以下是两种等效的正确写法: ##### 方法 1:反引号语法 ```bash cd `dirname $0`; pwd ``` ##### 方法 2:$() 语法(推荐) ```bash cd $(dirname $0); pwd ``` #### 关键组件解析 | 命令/符号 | 作用 | 示例值(假设脚本路径为 `/home/user/scripts/test.sh`) | |----------------|----------------------------------------------------------------------|------------------------------------------------------| | `$0` | 当前执行的脚本路径 | `/home/user/scripts/test.sh` | | `dirname $0` | 提取脚本所在目录(不含文件名) | `/home/user/scripts` | | `$(...)` | 命令替换:执行括号内命令并替换为输出结果 | 替换为 `/home/user/scripts` | | `cd ...` | 切换到命令替换得到的目录 | 进入 `/home/user/scripts` | | `pwd` | 打印切换后的工作目录(即脚本所在目录的绝对路径) | 输出 `/home/user/scripts` | #### 执行流程演示 1. **创建测试脚本** ```bash # 创建测试目录和脚本 mkdir -p /tmp/demo/dir1/dir2 echo '#!/bin/bash echo "原始工作目录: $(pwd)" cd $(dirname $0) echo "脚本所在目录: $(pwd)"' > /tmp/demo/dir1/dir2/test.sh chmod +x /tmp/demo/dir1/dir2/test.sh ``` 2. **执行测试** ```bash # 在任意目录执行(示例在 /tmp) $ cd /tmp $ /tmp/demo/dir1/dir2/test.sh # 输出结果: 原始工作目录: /tmp 脚本所在目录: /tmp/demo/dir1/dir2 ``` --- ### 核心价值与应用场景 1. **获取脚本绝对路径** 确保脚本能准确定位同目录资源(配置文件、子脚本等)[^2][^5] ```bash script_dir=$(cd $(dirname $0); pwd) config_file="${script_dir}/app.conf" ``` 2. **解决路径依赖问题** 当从不同目录调用脚本时,`pwd` 返回的是调用者目录而非脚本目录[^3] ```bash # 调用示例:可能产生错误路径 /some/other/path/run_script.sh ``` 3. **符号链接安全** 比 `readlink -f $0` 更兼容(适用于 macOS 等 BSD 系系统) --- ### 注意事项 1. **空格路径处理** 若路径含空格,需加双引号: ```bash cd "$(dirname "$0")"; pwd ``` 2. **符号链接场景** 若脚本是符号链接,`$0` 指向链接路径而非源文件,需额外处理: ```bash real_path=$(cd $(dirname $(readlink -f $0)); pwd) ``` 3. **子进程限制** 命令替换 `$(...)` 在子 Shell 执行,`cd` 不影响父 Shell 环境[^2] --- ### 相关问题 1. Shell`$0``$BASH_SOURCE` 有何区别? 2. 如何安全处理含空格或特殊字符的路径? 3. 在函数中获取脚本路径与直接获取有何不同? 4. 为什么推荐 `$()` 语法而非反引号 `` ` ``? 5. 如何跨平台兼容地获取脚本真实路径(支持符号链接)? [^1]: 命令替换与路径操作基础 [^2]: `dirname $0``pwd` 的差异解析 [^3]: 多级调用时的路径风险 [^5]: 实际脚本中的典型应用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值