Bash 经常无法将标准输出重定向到文件(来自 cat)

Bash 经常无法将标准输出重定向到文件(来自 cat)

我通常使用以下方法附加到文本文件cat

cat >> FILE

我使用别名来避免意外覆盖文件(使用单个>):

alias a='cat >>'

Enter 更改行,Ctrl+D终止命令。我在主文件夹中写入多个文本文件,所有这些文件都是我创建、拥有并可以编辑的。

有几次,在我的桌面 Linux 系统 (Fedora 39) 和 Termux (Android) 上,命令停止重定向到文件,同时看似正常地接受输入。我丢失了数百行,主要是我粘贴的 URL。似乎仅在命令运行一段时间后才会发生。

是否有任何原因导致重定向cat >>可能停止运行?例如,任何特殊字符(在输入中)是否会产生影响?

更新:我已经确认各个文件的 inode 编号不断变化($ ls -li或者使用 来查看确切时间$ stat -c '%w')——这种情况发生因为 Syncthing 的设计会重新创建同步文件。我将来必须重新评估我如何使用该软件。抱歉一开始没有提到 Syncthing。

在我自动化的命令中,至少sed -i(就地编辑文件)也替换了索引节点。

该命令cat >> FILE也需要更换(已给出建议,请参阅评论和答案)。

答案1

运行后cat >> file,shell 将在fd 1 上的子进程中的file当前工作目录中打开,如果成功则在该进程中执行。O_WRONLY|O_CREAT|O_APPENDcat

如果没有,shell 将输出错误消息并且不运行cat

如果找不到cat命令,它也会输出一条错误消息。

cat依次,在循环中读取其 fd 0 并将其读取的内容写入 fd 1。

同样,如果失败,它将输出一条错误消息。

如果正在运行的进程cat被信号杀死或挂起,shell 还应该在 stderr 上报告它(例如超出文件大小限制如果用SIGXFSZ或杀死暂停(tty 输入)如果通过 SIGTTIN 暂停)。

如果 fd 0 在 tty 设备上打开,并且该 tty 设备是相同的,则 shell 从中读取命令,通常是这种情况,在运行之前cat,shell 将按照进入其自己的行编辑器之前的方式配置 tty 的行规则。

一般来说,这意味着它将处于icanon行规则实现粗略行编辑器的模式。

在所有这些中,行编辑器实际上是唯一能够识别特殊字符的东西。stty -a会给你一个清单。就我而言:

$ stty -a
speed 38400 baud; rows 43; columns 159; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

你会看到icanon上面提到的。^C, ^\, ^?, ^U, ^D, ^Q, ^S, , ^Z, ^R, ^W,字符经过特殊处理。//受制于(在我的情况下已启用),/受制于,(上面)在 Linux 上不受支持。^V^O^C^Z^\isig^S^Qixondiscard^O

例如,如果您输入foobar^Ubazfoobar则为被杀,但话又说回来,您会看到它在您键入的回声中被删除。

输入^C角色会cat在其中间杀死read

如今大多数终端模拟器在粘贴时都会删除这些字符。因此,为了在粘贴时发生这种情况,您需要使用一个仍然不执行剥离操作的终端,或者将其中一些kill/ werase/ intr... 设置设置为一些非控制字符,这将是病理状况。

该行编辑器对其可以编辑的行的大小也有限制。在 Linux 上,IIRC 为 4095 字节。因此,如果您粘贴的行大于此值,则超过第 4095 个字节的任何内容都将被丢弃。

在该icanon模式下,当 tty 行编辑器退出时, cat's将返回,当您输入(将其转换为on ) 或或 任何,或字符read()时,就会发生这种情况。^M^Jicrnl^Jeoleol2eof

但无论如何,如果cat正常退出,也就是说,如果它没有被 SIGINT on^C或 SIGQUIT on杀死,^\或者没有被 SIGTSTP on 挂起^Z,并且没有报告错误,那么它将从终端读取输入并将其写入文件。

因此,总而言之,您正在尝试的事情在正常情况下不应该发生,我能想到的唯一情况是病理情况:

  • 所报告的 tty 设置stty -a全部被窃听。
  • 或者您粘贴的数据包含由 tty 驱动程序的行编辑器解释的控制字符,并且由于某种原因您的终端编辑器不会删除它们。

cat不要忽略人为错误,例如未能看到shell报告的错误或在与您想象的不同的目录中创建的文件,或者在cat运行时删除、替换、重命名或截断(就像这样同步事物您在评论中提到使用)。

使用该a='cat >>'别名,如果您输入a some file而不是a 'some file',则它cat >> some file与 相同,cat file >> some并且cat将读取file而不是 stdin 并将输出写入some而不是some file

对该别名的可能改进(假设 GNU tee)是:

a() {
  rlwrap -pblue -S 'add> ' tee --output-error=warn -a -- "$@" > /dev/null
}

Whererlwrap为您提供了比原始 tty 驱动程序更高级的行编辑器(如果用作bashshell,您已经熟悉了),并且有一个提示,可以更清楚地表明它正在等待一些输入。

并且使用tee允许您一次将输入写入多个文件。

答案2

如果目的是避免意外覆盖文件,请考虑设置适当的 shell 变量。例如,对于bash,

set -o noclobber    # Do not allow > to overwrite a file
set -C              # The same

例子

set -o noclobber
echo >ddd
echo >ddd
-bash: ddd: cannot overwrite existing file

set +o noclobber
echo >ddd

相关内容