又一个坑,Linux中~(tilde)和$HOME的区别
一个sratoolkit的报错
想到Linux中~
和$HOME
的区别这个问题,是因为在集群上安装了sratoolkit,在使用fastq-dump
时遇到的一个报错:
$ fastq-dump --split-3 A549-DRR016695.1
2019-11-30T16:49:38 fastq-dump.2.9.6 err: path not found while searching dynamic library within file system module - failed to open SRA manager
=============================================================
An error occurred during processing.
A report was generated into the file '/work/xxxxx/ncbi_error_report.xml'.
If the problem persists, you may consider sending the file
to 'sra-tools@ncbi.nlm.nih.gov' for assistance.
=============================================================
同样fasterq-dump
也无法正常使用:
$ fasterq-dump --split-3 A549-DRR016695.1
2019-11-30T16:50:19 fasterq-dump.2.9.6 err: cmn_iter.c cmn_get_acc_type( 'A549-DRR016695.1' ).VDBManagerMakeRead() -> RC(rcFS,rcDylib,rcSearching,rcPath,rcNotFound)
排查过程:
- 百度和Google搜索没有找到解决方案;
- GitHub上搜索关键词没有找到相关issue,看了一眼报错的源代码,但没有看懂;
- 发现本地安装的WSL上sratoolkit运行正常;
- 因此怀疑安装包或者sra文件损坏,但是用
md5sum
检查之后并没有问题; - 发现用相对路径或者绝对路径运行
fastq-dump
或fasterq-dump
都可以正常使用,但通过环境变量调用不能正常运行; - 比较了本地和集群
.bashrc
里sratoolkit环境变量的写法,区别是本地用的完整路径,集群上用的~
- 修改集群
.bashrc
环境变量写法,source
之后重新测试,问题解决。
总结下来就是在.bashrc
中用~
代替完整路径导致的一个问题。可能大约一年以前,我都一直认为~
和$HOME
两者是等效的,直到在win10上安装了WSL (Windows Subsystem for Linux)并把软件安装到了$HOME
之后才发现两者还是存在一些区别(平时在公司有专门的共享目录安装软件)。具体表现如下:
如果在.bashrc
中这么写:
export PATH='~/software/sratoolkit.2.9.6-1-centos_linux64/bin/':$PATH
source ~/.bashrc
之后运行which fastq-dump
将不会返回任何值。但是如果在.bashrc
中写完整路径:
export PATH='/home/xiaofei/software/sratoolkit.2.9.6-1-centos_linux64/bin/':$PATH
which
将正确返回fastq-dump
的路径:
$ which fastq-dump
/home/xiaofei/software/sratoolkit.2.9.6-1-centos_linux64/bin//fastq-dump
接下来我又尝试了在.bashrc
中用$HOME
替代/home/xiaofei
,发现两者是等效的(至少在这里运行fastq-dump
和which
时结果是一致的)。我在其他服务器上用which
从来没有遇到过这个问题,所以我一度以为是WSL或者Ubuntu的bug,直到今天在学校的集群上遇到这个sratoolkit的报错。
那~
和$HOME
到底有什么区别?
我查阅了GNU文档 - Tilde Expansion中介绍~
和由它构成的"tilde-prefix"在shell中是如何被处理的,直接用代码举例解释:
# 1.没有引号或者任何字符在`~`之前的时候,才构成所谓"tilde-prefix"
$ echo "~"
~ # "~"前有引号
$ echo test~
test~ # "~"前有其他字符串
# 2. 这个"tilde-prefix"中没有任何字符被引号包围的时候,"~"后的字符被当作可能的登录名
$ echo ~"test"
~test # 由于后面"test"被引号包围,所以"~"被当作字符串处理
# 3. 如果登录名是空字符串,"~"被替换为shell变量$HOME
$ echo ~
/home/xiaofei
$ echo ~/foo
/home/xiaofei/foo
# 4. 否则"tilde-prefix"被替换为特定登录名的$HOME
$ echo ~root/foo
/root/foo # ~root被当作用户root的home目录
$ echo ~xiaofei/foo
/home/xiaofei/foo # ~xiaofei被当作用户xiaofei的home目录
$ echo ~test
~test # 由于"test"用户名不存在,因此"~"被当作字符串处理
# 5. 其他一些用法
$ pwd
/home/xiaofei/test # 当前目录
$ echo ~+/foo
/home/xiaofei/test/foo # "~+"被替换为当前目录
$ echo ~-/foo
/home/xiaofei/foo # "~-"被替换为上一次所在目录
虽然该文档中也提到,在指定$PATH
等环境变量时用~
是OK的:
Each variable assignment is checked for unquoted tilde-prefixes immediately following a ‘:’ or the first ‘=’. In these cases, tilde expansion is also performed. Consequently, one may use filenames with tildes in assignments to PATH, MAILPATH, and CDPATH, and the shell assigns the expanded value.
但是从上文sratoolkit和which中发现的问题来看,~
在读入另一个程序中进行处理的时候,依旧可能出现问题,不够稳健。因此小伙伴们以后在写环境变量的时候,尽量用完整的绝对路径而不要用~
!
最后再解释一下为何在本地安装的WSL和集群上的which
对待~
会有区别:我发现WLS上安装的Ubuntu(包括16.04和18.04)用的/usr/bin/which
都是通过一个shell脚本实现的(我不确定原生的Ubuntu是不是这样);而集群安装的CentOS,用的/usr/bin/which
是一个二进制可执行文件GNU which。这个shell版的which
怎么产生的错误呢,大概逻辑如下:
$ a="~/test.txt" # 如果用"~"
$ b="/home/xiaofei/test.txt" # 如果用完整路径
$ if [ -f "$a" ];then echo "True";fi # which会找不到该文件
$ if [ -f "$b" ];then echo "True";fi # 用完整路径则可以
True
想必sratoolkit也会是类似的问题吧。最后再次强调,以后在写环境变量的时候,尽量用完整的绝对路径而不要用~
,以免踩坑!
Python中的补充知识
在Python中,~
也不能用常规方法进行解析,需要用os.path.expanduser
:
>>> import os
>>> os.path.isfile('~/test.txt')
False
>>> os.path.exists('~/test.txt')
False
>>> os.path.expanduser('~/test.txt')
'/home/xiaofei/test.txt'
>>> new_path = os.path.expanduser('~/test.txt')
>>> os.path.isfile(new_path)
True
>>> os.path.exists(new_path)
True
参考资料
https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
暂无评论