LINUX.ORG.RU

bash wrapper

 , ,


0

1

Я решил интегрировать свои кириллические алиасы в самописную командную оболочку (надстройку над bash). Порылся в гугле, и родил вот такой код (Github).

alias см='cd'
alias ск='mkdir'
alias сс='ln -s'

Подскажите, почему при Permission denied (выполняю ls /root/) поток stderr можно прочитать только за два цикла while not IOError: p.stdout.read()? (у меня вывод stderr посылается в stdout)

То есть, вначале получается прочитать `ls: cannot open directory /root/`, и только после этого - конец сообщения `: Permission denied`.

С чем это связано?

★★★★★

вопрос не понял, вероятно:

$ locale
LANG=ru_RU.UTF-8
LANGUAGE=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=ru_RU.UTF-8

quester ★★
()
Последнее исправление: quester (всего исправлений: 1)
Ответ на: комментарий от quester

Сообщение не теряется (просто читается в два захода).

Локаль у меня на тестовой машине правильная, я тут привёл английский вариант, так как под рукой оказался лишь Debian без русской локали.

pacify ★★★★★
() автор топика
Ответ на: комментарий от quester

quester, запустил я на «родной» машине:

> ls /root/
ls: > 
> ls /
невозможно открыть каталог /root/: Отказано в доступе
> 
Причину понял. У меня файл в неблокирующем режиме, и после отправки команды в bash, ответ ещё не сформирован, а я пытаюсь его прочитать. Так быстро ответ в командном интерпретаторе сформироваться не может. Я вставил искуственно задержку после отправки команды write(), и получил корректный ответ:
Linux()
> ls /root/
ls: невозможно открыть каталог /root/: Отказано в доступе
> exit
На stackoverflow советуют использовать asyncio (asyncore) или selectors.

pacify ★★★★★
() автор топика
Ответ на: комментарий от kardapoltsev

А что означают твои алиасы? см — сменить место?

Да, типа того.

Посоветовались тут с другом, решили забить алиасы в python-код (конфиг). Чтобы работало и под Windows (там ведь команды alias нет).

Вчера решил не делать screen scraping, а просто валить stdout весь подряд на экран.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

Сообщение не теряется (просто читается в два захода).

Это нормально. Такое происходит когда внутри программы реализовано в два вывода, вначале выводится сообщение в конкретном месте, что не смогла, а потом вызывается печать системной ошибки из кода уже при аварийном завершении. При чтении из pipe проиходит получение двух буферов. stderr не буферизуется и потому выводится всё одним куском с fflush при каждом выводе.

У меня файл в неблокирующем режиме, и после отправки команды в bash, ответ ещё не сформирован, а я пытаюсь его прочитать.

Это хорошая догадка, но не правильная. Как вы узнаете, что ответ уже полностью сформирован? А если там много-много чего будет написано, типа хелпа по вызову программы? Этот хелп при приличных размерах может даже при всё желании не поместиться в один PIPE_BUF.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 3)
Ответ на: комментарий от pacify

Я вставил искуственно задержку

И вашим враппером будут пользоваться только вы. :)

советуют использовать asyncio

Можно подумать, оно поможет.

С чего вы решили, что пользователь хочет видеть вывод только после завершения запущенных программ, а не в процессе появления от них сообщений?

vodz ★★★★★
()
Ответ на: комментарий от vodz

И вашим враппером будут пользоваться только вы. :)

Лол :-) Как будто есть шанс на нечто другое :-) Лол :-)

anonymous
()
Ответ на: комментарий от pacify

см — сменить место

забить алиасы в python-код (конфиг).

Чтобы работало и под Windows

У меня от такого ненужно чуть не лопнул экран.

legolegs ★★★★★
()
Ответ на: комментарий от vodz

Как вы узнаете, что ответ уже полностью сформирован?

По наличию Command prompt в stdout (PS1 env).

pacify ★★★★★
() автор топика
Ответ на: комментарий от vodz

С чего вы решили, что пользователь хочет видеть вывод только после завершения запущенных программ

У меня сейчас дамп stdout-а реализован в отдельном потоке с ждущим select(). Попробуй, запусти yes - будет выдаваться в рилтайме.

Я вставил искуственно задержку

И вашим враппером будут пользоваться только вы. :)

Увы, вчера надо было ложиться спать, и успеть отправить коммит - чтобы друг прочитал.

Парсить на наличие Command prompt я пока не хочу.

Сорцы выкладываю ведь не с подписью «в продакшн», а для тех - кто умеет читать. Вон, Гоголь тоже не дописал свои романы.

pacify ★★★★★
() автор топика
Ответ на: комментарий от vodz

При чтении из pipe проиходит получение двух буферов.

Да, собственно, никто не гарантирует, что не прилетит третий буфер. Окончание вывода баша можно определить лишь по наличию Command prompt, который задаётся в качестве переменных среды при запуске процесса (Popen()).

stderr не буферизуется и потому выводится всё одним куском с fflush при каждом выводе.

Что ты тут написал, я не понял. Ты внутрь bash заглядывал, и увидел там fflush()?

Понятие буфера - это внутреннее дело операционной системы, и не имеет никакого отношения к прикладному уровню обмена данными. У меня вопрос лишь был, когда выдавать следующий command prompt в своём враппере, какой read() считать «последним».

Мой sleep(0.2) - чисто косметический, для быстрых команд. И не сработает при seq 99999, например.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

Ты внутрь bash заглядывал, и увидел там fflush()?

Куда я только не заглядывал... Но вас подпускать к программированию совсем нельзя. У вас в корне неправильные представления как работает система. Какой нафиг bash? У вас выводит сообщение в stderr команда ls, башу, если ему не сказано как-то читать stderr, на эти сообщения вообще плевать.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Я тебе говорю, мне без разницы - какие там буфера в операционной системе. Я работаю с данными. Собственно, ключевое слово было - screen scraping, и разговор с человеком, который такой парсинг выхлопа баша уже писал. Я ж про прикладной уровень.

То, то ты подтвердил мои догадки про два write(), за это спасибо. Я лишь хотел сказать, ты там сам видел fflush(), или просто теоретизируешь?

pacify ★★★★★
() автор топика
Последнее исправление: pacify (всего исправлений: 2)
Ответ на: комментарий от vodz

У вас в корне неправильные представления как работает система. Какой нафиг bash? У вас выводит сообщение в stderr команда ls.

Ну, поспешил я. ls может быть интегрирован и в оболочку, я не хочу вдаваться в эти мелочи.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

мне без разницы

плохо.

какие там буфера в операционной системе

Это важно только для понимания, ибо этим управлять весьма-весьма проблематично.

Я работаю с данными. Собственно, ключевое слово было - screen scraping,

ну то есть вы работаете не с bash, а с терминалом. Это тоже полезная вещь, но тогда никакой надежды на PROMPT bash-а быть не может, работать надо именно с поступающей информацией как есть, а не парсить что-то там в надежде на какие-то их порции и промпт.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Это тоже полезная вещь, но тогда никакой надежды на PROMPT bash-а быть не может

Что-то я запутался. Приглашение ко вводу выдаёт всё-таки xterm, на основе переменных среды?

Или bash в интерактивном режиме сам печатает $PS1?

pacify ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Да, я уже посмотрел исходники.

Запускаю такой py-скрипт:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# MIT/Expat License

import sys
import os
from subprocess import Popen, PIPE, STDOUT
import errno
import select
from multiprocessing import Process
import time

def Dump(fd):
        reads = [fd]
        while True:
                ret = select.select(reads, [], [])
                s = os.read(ret[0][0],4096)
                sys.stdout.write(s)
                sys.stdout.flush()

p = Popen(["/bin/bash","-i"], shell = False,
stdin = PIPE, stdout = PIPE, stderr = STDOUT, bufsize = 1, env={"PS1":"\\h"})
proc = Process(target=Dump, args=(p.stdout.fileno(),))
proc.start()

while True:
        ss = raw_input("> ")
        if ss == "exit":
                proc.terminate()
                break
        if len(ss) == 0: continue # ничего кроме пробельных символов нет
        try:
                p.stdin.write(ss+"\n")
                p.stdin.flush()
        except IOError as e:
                print repr(e)
                if e.errno == errno.EPIPE:
                        break

        time.sleep(0.2) # чтобы выхлоп stdout не затирал промт

У друга на машине всё работает нормально. Выдаёт промт \h

У меня вылетает на шаге raw_input(), и через некоторое время упавший процесс становится зомби (Z). Debian 8.6 x86_64. Я пока в непонятках. Завтра буду смотреть на ROSA Fresh 10.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

Здесь не подскажу, уж извиняй - в python нуб

anonymous
()
Ответ на: комментарий от pacify

Приглашение ко вводу выдаёт всё-таки xterm, на основе переменных среды?

xterm эмулирует графическую виртуальную консоль. Ему всё равно, bash там или текставая игра там запускается в качестве shell-а. Может там и 100 башей в параллель дерутся за ввод пользователя. xterm-у на это фиолетово, он выводит буковки, рисуя их в графике. Всё.

А вы занимаетесь какой-то фигнёй. Заставляете bash работать в интерактивном режиме, подсовывая ему pipe и пытаетесь что-то там предсказать на основе полученных символов. Совершенно пустая затея.

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

Что-то ты мне паришь мозги, vodz. Про терминал зачем-то начал объяснять. Как связан терминал и PROMPT, выдаваемый башем?

pacify ★★★★★
() автор топика

В общем, скрипт дописал (линуксовая версия). Ответ нашёл здесь: https://stackoverflow.com/a/41593121 (как грузить интерактивный bash)

#!/usr/bin/python
# -*- coding: utf-8 -*-
# MIT/Expat License

import sys
import os
from subprocess import Popen, PIPE, STDOUT
import errno
import select
from multiprocessing import Process
import time

def Dump(fd):
        reads = [fd]
        while True:
                ret = select.select(reads, [], [])
                s = os.read(ret[0][0],4096)
                sys.stdout.write(s)
                sys.stdout.flush()

# https://stackoverflow.com/questions/41542960/run-interactive-bash-with-popen-and-a-dedicated-tty-python

import ctypes

libc = ctypes.CDLL('libc.so.6')

p = Popen(["/bin/bash","-i"], shell = False,
        stdin = PIPE, stdout = PIPE, stderr = STDOUT, bufsize = 1,
        env={"PS1":"\\h"},
        preexec_fn=libc.setsid)

proc = Process(target=Dump, args=(p.stdout.fileno(),))
proc.start()

while True:
        ss = raw_input("> ")
        if ss == "exit":
                proc.terminate()
                break
        if len(ss) == 0: continue # ничего кроме пробельных символов нет
        try:
                p.stdin.write(ss+"\n")
                p.stdin.flush()
        except IOError as e:
                print repr(e)
                if e.errno == errno.EPIPE:
                        break

        time.sleep(0.2) # чтобы выхлоп stdout не затирал промт

Теперь уже можно будет парсить выхлоп баша (PROMPT), если нужно будет.

pacify ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.