尽管使用大括号,连接字符串中仍插入空格

尽管使用大括号,连接字符串中仍插入空格

我使用以下语法读取文件名并修改其扩展名以生成匹配数据文件的名称。例如,我有 ABC_1.fastq.Blockxy、XYZ_1.fastq.Block34,我想生成 ABC_2.fastq.Block12、XYZ_2.fastq.Block34 作为我的新文件名:

for infile in *_1.fastq.Block*
do
    #base=$(basename ${infile} _1.fastq.**)
    IFS='_'
    read -ra ADDR <<< $infile
    base=${ADDR[0]}
    IFS='.'
    read -ra ADDR <<< ${ADDR[1]}
    second_file="${base}_2.fastq.${ADDR[2]}"
    echo $second_file
done

执行时,该脚本打印例如

ABC_2 fastq Block12
XYZ_2 fastq Block34

即 fastq 和 Block12 之间的空格。当我连接时,为什么我在这三个字符串之间得到空格而不是句点?我认为对变量名使用大括号应该可以消除这个问题。

答案1

echo $second_file

由于您忘记引用参数扩展,因此它受到 split+glob 的影响,因此 with IFS=.,ABC_2.fastq.Block12首先被拆分为, ABC_2,fastqBlock12每个单词受到 globbing 的影响,这里没有任何效果,因为没有一个单词包含 glob 运算符。

因此传递了 3 个参数,echo并打印空格分隔。

要打印变量的内容,后跟换行符,您需要:

printf '%s\n' "$var"

有关更多详细信息,请参阅:


现在,对您的代码添加一些注释:

  • 由于 bash 没有相当于zsh(N)glob 限定符或 ksh93 的~(N)glob 运算符,因此在 for 循环中(至少)使用 glob 之前,您需要设置以下nullglob选项:

    shopt -s nullglob
    for infile in *_1.fastq.Block*; do...
    

    如果不这样做并且没有匹配的文件,您将循环遍历文字*_1.fastq.Block*

  • 您只能read使用以下命令设置 IFS:(另请参阅 旧版本中需要的IFS=_ read -ra ADDR <<< "$infile"引号)。这样,仅在运行时更改,并且在返回后恢复到之前的值。$infilebash$IFSreadread

  • IFS=. read -ra <<< "$var"是一种糟糕的分裂方法。首先仅适用于单行$var,这不一定是文件名的情况,而且效率很低。这涉及到将 的内容存储$var到临时文件中,或者根据 的版本bash和/或大小通过管道提供它$var,然后一次读取一个字节,直到找到换行符。

    在这里,您可以使用 split+glob 运算符来代替:

    IFS=:; set -o noglob
    addr=( $infile )
    

    addr=( $infile'' )(或者不是忽略尾随:。)

    或者使用适当的拆分运算符切换到更好的 shell。

    这里的另一种方法是:

    regex='^(.*)_1\.fastq\.(Block.*)$'
    if [[ $infile =~ $regex ]]; then
      outfile=${BASH_REMATCH[1]}_2.fastq.${BASH_REMATCH[2]}
      ...
    

    需要注意的是,正则表达式匹配仅适用于有效文本,这同样不能保证文件名。

    在这里,您还可以使用标准sh参数扩展运算符:

    new_file=${infile%_1.fastq.Block*}_2.fastq.Block${infile##*_1.fastq.Block}
    

    或者 ksh93 风格:

    new_file=${infile/_1.fastq.Block/_2.fastq.Block}
    

    _1.fastq.Block(请注意,如果文件名中出现多次,所有这些方法之间的行为差​​异)。


1 但请注意,如果 a在运行trap时被处理read,则该陷阱中的代码将被修改$IFS

相关内容