首页 > 解决方案 > bash 重定向到文件添加了意外的 0A 字节

问题描述

我认为如果我将ls的输出重定向到一个文件,那么本来会发送到控制台的完全相同的字符序列将被写入该文件。

为了测试这一点,我创建了 3 个文件,然后列出它们

$ touch a b c
$ ls
a  b  c

我现在再次运行ls,这次重定向到我cat的文件

$ ls > out
$ cat out
a
b
c
out

没想到在out的每个文件名之间都有一个0A换行符

$ xxd out
00000000: 610a 620a 630a 6f75 740a                 a.b.c.out.

将 ls 的输出通过管道传输到 xxd

$ ls | xxd
00000000: 610a 620a 630a 6f75 740a                 a.b.c.out.

换行符仍然存在。

0A 字节是如何到达那里的?如果ls被重定向或者 shell 在某些情况下忽略换行符,它的行为是否会有所不同?

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:    20.04
Codename:   focal

标签: bashio-redirection

解决方案


是的,ls如果它被重定向,行为会有所不同。你可以得到你期望的输出-x

$ mkdir /tmp/t
$ cd /tmp/t
$ touch a b c
$ ls | cat
a
b
c
$ ls -x | cat
a b c
$ ls --format=single-column
a
b
c

@GordonDavisson 向我们指出ls 的 POSIX 规范,内容如下

默认格式应为每行列出一个条目到标准输出;例外情况是终端或指定了 -C、-m 或 -x 选项之一时。如果输出到终端,则格式是实现定义的。

因此,无论如何,在 POSIX 中,行输出是“规范”;终端输出可以是任何东西(尽管除了空格我从未见过任何东西)。大概这是为了使逐行迭代响应成为可能。我也从来没有注意到它,尽管我多次依赖它,但现在我开始思考它!

执行

它在一个 ls 实现的源代码中,明确检查:

    case LS_LS:
      /* This is for the `ls' program.  */
      if (isatty (STDOUT_FILENO))
        {
          format = many_per_line;
          /* See description of qmark_funny_chars, above.  */
          qmark_funny_chars = true;
        }
      else
        {
          format = one_per_line;
          qmark_funny_chars = false;
        }
      break;

资源

或者在当前的 gnu coreutils 中:

  format = (0 <= format_opt ? format_opt
            : ls_mode == LS_LS ? (stdout_isatty ()
                                  ? many_per_line : one_per_line)
            : ls_mode == LS_MULTI_COL ? many_per_line
            : /* ls_mode == LS_LONG_FORMAT */ long_format);

wherestdout_isatty的定义与前面的示例相同。

资源


推荐阅读