简体   繁体   English

STDOUT 和 STDERR 到同一个文件,STDERR 到另一个 [清单]

[英]STDOUT and STDERR to SAME file and STDERR to ANOTHER [checklist]

UPDATE更新

Thanks a lot for the answers and comments.非常感谢您的回答和评论。 Thanks to @Fravadona for the help I managed to copy it and change a few things.感谢@Fravadona 的帮助,我设法复制了它并做了一些改动。 This is the actual code:这是实际的代码:

{ exec 0>&9; } 1> /dev/null 2>&1 || exec 9> /dev/tty

checklist() {

    (( $# >= 1 )) || return 1

    {
      printf '%s' "$2"
      #(( $# > 1 )) #&& printf ' %q' "${@:2}" I think I don't need this line
      printf '...\n'
    } >&9

    if "$1"; then
      printf '%s\n' "$3" >&9
    else
      printf '%s\n' "$4" >&9
    fi 
 }

checklist "ping -c 3 google.es" "Trying to ping google" "Ping is successfull!" "Error trying to ping google!"
checklist lsblk "Trying to list disks" "The command ended succesfully!" "Error trying to list disks!"

The first argument is the actual command, the 2nd is the string I want to show while the command is running, the 3rd is when the command ends successfully and the 4th is when there's some error.第一个参数是实际的命令,第二个是我想在命令运行时显示的字符串,第三个是命令成功结束的时候,第四个是出现错误的时候。

The output I'm getting is:我得到的 output 是:

Trying to ping google...
Error trying to ping google!
Trying to list disks...
The command ended succesfully!

The lsblk command is working because there's only 1 word but the ping command doesn't work because it has more words. lsblk 命令有效,因为只有 1 个单词,但 ping 命令不起作用,因为它有更多单词。 How can I do it?我该怎么做? I've tried with single quotes, backward apostrophe, brackets... I think I'm missing something.我试过使用单引号、反撇号、方括号……我想我遗漏了什么。

The logs are working fine.日志工作正常。

Thank you for your help!谢谢您的帮助!

ORIGINAL原版的

I'm doing an script in bash and I want to redirect the stderr and stdout to a single file (output.log) and the stderr to another one (error.log).我正在 bash 中执行脚本,我想将 stderr 和 stdout 重定向到一个文件 (output.log),将 stderr 重定向到另一个文件 (error.log)。 In the terminal, it has to show only the echo commands I want from the script.在终端中,它必须只显示我想要从脚本中得到的回显命令。

I just want to make like a checklist.我只想做一个清单。 Redirect stdout and stderr from the commands to a file and stderr to diferent file.将 stdout 和 stderr 从命令重定向到一个文件,将 stderr 重定向到不同的文件。 Once you have this, the only thing left is checking if the command is successful or not.一旦你有了这个,唯一剩下的就是检查命令是否成功。 This is easy just checking the stderr file.这很容易,只需检查 stderr 文件即可。

I did something similar to what I want but I have to execute the script like this:我做了一些与我想要的类似的事情,但我必须像这样执行脚本:

{ . script.sh 2>&1 1>&3 | tee error.log; } > output.log 3>&1

But in each command that I want to show in the terminal, I have to add:但是在我想在终端中显示的每个命令中,我必须添加:

| tee -a /dev/tty

A minor problem with this method is that tput civis and tput cnorm to hide and show the cursor is not working此方法的一个小问题是 tput civis 和 tput cnorm 隐藏和显示 cursor 不起作用

It would be nice if I can execute the script like this, but this is not required as long as tput works:如果我能像这样执行脚本就好了,但只要 tput 有效就不需要这样做:

. script.sh

I'm showing an example with ping command and another one to show you what I want:我正在展示一个带有 ping 命令的示例,另一个示例向您展示我想要什么:

echo "Trying ping..." | tee -a /dev/tty # Show to terminal
if ! ping -c 3 google.es; then # Check if the ping is successful or not, redirecting all outputs to files
  echo "Error trying to ping" | tee -a /dev/tty # Show to terminal
else
  echo "Ping finished successfully!" | tee -a /dev/tty # Show to terminal
fi

echo "Trying {cmd}..." | tee -a /dev/tty # Show to terminal
if ! {cmd2}; then # Check if the {cmd2} is successful or not, redirecting all outputs to files
  echo "Error trying to {cmd2}..." | tee -a /dev/tty # Show to terminal
else
  echo "{cmd2} finished successfully!" | tee -a /dev/tty # Show to terminal
fi
.
.
.
.

The output would be and I want is: output 是,我想要的是:

Trying ping...
Ping finished successfully!
Trying {cmd2}...
Error trying to {cmd2}!
.
.
.
.

If there's another way to make that checklist I am all ears.如果有另一种方法来制作该清单,我会洗耳恭听。

Thank you for your time:)感谢您的时间:)

PS: I will do functions to refactor the code, don't worry about that. PS:我会做函数重构代码的,这个不用担心。 For example a function to check if the command is successful or not.例如 function 来检查命令是否成功。

UPDATE更新

If you could refactor script.sh like this:如果您可以像这样重构script.sh

#!/bin/bash

{ exec 0>&9; } 1> /dev/null 2>&1 || exec 9> /dev/tty

checklist() {

    (( $# >= 1 )) || return 1

    {
        printf 'Trying `%q' "$1"
        (( $# > 1 )) && printf ' %q' "${@:2}"
        printf '`...\n'
    } >&9

    if "$@"
    then
        printf '%s finished successfully!\n' "$1" >&9
    else
        printf 'Error with %s\n' "$1" >&9
    fi 
 }

checklist ping -c 3 google.es
checklist {cmd2}

Then, when you run it without sourcing:然后,当您在没有采购的情况下运行它时:

{ ./script.sh 2>&1 1>&3 | tee error.log; } > output.log 3>&1

On the terminal you'll get:在终端你会得到:

Trying `ping -c 3 google.es`...
ping finished successfully!
Trying `\{cmd2\}`...
Error with {cmd2}

In error.log you'll have:在 error.log 中,您将拥有:

./script.sh: line 15: {cmd2}: command not found

In output.log there'll be:在 output.log 中会有:

PING google.es (142.250.75.227): 56 data bytes
64 bytes from 142.250.75.227: icmp_seq=0 ttl=115 time=1.743 ms
64 bytes from 142.250.75.227: icmp_seq=1 ttl=115 time=1.893 ms
64 bytes from 142.250.75.227: icmp_seq=2 ttl=115 time=1.569 ms

--- google.es ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 1.569/1.735/1.893/0.132 ms
./script.sh: line 15: {cmd2}: command not found

OLD ANSWER旧答案

A standard way would be:一个标准的方法是:

 { . script.sh 2>&1 1>&3 | tee error.log; } > output.log 3>&1

To understand it you need to know that the "redirections" of a command are executed from right to left .要理解它,您需要知道命令的“重定向”是从右到左执行的。

Let's consider what happens with cmd 2>&1 1>&3 .让我们考虑一下cmd 2>&1 1>&3会发生什么。

  1. The content of 1 is "moved" to 3 . 1的内容被“移动”到3

  2. The content 2 is "moved" to 1 .内容2被“移动”到1

  3. As there is no more redirections for cmd , the contents of 1 , 2 (empty) and 3 are then "consumed" by the rest of the script.由于不再有cmd的重定向,因此脚本的 rest 会“消耗” 12 (空)和3的内容。

Now, what would happen if we change the order of the redirections with cmd 1>&3 2>&1 ?现在,如果我们用cmd 1>&3 2>&1改变重定向的顺序会发生什么?

  1. The content of 2 is "moved" to 1 2的内容被“移动”到1

  2. The content of 1 (which also contains a copy of 2 ) is then "moved" to 3 . 1的内容(也包含2的副本)然后“移动”到3

  3. As there is no more redirections for cmd , the contents of the file descriptors 1 (empty), 2 (empty) and 3 are then "consumed" by the rest of the script.由于不再有cmd的重定向,文件描述符1 (空)、 2 (空)和3的内容随后被脚本的 rest“消耗”。

This should do what you want:这应该做你想做的:

. script.sh > >( tee output.log ) 2> >( tee error.log )

This uses process substitutions to write the output of the command to two instances of tee .这使用进程替换将命令的 output 写入tee的两个实例。

The first substitution writes stdout to output.log and copies it to the existing stdout (likely your terminal).第一个替换将 stdout 写入output.log并将其复制到现有的 stdout(可能是您的终端)。 The second substitution copies stderr to both error.log and stdout, which goes to the first tee that writes it to the output file and the terminal.第二个替换将 stderr 复制到error.log和 stdout,这将转到第一个将其写入 output 文件和终端的tee

I think this will do what you want.我认为这会做你想做的。

$: { ls -ld . bogus 2> >( tee err.log ) ;} >all.log
$: grep . ???.log
all.log:drwxr-xr-x 1 paul 1049089 0 Jan  9 11:45 .
all.log:ls: cannot access 'bogus': No such file or directory
err.log:ls: cannot access 'bogus': No such file or directory

or to show the results as you go,或将结果显示为您 go,

$: { ls -ld . bogus 2> >( tee err.log ) ;} | tee all.log
drwxr-xr-x 1 paul 1049089 0 Jan  9 12:01 .
ls: cannot access 'bogus': No such file or directory
$: grep . ???.log
all.log:drwxr-xr-x 1 paul 1049089 0 Jan  9 12:01 .
all.log:ls: cannot access 'bogus': No such file or directory
err.log:ls: cannot access 'bogus': No such file or directory

It's also possible to do this inside your script, though it starts getting messy...也可以在您的脚本中执行此操作,尽管它开始变得混乱......

$: cat tst
#!/bin/bash
{ { # double-group all I/O
    ls -ld . bogus
} 2> >( tee err.log ) ;} | tee all.log # split by grouping

$: ./tst
ls: cannot access 'bogus': No such file or directory
drwxr-xr-x 1 paul 1049089 0 Jan  9 12:09 .

$: grep . ???.log
all.log:ls: cannot access 'bogus': No such file or directory
all.log:drwxr-xr-x 1 paul 1049089 0 Jan  9 12:09 .
err.log:ls: cannot access 'bogus': No such file or directory

You could also do something like -你也可以做类似的事情 -

#!/bin/bash
exec > >(tee all.log)
{ # group all I/O
  ls -ld . bogus
} 2> >( tee err.log )

though at that point I think the collation of stdout (which is buffered) and stderr (which is generally not) will decouple.尽管那时我认为 stdout(缓冲)和 stderr(通常不缓冲)的整理将解耦。 Logs with errors out of sync can be nightnarish to debug...错误不同步的日志调试起来可能会很晚...

addendum附录

how can I select to show the commands I want?我怎样才能 select 显示我想要的命令?

I usually use a function.我通常使用 function。

show() { echo "$@" | tee -a /dev/tty; }

This will still send a copy through STDOUT to the global logging, but will fork a copy to the terminal as well.这仍然会通过 STDOUT 将副本发送到全局日志记录,但也会将副本分叉到终端。

ls -ld . bogus # . listing to stdout, "bogus: not found" to stderr
show this goes to terminal # also stdout's log...

If you need to make it able to accept input on stdin instead of args -如果你需要让它能够接受 stdin 而不是 args 的输入 -

show() { 
  if (($#))
  then echo "$@"
  else cat
  fi | tee -a /dev/tty
}

$: show this to tty >x.log
this to tty

$: cat x.log
this to tty

$: ls -ld . bogus | show >x.log # doesn't catch stderr
ls: cannot access 'bogus': No such file or directory
drwxr-xr-x 1 paul 1049089 0 Jan 10 14:04 .

$: cat x.log
drwxr-xr-x 1 paul 1049089 0 Jan 10 14:04 .

Then yes, you have to explicitly use show instead of echo` for things you DEFINITELY want on your console, but you have options.那么是的,对于您在控制台上绝对想要的东西,您必须明确使用show instead of echo`,但是您有选择。 It will still work with the above code though.尽管如此,它仍然可以使用上面的代码。

wrapping up -包起来 -

if what you want is ONLY things you specify going to the console, try something like this -如果您想要的只是您指定的进入控制台的东西,请尝试这样的事情 -

$: cat tst
#! /bin/bash
exec 1> all.log          # all stdout to all.log
exec 2> >( tee err.log ) # stderr->err.log, tee'd to stdout->all.log
show() { if (($#)); then echo "$@"; else cat; fi | tee -a /dev/tty; }
# now just write your code, and use the show function for console output
ls -ld . bogus
show logged AND to screen.

$: ./tst
logged AND to screen.

$: cat err.log
ls: cannot access 'bogus': No such file or directory

$: cat all.log
ls: cannot access 'bogus': No such file or directory
drwxr-xr-x 1 paul 1049089 0 Jan 10 14:15 .
logged AND to screen.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将stdout和stderr重定向到一个文件,将stderr的副本重定向到另一个文件 - redirect stdout and stderr to one file, copy of just stderr to another 将标准输出重定向到文件并将标准错误重定向到同一个文件 - Redirect stdout to file and tee stderr to the same file 将stdout和stderr重定向到文件,将stderr重定向到stdout - Redirect stdout and stderr to file and stderr to stdout 将stderr和stdout重定向到同一文件的顺序 - Order of redirecting stderr and stdout to the same file 如何将stderr重定向到文件,并将某些回显(不是stdout)重定向到同一文件 - How to redirect stderr to a file and some echos (not stdout) to the same file 重定向屏幕上的stderr和文件,仅将stdout重定向到同一文件 - Redirect stderr on screen and a file, stdout on the same file only 首先重定向到文件 STDOUT 然后是 STDERR - Redirect to a file STDOUT first and then STDERR 从bash脚本中将stdout和stderr的副本重定向到一个文件,将stderr的副本重定向到另一个文件 - Redirect copy of stdout and stderr to one file, copy of just stderr to another file from within bash script 如果另一个成功,则执行命令,并将stdout和stderr记录到bash中 - Execute command if another is successful and log stdout and stderr to file in bash 将 stdout 和 stderr 显示到控制台和文件,将 stderr 着色为红色并在生成时显示 stdout 和 stderr - Display stdout and stderr to both console and file, color stderr red and display stdout and stderr as they are being produced
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM