Работа с потоками stdin, stdout, stderr

Метод thread-safe

Первым фрагментом необходимого нам кода является класс, который мы можем использовать для того, чтобы вычленить пишущее API TextCtrl. Вот довольно стандартный пример.

Python

class RedirectText():

def __init__(self, my_text_ctrl):
self.out = my_text_ctrl

def write(self,string):
wx.CallAfter(self.out.WriteText, string)

1
2
3
4
5
6
7

classRedirectText()

def__init__(self,my_text_ctrl)

self.out=my_text_ctrl

defwrite(self,string)

wx.CallAfter(self.out.WriteText,string)

Обратите внимание на то, что в данном классе используется лишь один метод (помимо метода инициализации, разумеется). Это позволяет нам записывать текст из stdout или stderr в текстовый контроль

Стоит отметить, что этот метод записи не является thread-safe. Если вам нужно перенаправить текст из thread, нужно немного видоизменить утверждение write следующим образом:

Python

def write(self, string):
wx.CallAfter(self.out.WriteText, string)

1
2

defwrite(self,string)

wx.CallAfter(self.out.WriteText,string)

Теперь, когда мы знаем, как записать stdout в TextCtrl, давайте попробуем самостоятельно написать немного кода, позволяющего собрать все части воедино. Вы также можете добавить следующий код в файл, содержащий класс, который был написан нами только что. Когда вы запустите код, то увидите приложение, которое выглядит примерно так:

Python

import sys
import wx

class RedirectText():

def __init__(self, my_text_ctrl):
self.out = my_text_ctrl

def write(self,string):
wx.CallAfter(self.out.WriteText, string)

class MyForm(wx.Frame):

def __init__(self):
wx.Frame.__init__(self, None, title=»wxPython Redirect Tutorial»)

# Добавляем панель, чтобы всё отображалось корректно на всех платформах
panel = wx.Panel(self, wx.ID_ANY)
log = wx.TextCtrl(panel, wx.ID_ANY, size=(300,100),
style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
btn = wx.Button(panel, wx.ID_ANY, ‘Push me!’)
self.Bind(wx.EVT_BUTTON, self.onButton, btn)

# Добавляем виджеты на сайзер
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(log, 1, wx.ALL|wx.EXPAND, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)

# Перенаправленный текст отправляется сюда
redir = RedirectText(log)
sys.stdout = redir

def onButton(self, event):
print(«You pressed the button!»)

if __name__ == «__main__»:
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

importsys

importwx

classRedirectText()

def__init__(self,my_text_ctrl)

self.out=my_text_ctrl

defwrite(self,string)

wx.CallAfter(self.out.WriteText,string)

classMyForm(wx.Frame)

def__init__(self)

wx.Frame.__init__(self,None,title=»wxPython Redirect Tutorial»)

# Добавляем панель, чтобы всё отображалось корректно на всех платформах

panel=wx.Panel(self,wx.ID_ANY)

log=wx.TextCtrl(panel,wx.ID_ANY,size=(300,100),

style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)

btn=wx.Button(panel,wx.ID_ANY,’Push me!’)

self.Bind(wx.EVT_BUTTON,self.onButton,btn)

# Добавляем виджеты на сайзер

sizer=wx.BoxSizer(wx.VERTICAL)

sizer.Add(log,1,wx.ALL|wx.EXPAND,5)

sizer.Add(btn,,wx.ALL|wx.CENTER,5)

panel.SetSizer(sizer)

# Перенаправленный текст отправляется сюда

redir=RedirectText(log)

sys.stdout=redir

defonButton(self,event)

print(«You pressed the button!»)

if__name__==»__main__»

app=wx.App(False)

frame=MyForm().Show()

app.MainLoop()

В коде, расположенном вверху, я создал мультистрочный текстовый контроль, доступный только для чтения, и кнопку, главной задачей которой является вывод текста на stdout. Я добавил их в BoxSizer, чтобы защитить виджеты от напяливания друг поверх друга и для того, чтобы не возникало проблем с изменением размера рамки. Затем я инициирую класс RedirectText, пропуская его через инстанцию моего текстового контроля. Наконец, я настраиваю stdout в инстанцию RedirectText, redir (например, sys.stdout=redir).

Если вы хотите перенаправить stderr, просто добавьте следующую строку после sys.stdout=redir: sys.stderr=redir.

Вы можете улучшить данный пример изменив цвета кода, поступающего из stdout и stderr для того, чтобы быстро их различать. Эту задачу я оставил для вас, чтобы вы могли поупражняться.

The scanf() and printf() Functions

The int scanf(const char *format, …) function reads the input from the standard input stream stdin and scans that input according to the format provided.

The int printf(const char *format, …) function writes the output to the standard output stream stdout and produces the output according to the format provided.

The format can be a simple constant string, but you can specify %s, %d, %c, %f, etc., to print or read strings, integer, character or float respectively. There are many other formatting options available which can be used based on requirements. Let us now proceed with a simple example to understand the concepts better −

#include <stdio.h>
int main( ) {

   char str;
   int i;

   printf( "Enter a value :");
   scanf("%s %d", str, &i);

   printf( "\nYou entered: %s %d ", str, i);

   return 0;
}

When the above code is compiled and executed, it waits for you to input some text. When you enter a text and press enter, then program proceeds and reads the input and displays it as follows −

$./a.out
Enter a value : seven 7
You entered: seven 7

Here, it should be noted that scanf() expects input in the same format as you provided %s and %d, which means you have to provide valid inputs like «string integer». If you provide «string string» or «integer integer», then it will be assumed as wrong input. Secondly, while reading a string, scanf() stops reading as soon as it encounters a space, so «this is test» are three strings for scanf().

Previous Page
Print Page

Next Page  

Перенаправление ошибок

Иногда, когда вы запускаете какую-либо команду или скрипт, вы видите, что на экране отображается сообщение об ошибке.

andreyex@destroyer:~$ ls -l ffffff > output.txt
ls: cannot access 'ffffff': No such file or directory

В начале этой статьи мы упоминали, что существует три потока данных, и stderr – это один из потоков выходных данных, который отображается на экране по умолчанию.

Вы также можете перенаправить stderr. Поскольку это поток выходных данных, вы можете использовать тот же символ перенаправления > или >>, который вы использовали для перенаправления стандартного вывода.

Но как вы различаете stdout и stderr, когда они оба являются потоком выходных данных? По их идентификатору потока (также называется дескриптором файла).

Поток данных Идентификатор потока
stdin
stdout 1
stderr 2

По умолчанию, когда вы используете выходной символ перенаправления >, это фактически означает 1>. Словом, вы говорите, что здесь выводится поток данных с ID 1.

Когда вам нужно перенаправить stderr, вы используете его идентификатор как 2> или 2>>. Это означает, что перенаправление вывода для потока данных stderr (ID 2).

Примеры перенаправления Stderr

Позвольте нам показать вам несколько примеров. Предположим, вы просто хотите сохранить ошибку, вы можете использовать что-то вроде этого:

andreyex@destroyer:~$ ls fffff 2> error.txt
andreyex@destroyer:~$ cat error.txt 
ls: cannot access 'fffff': No such file or directory

Это было просто. Давайте сделаем это немного более сложным (и полезным):

andreyex@destroyer:~$ ls -l new.txt ffff > output.txt 2> error.txt 
andreyex@destroyer:~$ cat output.txt 
-rw-rw-r-- 1 andreyex andreyex 0 May  5 10:12 new.txt
andreyex@destroyer:~$ cat error.txt 
ls: cannot access 'ffff': No such file or directory

В приведенном выше примере команда ls пытается отобразить два файла. Для одного файла она получает успех, а для другого – ошибку. Поэтому мы перенаправили stdout в ouput.txt (с>), а stderr в error.txt (с 2>).

Вы также можете перенаправить как stdout, так и stderr в один и тот же файл. Есть способы сделать это.

В приведенном ниже примере мы сначала отправляем stderr (с 2 >>) в файл комбинированный .txt в режиме добавления. Затем стандартный вывод (с >>) отправляется в тот же файл в режиме добавления.

andreyex@destroyer:~$ ls -l new.txt fff 2>> combined.txt >> combined.txt 
andreyex@destroyer:~$ cat combined.txt 
ls: cannot access 'fff': No such file or directory
-rw-rw-r-- 1 andreyex andreyex 0 May  5 10:12 new.txt

Другой способ, и он является предпочтительным, состоит в том, чтобы использовать что-то вроде 2>&1. Что можно примерно перевести как «перенаправить stderr на тот же адрес, что и stdout».

Давайте возьмем предыдущий пример и на этот раз используем 2>&1 для перенаправления как stdout, так и stderr в один и тот же файл.

andreyex@destroyer:~$ ls -l new.txt fff > output.txt 2>&1
andreyex@destroyer:~$ cat output.txt 
ls: cannot access 'fff': No such file or directory
-rw-rw-r-- 1 andreyex andreyex 0 May  5 10:12 new.txt

Имейте в виду, что вы не можете использовать 2>>&1, думая об использовании его в режиме добавления. 2>&1 уже переходит в режим добавления.

Вы также можете сначала использовать 2>, а затем 1>&2, чтобы перенаправить стандартный вывод в тот же файл, что и стандартный вывод. По сути, это «>&», который перенаправляет один поток данных в другой.

Резюме

  • Есть три потока данных. Один вход, stdin (0) и два потока выходных данных stdout (1) и stderr (2).
  • Клавиатура является стандартным устройством ввода, а экран является устройством вывода по умолчанию.
  • Перенаправление вывода используется с > или >> (для режима добавления).
  • Перенаправление ввода используется с <.
  • Stderr может быть перенаправлен с помощью 2> или 2>>.
  • Stderr и stdout можно комбинировать, используя 2>&1.

3. sys.stderr

This is similar to because it also prints directly to the Console. But the difference is that it only prints Exceptions and Error messages. (Which is why it is called Standard Error).

Let us illustrate this with an example.

import sys

stdout_fileno = sys.stdout
stderr_fileno = sys.stderr

sample_input = 

for ip in sample_input:
    # Prints to stdout
    stdout_fileno.write(ip + '\n')
    # Tries to add an Integer with string. Raises an exception
    try:
        ip = ip + 100
    # Catch all exceptions
    except:
        stderr_fileno.write('Exception Occurred!\n')

Output

Hi
Exception Occurred!
Hello from AskPython
Exception Occurred!
exit
Exception Occurred!

As you can observe, for all the input strings, we try to add to an Integer, which will raise an Exception. We catch all such exceptions and print another debug message using .

Redirection to a file

We can redirect the , and file handles to any other file (file-handle). This may be useful if you want to log events to a file without using any other module such as Logging.

The below snippet redirects output() to a file called .

So, we will not see anything printed to the Console, because it is now being printed to the file itself! This is the essence of Output redirection. You ‘redirect’ the Output to some other place. (This time, to , instead of the Console)

import sys

# Save the current stdout so that we can revert sys.stdou after we complete
# our redirection
stdout_fileno = sys.stdout

sample_input = 

# Redirect sys.stdout to the file
sys.stdout = open('Output.txt', 'w')

for ip in sample_input:
    # Prints to the redirected stdout (Output.txt)
    sys.stdout.write(ip + '\n')
    # Prints to the actual saved stdout handler
    stdout_fileno.write(ip + '\n')

# Close the file
sys.stdout.close()
# Restore sys.stdout to our old saved file handler
sys.stdout = stdout_fileno

Output

root@ubuntu:~# python3 output_redirection.py
Hi
Hello from AskPython
exit
root@ubuntu:~# cat Output.txt
Hi
Hello from AskPython
exit

As you can see, we have printed the output to both the Console, as well as to .

We first save the original file handler object to another Variable. We not only need this to restore to the old handler (pointing to the Console), but we can also print to the console using this Variable!

Note that after writing to the file, we close it, similar to how we close a file because that file was still open.

We finally restore the handler of to the Console, using the variable .

A similar process can be followed for Input and Error redirection, replacing with or and working with Inputs and Exceptions instead of Output.

Виды потоков

В системах Linux и Unix существуют стандартные входной (STDIN) и выходные (STDOUT, STDERR) потоки (каналы). Далее рассмотрим подробнее каждый из них.

  • STDIN (Номер файлового дескриптора — 0)Стандартный входной поток. Канал принимающий данные для обработки и последующей передачи на канал STDOUT и/или STDERR.
  • STDOUT (Номер файлового дескриптора — 1)Стандартный выходной поток. Представляет собой канал записи результатов выполнения каких-либо процессов.
  • STDERR (Номер файлового дескриптора — 2)Стандартный выходной поток ошибок. В данный канал попадают сообщения об ошибках.

В рамках терминала канал STDIN считывает входные данные, а каналы STDOUT и STDERR выводят выходные данные на экран.

NOTES top

       The stream stderr is unbuffered.  The stream stdout is line-buffered
       when it points to a terminal.  Partial lines will not appear until
       fflush(3) or exit(3) is called, or a newline is printed.  This can
       produce unexpected results, especially with debugging output.  The
       buffering mode of the standard streams (or any other stream) can be
       changed using the setbuf(3) or setvbuf(3) call.  Note that in case
       stdin is associated with a terminal, there may also be input
       buffering in the terminal driver, entirely unrelated to stdio
       buffering.  (Indeed, normally terminal input is line buffered in the
       kernel.)  This kernel input handling can be modified using calls like
       tcsetattr(3); see also stty(1), and termios(3).
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *