Linux shell 命令粘贴问题

Linux shell 命令粘贴问题

我经常需要运行脚本并为其提供几行 STDIN。执行此操作时,我通常会准备调用,然后将它们从一个终端窗口复制并粘贴到另一个终端窗口。理想情况下,我希望能够粘贴文本块,例如:

<script name> <script args>
<STDIN line 1>
<STDIN line 2>
...

之后,我将按 control-d 向脚本指示 STDIN 已到达输入末尾。

如果我在 bash 中执行此操作(作为单个粘贴),则脚本执行,会话中没有任何 STDIN 回显,<cr>在处理 ^d 之前必须命中额外的内容,并且脚本 STDIN 的第一行会丢失。因此,我通常将脚本调用粘贴为其自己的粘贴,然后将 STDIN 的所有行粘贴为第二个粘贴。通过这种方式,这些行粘贴回显,我可以按 control-d 来指示输入结束。

如果我在 zsh 下执行此操作,大多数 STDIN 行不会回显(如果有很多 STDIN 行,最后一些行将回显,其中第一行可能仅回显该行的尾随片段),然后再次,<cr>在识别 ^d 之前需要额外的。

所涉及的脚本是 python 还是 perl 并不重要...两者的行为都是相同的,这让我相信这是一个 shell 问题。

问题是,问题出在哪里,是否有解决方案可以允许将每次执行作为单个粘贴进行?

正在运行的脚本的内容实际上并不重要。在测试中,我使用了像 while 循环一样简单的脚本,将 STDIN 逐行读取到缓冲区中,然后在 while 循环之外打印缓冲区。我所说的<cr>是指 Enter 或 Return 键...具体取决于键盘上的键。

答案1

尝试使用heredoc,它们很容易剪切和粘贴:

script.sh arg arg arg <<'END_INPUT'
line1
line2
line3
END_INPUT

答案2

最有可能的是,您所观察到的取决于行编辑以及 shell 设置终端的模式。

当您粘贴文本时

cat
a
b

(包括“b”后的换行符)到交互式 Bash 会话的命令行,默认情况下它会找到设置在原始模式— 就像当行编辑由GNU 阅读线图书馆;这意味着,除其他外,输入被逐个字符(而不是逐行)发送到 shell,将回车符(\r,按下时终端看到的内容Enter或将换行符粘贴到命令行)转换为换行符 ( \n) 被禁用,键入的字符将由 shell 本身(而不是终端)回显到命令行。
然后 shell 读取第一行,发现它是一个完整的简单命令 ( cat),并运行它。在执行此操作的同时,它还会禁用自己的行编辑,将终端设置为“cooked”模式并启用\r\n转换。不过,此时输入缓冲区已经包含a\rb\r,这是读取cat并打印在屏幕上的内容。如果您随后按Ctrl+ D( ^D),cat则退出并打印新的提示,覆盖(唯一)打印的行(因为它以回车符结尾)。

注意:

  • 如果代替cat,您调用一个在循环中从标准输入读取的程序,相当于脚本

    while IFS= read -r foo
    do
      # Accumulate the content of foo
    done
    

    它不会读取任何内容,直到您点击Enter,因为输入缓冲区中没有\n字符(除非粘贴的数据超出了终端的输入缓冲区大小,请参见下文)。

  • b粘贴后,您在命令后面看到的文本(在本例中为 )不是粘贴内容的回显,而是 的输出cat。您可以轻松验证这一点,例如,通过粘贴

    od -An -tc
    a
    b
    

    粘贴到 shell run asstrace -f -e read,write bash也可能有助于理解正在发生的事情。

  • 如果要从标准输入读取的粘贴文本“足够”(在我的系统上,超过 4095 个字符;请参阅中的“规范和非规范模式”)man 3 termios),上面写的内容仅适用于第一个 4095 字节大小的读取 - 终端在原始模式下最多放入其输入缓冲区中的内容。后续读取在烘焙模式下进行,\r\n发生转换,并且粘贴文本的该部分将回显到命令行(并且可能与命令的输出混合)。

原则上,可以通过禁用 Bash 的行编辑来更改此行为:

$ set +o emacs
$ set +o vi
$ cat         # Pasting the above snippet here
a
b
a
b
$             # ^D makes cat exit and brings the prompt back

但请注意,冗长的输入仍然会与输出混合在一起,因为终端无法告诉 shell 等到粘贴操作结束后再开始处理,除非启用了“括号粘贴”(见下文)。


zsh似乎简单了一些。唯一的陷阱:“括号内的粘贴”可能是默认启用的。它允许您将多行命令粘贴到命令行,像脚本一样执行它们,同时确保正确处理特殊字符(例如制表符、换行符)。这也意味着,当给定

cat
a
b

zsh读取三行并执行cat,等待来自终端的新输入;退出时cat,shell 尝试运行a(失败,除非系统上存在这样的命令),然后b.

禁用括号内的粘贴可以实现您似乎正在寻找的行为(但是,与bash无行编辑相比,默认情况下zsh不会回显粘贴的内容,除了超过前 4095 个字节的部分(请参阅上面的项目符号列表)) :

% unset zle_bracketed_paste
% cat         # Pasting the above snippet here
a             # cat prints these two lines
b
%             # ^D makes cat exit and brings the prompt back

相关内容