shell脚本如何替换包含换行符的变量

shell script how to substitute variable contains newline

$ ls . > /tmp/tfile.txt
$ flist=`cat /tmp/tfile.txt`
$ echo $flist
a.txt b.txt c.txt
$ echo ${flist#* }
a.txt b.txt c.txt

为什么${flist#* }不能输出b.txt c.txt?如何处理变量包含换行符?

${flist#* }$flist 一样展开,但删除了与 * 匹配的最小部分(星号 +space,其中星号匹配零个或多个字符,而 space从开头匹配 space 个字符)。

因此,它从扩展中删除了第一个 space 之前的字符。因此,在您的情况下,它会打印 a.txt 然后看到 space 并退出,因此不会打印 b.txtc.txt.

如果你输入 echo "${line#*.txt}",那么输出将是:

b.txt c.txt

查看此 reference 以了解 bash 中的 #*

一般来说,shell 不是处理数据和修改变量的好工具。 (尽管它是与 Unix 一起工作的一个很好的工具,请继续学习和使用它。)一些方言可以在某些方面解决这个问题,但记住复杂的数据处理最好在 shell.

大多数数据处理预计将使用外部工具进行,而不是使用 shell 原语。

这里有几种方法可以修剪您刚刚阅读的文件的第一个条目,最特殊的是:

omit_first_line()
{
   tail -n+2 ""
}

flist=$(omit_first_line tfile.txt)

了解如何使用 $(…) 代替反引号,不鼓励使用反引号,因为它们很难配对和引用。

还有许多其他工具可以为您跳过文件中的第一行,例如 sedawk

如果您正在处理文件,您真的应该考虑使用 find 和 xargs,它们可以很好地协同工作,如

find . -type f -print0 | xargs -0 stat

find . -type f -print | { while read filename; do printf 'Filename %s\n' "${filename}"; done }

一般来说,编写过滤器比处理列表更容易。不是将数据存储在列表中,而是使用经典的命令式语言,将数据存储在文件中,每条记录单独一行,并在过滤器之间传输数据。如果您能够直接打印数据,则可能根本不需要将其存储在文件中。

现在,如果您有充分的理由在 shell 中处理您的列表,您可以使用位置参数:

gobble()
(
  set -- "$@"
  shift
  echo "$@"
)

flist='a.txt b.txt c.txt'
flist=$(gobble ${flist})
echo ${flist}

非常感谢您的依赖。最后,我通过 re-assigning flistflist=`echo $flist`;.

解决了这个问题