复杂的bash管道在跳转中工作

时间:2021-02-27 07:29:16

My intention is to output log of recursive wget in one line, 'status bar'-like. So I assembled this pipeline (my wget call has a bit more options but I left those only essential for the problem described) :

我的目的是在一行中输出递归wget的日志,类似于“状态栏”。因此,我组装了这个管道(我的wget调用有更多的选项,但我只保留了描述的问题所必需的选项):

wget -r -nv ftp://example.com 2>&1 | cut -c1-80 | xargs -I line echo -ne 'line\033[0K\r'

Let me explain what I meant to do. Maybe there's something wrong with my command.

让我解释一下我想做什么。也许我的命令有问题。

  • -r means 'recursive download';
  • - r意味着“递归下载”;
  • -nv makes messages about each downloads to be brief, like: "time: URL -> local file";
  • -nv对每个下载的消息都很简短,比如:“时间:URL ->本地文件”;
  • &2>1 redirects stderr to stdout so I could work with the messages through pipes;
  • 2>1将stderr重定向到stdout,这样我就可以通过管道处理消息;
  • | cut -c1-80 cuts the output line to 80 characters. Sometimes the URL and local file name together make a long string which breaks a line into 2 or more. And I need it to fit in a single line of console. 80 stands here just for example. In my script I determine console width with tput cols;
  • |裁剪-c1-80将输出行裁剪为80个字符。有时,URL和本地文件名一起组成一个长字符串,它将一条线分成2个或多个。我需要它能装进一个控制台。80就在这里。在我的脚本中,我使用tput cols确定控制台的宽度;
  • | xargs -I line echo -ne 'line\033[0K\r' prints output of previous command and adds two special characters: \033[OK - end of line, which cleans the rest of line if there are any characters left from prevoius output; and \r - carriage return which sets the cursor to the beginning of current line.
  • | xargs - i行echo -ne 'line\033[0K\r'打印前一个命令的输出,并添加两个特殊字符:\033[OK - end of line,如果有任何来自prevoius输出的字符,可以清除其余的行;和\r -回车,将光标设置为当前行的开始。

So the wanted behaviour is:

因此,通缉行为是:

  1. wget downloads a file and tries to print a notice about this to stdout
  2. wget下载一个文件,并试图打印一个关于此的通知给stdout
  3. cut immediately intercepts the output of wget and trims it to 80 chars
  4. cut会立即截断wget的输出,并将其修剪成80个chars
  5. xargs catches the trimmed line and prints it immediately with special characters
  6. xargs捕捉裁剪好的线条,并立即用特殊字符打印出来

So I should see some kind of status bar where current download is displayed.

所以我应该看到一些状态栏显示当前下载。

But! All I see is nothing happening for 10 to 60 seconds and then all messages about downloads that was done during that time are printed in about 1 second. They actually printed the way I wanted, but very fast. Then again, a pause, another portion of messages in 1 second, and so on. So all is fine except the immediately-ness.

但是!我所看到的是在10到60秒内没有任何事情发生,然后在这段时间内完成的所有关于下载的消息都会在1秒内打印出来。他们按我想要的方式印刷,但很快。然后是暂停,1秒内的另一部分信息,等等。所以一切都很好,除了即时性。

When I remove xargs part, the messages are displayed instantly (but not in one line). When I remove cut call, they are instant, but sometimes the line breaks with some really long URL. If I remove only special characters from echo call the output is still "jumpy" and not in one line.

当我删除xargs部分时,消息会立即显示(但不是在一行中)。当我删除剪切调用时,它们是即时的,但有时换行符会有一些非常长的URL。如果我只从echo中删除特殊字符,那么输出仍然是“跳动的”,而不是一行。

To reproduce this, you can replace "ftp://example.com" with any URL (HTTP will work too) that can be used for testing recursive download, i.e. in case of FTP has many files and directories and in case of HTTP has many links to pages that have more links (don't be afraid that it might try to download all Internet, beacuse -r option has default recursion level of 5). If you can't reproduce this, then I suppose it's something wrong with my distribution, please write about it in comments section below.

复制这个,你可以用任何URL替换“ftp://example.com”(HTTP)工作,可以用于测试递归下载,即在FTP的情况下有很多文件和目录,如果HTTP有很多链接的页面有更多的链接(不要害怕它可能会试图下载所有的互联网,因为- r选项默认递归级别的5)。如果你不能复制,我想这是错误的分布,请在下面的评论部分写下它。

P.S. If you know a better way of organizing a status bar for wget, your comments are very welcome. But I am learning Bash and would like to know what is causing such strange behaviour. Maybe there's something about pipes or echo or xargs I don't know. So the question is why this pipeline work so and not as I expected.

注:如果你知道更好的组织wget状态栏的方法,欢迎你的评论。但我正在学习Bash,想知道是什么导致了这种奇怪的行为。也许是关于管道或者echo或者xargs我不知道。所以问题是为什么这条管道能正常工作,而不是像我想的那样。

2 个解决方案

#1


1  

The problem is output buffering, there are solutions for this: Turn off buffering in pipe

问题是输出缓冲,有解决方法:关闭管道中的缓冲

Unfortunately when I try to apply them, I get xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option.

不幸的是,当我尝试应用它们时,我得到了xargs:无与伦比的双引号;除非使用-0选项,否则默认引号对xargs是特殊的。

You have to try a different approach, I don't think xargs is a good choice for this task, try awk, perl, python, ruby...

您必须尝试不同的方法,我认为xargs不是这个任务的好选择,试试awk、perl、python、ruby……

#2


2  

xargs gathers many lines of input and calls the command (echo in your situation) only once*. Add '-L 1' to the arguments to xargs and see if this helps.

xargs收集许多输入行,只调用一次命令(在您的情况下是echo) *。将“-L 1”添加到xargs的参数中,看看是否有帮助。

* xargs uses more calls to the command if the commandline would grow too long, but groups as much as possible.

* xargs使用更多的命令,如果命令行长得太长,但是尽可能多的组。

#1


1  

The problem is output buffering, there are solutions for this: Turn off buffering in pipe

问题是输出缓冲,有解决方法:关闭管道中的缓冲

Unfortunately when I try to apply them, I get xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option.

不幸的是,当我尝试应用它们时,我得到了xargs:无与伦比的双引号;除非使用-0选项,否则默认引号对xargs是特殊的。

You have to try a different approach, I don't think xargs is a good choice for this task, try awk, perl, python, ruby...

您必须尝试不同的方法,我认为xargs不是这个任务的好选择,试试awk、perl、python、ruby……

#2


2  

xargs gathers many lines of input and calls the command (echo in your situation) only once*. Add '-L 1' to the arguments to xargs and see if this helps.

xargs收集许多输入行,只调用一次命令(在您的情况下是echo) *。将“-L 1”添加到xargs的参数中,看看是否有帮助。

* xargs uses more calls to the command if the commandline would grow too long, but groups as much as possible.

* xargs使用更多的命令,如果命令行长得太长,但是尽可能多的组。