一个空格引发的惨案

记得有个有名的开源软件 (bumblebe)更新时,程序员写代码,多了个空格,结果装了该软件的人都中枪了,是什么神奇的命令呢?

/bin/rm -rf /a
和 (什么区别呢?)
/bin/rm -rf / a

当然这不是咱们这次说的惨案!!!

在 windows 下习惯了,建个文件夹或文件,多打几个空格作为单词的分割,看着层次清晰。不得不说,windows 做的好,空格、UTF8 字符,哪个做名字都适应的很好。但用了 Linux 后,这就是给自己添麻烦了。

接下来看看我这跟空格纠缠的前半天;

1. 新建 2 个文件名带空格的文件作为例子

这里用touch 建一个新的空文件,echo 写入一个有内容的文件。

touch "a b.txt"  
echo "a c " >"a c.txt"


2. 读取下文件内容

直接使用cat 查看,命令报错了,提示缺少文件或目录,因为空格把一个文件名拆成了 2 个文件名

cat a c.txt
cat: a: 没有那个文件或目录
cat: c.txt: 没有那个文件或目录

这时就体现出用tab 键快捷补全的好处了。

在终端输入cat a 之后,按键盘的tab 键, 输入的命令会变为cat a\,这时可以再输入c,就变成了cat a\ c.txt,可以正常运行输出结果了。

cat a\ c.txt
a c

当然还有另外一个方式,加双引号

cat "a c.txt"
a c

单个文件,怎么都还好,虽然麻烦点,也能操作。

但如果你想对带空格的文件进行循环批量操作时呢?

3. 文件批量操作如何遍历?

在 Linux 下,批量操作是很常用的,来试试?

for i in $(ls); do echo $i; done
#  这个输出让人看着很凌乱,文件名都被分开了
a
b.txt
a
c.txt

怎么能把空格分开的文件名合在一起显示呢?

这需要引入 Linux 下一个特殊的变量IFS (internal field separtor) 了。

IFS本质是一个字符集,默认值是空格TAB换行符,即#39; \t\n'

用来将一串输入分割成不同的域。

如上面for 循环中ls 返回的 2 个文件名就被空格、换行符分成 4 个字符串,被遍历了 4 次。

IFS的默认行为通常就是我们需要的,只是在这给我吗带来 了困扰,我们需要临时修改下这个值才可以走下去。

# 把之前的默认值存起来
SAVEIFS=$IFS
# 重定义 IFS,
IFS=$(echo -en "\n\b")
for i in $(ls); do echo $i; done
# 这个输出就正常了
a b.txt
a c.txt
# 恢复默认值
IFS=$SAVEIFS

修改下文件试试, 结果也没问题。

# 把之前的默认值存起来
SAVEIFS=$IFS
# 重定义 IFS,
IFS=$(echo -en "\n\b")
for i in $(ls); do echo "HAHA" > $i; done
cat "a b.txt"
HAHA
# 恢复默认值
IFS=$SAVEIFS


4. IFS 的一个妙用

IFS是默认把字符串分割为数组的工具,那么我们要把一个字符串分割成数组时,就可以把字符串的分隔符替换为IFS字符集中的字符即可。

比如今天遇到一个问题,要批量修改多个文件中的某一列。

有一个输入文件b,内容如下:

a    file1.txt
b    file2.txt
c    file3.txt

想在每个文件如file1.txt里面加一列,内容为a; 如file2.txt里面加一列,内容为b,怎么写呢?

单个文件如果要加,方法比较多,可以sed, 可以awk,这里用awk:

awk 'BEGIN{OFS=FS="\t"}{print $0,"a"}' file1.txt >file1.new.txt

怎么写个循环操作呢?

先直接遍历下,看看取出什么来了?

for i in `cat b`; do echo $i; done
# 输出如下,乱套了
a
file1.txt
b
file2.txt
c
file3.txt

我们先做个替换,把\t替换为;,跳过IFS的分割;

拿到每一行之后,再把;替换为空格( ),利用IFS进行分割。

for i in `cat b | tr '\t' ';'`; do 
    echo $i; 
    i_split=(${i//;/ }) ; 
    filename=${i_split[1]}; 
    sample=${i_split[0]}; 
    awk -v samp="${sample}" 'BEGIN{OFS=FS="\t"}{$0=$0"\t"samp; print $0}' ${filename} >${filename}.1;  
done

当然,还有其它比较坑的包含在文件名里的符号,如括号、引号、短线等,也都会带来困扰。


https://pixabay.com/photos/autumn-leaf-autumn-leaves-6820879/

举报
评论 0