Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
session command
#1
file session.py is a command i created to start background screen sessions set to the same terminal geometry as started from, if available, or a specified geometry, that can be specified in many ways.  a session name is the only required argument.  the columns, rows, and starting command are optional.  the name, columns, and rows can be given inline as plain numbers, or assigned as keyed options in many ways, and abbreviated all the way down to one letter.  when given as plain numbers where the order does not matter, it determines which is columns and which is rows by alays havin the number of columns be the larger number.  when using keyed options, the values can be specifically assigned.  if no command string is given then the new session is stuffed with a comment indicating when the session was created. here are examples:

session 160 50 foo ping 8.8.4.4

start a screen session named "foo" with a virtual 160x80 terminal on a shell running a ping command to ping one of Google's generic DNS servers.

session 50 bar 160

start a screen session named "bar" with a virtual 160x80 terminal on a shell sitting idle with a command line comment stating when the session was started.

session co=80 ro=96 xyzzy

start a screen session named "xyzzy" with a virtual 80x96 terminal on a shell sitting idle with a command line comment stating when the session was started.

session //row 72 test1 64 ping localhost

start a screen session named "test1" with a virtual 64x72 terminal on a shell running a ping of localhost.  a message will be output indicating that since one number was explicitely specified as rows, the other number is assumed to be columns.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division, generators, print_function, with_statement
"""Create screen sessions with a specified name and optionally a specified width and height.

file          session.py
purpose       create screen sessions with a specified name and
              optionally a specified width and height

method        command
syntax        session <options> [ <session_input> ]

options       name (required), cols (optional), rows (optional)
option forms  {,-,+,*,/,--,++,**,//}name=<name>
              {,-,+,*,/,--,++,**,//}nam=<name>
              {,-,+,*,/,--,++,**,//}na=<name>
              {,-,+,*,/,--,++,**,//}n=<name>
              {-,+,*,/,--,++,**,//}name <name>
              {-,+,*,/,--,++,**,//}nam <name>
              {-,+,*,/,--,++,**,//}na <name>
              {-,+,*,/,--,++,**,//}n <name>
              cols and rows may also be specified in like forms
              name, cols and rows values may also be specified alone

email         10054452614123394844460370234029112340408691

The intent is that this module and command works correctly under both
Python 2 and Python 3.  Please report failures or code improvement to
the author.
"""

__license__ = """
Copyright © 2017, by Phil D. Howard - all other rights reserved

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

The author may be contacted by decoding the number
10054452614123394844460370234029112340408691
(provu igi la numeron al duuma)
"""

# source code configuration:

howto_detach = chr(29)+'d'


import os,fcntl,struct,termios

from os         import environ
from sys        import argv, stderr, stdin, stdout, version_info
from subprocess import call
from time       import localtime, sleep, strftime, time as secs

bases = { -8, -1, 0, 1, 2, 3, 4, 8, 10, 16, 32, 36 }
octal = {'','0','1','2','3','4','5','6','7'}
datef = '%Y-%m-%d %H:%M:%S'

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
exitcode = 0
def error(*args):
    global exitcode
    if not args:
        return 1
    if exitcode < args[0]:
        exitcode = args[0]
    print(*(args[1:]),file=stderr)
    return 1

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def geo(fd=1):
    try:
        h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
    except:
        h,w = None,None
    if not h:
        try:
            fd = os.open('/dev/tty',os.O_WRONLY)
            h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
            os.close(fd)
        except:
            h,w = None,None
    return w,h

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def intx(*a):
    "Convert digits to int, in an extended way, better than int()"

    b = bases
    if len(a) > 1:
        b = set(a[1])

    # i bet you do not understand this:
    d = a[0].lower()
    if bytes == str:
        d = ''.join([chr(c) for c in bytearray(d)])
    elif isinstance(d,(bytes,bytearray)):
        d = ''.join([chr(c) for c in bytearray(d)])

    if 0 in b:
        try:
            return intx(d,b-{0})
        except ValueError:
            return None

    if -1 in b and d[:1]=='-':
        return -intx(d[:1],b-{-1})

    if -2 in b:
        return -intx(d,b-{-2})

    # the order of the next 7 does not matter,
    # but is arranged in order of probability

    if 16 in b:
        if d[:2]=='0x':
            return int(d[2:],16)
        if d[:1]=='x':
            return int(d[1:],16)

    if 8 in b:
        if d[:2]=='0o':
            return int(d[2:],8)
        if d[:1]=='o':
            return int(d[1:],8)

    if 2 in b:
        if d[:2]=='0b':
            return int(d[2:],2)
        if d[:1]=='b':
            return int(d[1:],2)

    if 3 in b:
        if d[:2]=='0t':
            return int(d[2:],3)
        if d[:1]=='t':
            return int(d[1:],3)

    if 4 in b:
        if d[:2]=='0q':
            return int(d[2:],4)
        if d[:1]=='q':
            return int(d[1:],4)

    if 32 in b:
        if d[:2]=='0y':
            return int(d[2:],32)
        if d[:1]=='y':
            return int(d[1:],32)

    if 36 in b:
        if d[:1]==':':
            return int(d[1:],36)

    if -8 in b:
        if d[:1]=='0':
            if d[1:2] in octal:
                return int(d[1:],8)

    if 10 in b:
        return int(d,10)

    return None

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def main(args):
    errors = 0

    optm = {}
    for o in ('cols','rows','name'):
        for n in range(1,len(o)+1):
            optm[o[0:n]] = o

    cmd_name = args.pop(0)

    optd = {}
    namz =
    numz =
    while args:
        arg = args.pop(0)
        if not arg:
            errors += error(1,'empty argument',repr(arg))
        elif arg[0:2] in {'--','++','**','//'}:
            opt = arg[2:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,' Unknown assignment option name in',repr(arg))
        elif arg[0:1] in {'-','+','*','/'}:
            opt = arg[1:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'Unknown assignment option name in',repr(arg))
        elif '=' in arg:
            opt, val = arg.split('=',1)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'unknown assignment option name in',repr(arg))
        else:
            num = intx(arg)
            if isinstance(num,int):
                numz.append(num)
            else:
                if len(namz) > 0:
                    args = [ arg ] + args
                    break
                namz.append(arg)

    while True:
    
        if args:
            stuff_in = ' '.join(args)
        else:
            stuff_in = '# this screen session was started at {} by the {} commamd'.format(strftime(datef,localtime(secs())),repr(cmd_name))

            if 'name' in optd:
                namz.append(optd.pop('name'))

        if len(namz) < 1:
            errors += error(1,'no name given')
        if len(namz) > 1:
            errors += error(1,'internal logic error - more than one name - ',repr(namz))
        name = namz[0]

        if len(numz) == 0:
            if 'cols' not in optd or 'rows' not in optd:
                cols, rows = geo()
                if cols is None or rows is None:
                    errors += error(1,'cols and rows not specified and current geometry not available')
                optd['cols'], optd['rows'] = cols, rows
                break
        if len(numz) == 1:
            if 'cols' in optd and 'rows' in optd:
                errors += error(1,'more than two numbers specified - ',repr(numz))
                break
            if 'cols' not in optd and 'rows' not in optd:
                errors += error(1,'just one number specified - unknown if it is cols or rows - ',repr(numz))
                break
            if 'cols' in optd and 'rows' not in optd:
                print('cols specified so',numz[0],'presumed to be rows',file=stderr)
                optd['rows'] = numz[0]
                break
            if 'cols' not in optd and 'rows' in optd:
                print('rows specified so',numz[0],'presumed to be cols',file=stderr)
                optd['cols'] = numz[0]
                break
        if len(numz) == 2:
            cols, rows = numz
            if cols < rows:
                rows, cols = numz
            optd['cols'], optd['rows'] = cols, rows
            break
        if len(numz) > 2:
            errors += error(1,'more than two numbers specified - ',repr(numz))
            break

        break

    cols, rows = str(optd.pop('cols')), str(optd.pop('rows'))

    for k,v in optd.items():
        errors += error(1,'extra/unknown option specified',repr(k),'with value',repr(v))

    if errors or exitcode:
        print('aborting ...',file=stderr)
        print(file=stderr)
        print('syntax:',cmd_name,'<name> [ <columns> <rows> [ <input> ... ] ]',file=stderr)
        print('or:    ',cmd_name,'<name> [ <columns>x<rows> [ <input> ... ] ]',file=stderr)
        print('options: -cols -rows -name')
        return exitcode

    micro = int(secs()*1000000)
    # be sure the temp name can never begin with the real name
    tname = 'y'+str(micro)+name if name[0] == 'x' else 'x'+str(micro)+name

    bash_command = 'stty cols {} rows {};bash -i'.format(cols,rows)
    end_screen_1 = 'exit'                     +chr(13)
    new_screen_2 = 'screen -mDRR '+repr(name) +chr(13)
    
    environ['COLUMHS'] = environ['COLS']    = cols
    environ['LINES']   = environ['ROWS']    = rows

    call(['screen','-S',tname,'-dm','bash','-c',bash_command])
    sleep(1)
    call(['screen','-S',tname,'-X','stuff',new_screen_2])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',howto_detach])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',end_screen_1])
    sleep(0.5)
    call(['screen','-S',name,'-X','stuff',stuff_in+chr(13)])
    return 0

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
if __name__ == '__main__':
    if version_info.major < 3:
        BrokenPipeError = IOError
    try:
        result = main(argv)
        stdout.flush()
    except BrokenPipeError:
        result = 99
    except KeyboardInterrupt:
        print('')
        result = 98
    if result is 0 or result is None or result is True:
        exit(0)
    if result is 1 or result is False:
        exit(1)
    if isinstance(result,str):
        print(result,file=stderr)
        exit(2)
    try:
        exit(int(result))
    except ValueError:
        print(str(result),file=stderr)
        exit(3)
    except TypeError:
        exit(4)

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
#        111111111122222222223333333333444444444455555555556666666666777777777788
#23456789012345678901234567890123456789012345678901234567890123456789012345678901
#=======#=======#=======#=======#=======#=======#=======#=======#=======#=======#
# EOF
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#2
I get following error:

user@pc2:~/Downloads$ ./sess.py
  File "./sess.py", line 186
    namz =
         ^
Reply
#3
Hm! All good but what is the meaning of running a ping command in the background as you can't see the result? And how is this different from ping -c 4 8.8.4.4 &?
"As they say in Mexico 'dosvidaniya'. That makes two vidaniyas."
https://freedns.afraid.org
Reply
#4
(Nov-26-2017, 01:40 PM)heiner55 Wrote: I get following error:

user@pc2:~/Downloads$ ./sess.py
  File "./sess.py", line 186
    namz =
         ^

for whatever unknown reason, the on the RHS of the = in that and the next line are missing in the posted copy.

let's try that again:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division, generators, print_function, with_statement
"""Create screen sessions with a specified name and optionally a specified width and height.

file          session.py
purpose       create screen sessions with a specified name and
              optionally a specified width and height

method        command
syntax        session <options> [ <session_input> ]

options       name (required), cols (optional), rows (optional)
option forms  {,-,+,*,/,--,++,**,//}name=<name>
              {,-,+,*,/,--,++,**,//}nam=<name>
              {,-,+,*,/,--,++,**,//}na=<name>
              {,-,+,*,/,--,++,**,//}n=<name>
              {-,+,*,/,--,++,**,//}name <name>
              {-,+,*,/,--,++,**,//}nam <name>
              {-,+,*,/,--,++,**,//}na <name>
              {-,+,*,/,--,++,**,//}n <name>
              cols and rows may also be specified in like forms
              name, cols and rows values may also be specified alone

email         10054452614123394844460370234029112340408691

The intent is that this module and command works correctly under both
Python 2 and Python 3.  Please report failures or code improvement to
the author.
"""

__license__ = """
Copyright © 2017, by Phil D. Howard - all other rights reserved

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

The author may be contacted by decoding the number
10054452614123394844460370234029112340408691
(provu igi la numeron al duuma)
"""

# source code configuration:

howto_detach = chr(29)+'d'


import os,fcntl,struct,termios

from os         import environ
from sys        import argv, stderr, stdin, stdout, version_info
from subprocess import call
from time       import localtime, sleep, strftime, time as secs

bases = { -8, -1, 0, 1, 2, 3, 4, 8, 10, 16, 32, 36 }
octal = {'','0','1','2','3','4','5','6','7'}
datef = '%Y-%m-%d %H:%M:%S'

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
exitcode = 0
def error(*args):
    global exitcode
    if not args:
        return 1
    if exitcode < args[0]:
        exitcode = args[0]
    print(*(args[1:]),file=stderr)
    return 1

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def geo(fd=1):
    try:
        h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
    except:
        h,w = None,None
    if not h:
        try:
            fd = os.open('/dev/tty',os.O_WRONLY)
            h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
            os.close(fd)
        except:
            h,w = None,None
    return w,h

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def intx(*a):
    "Convert digits to int, in an extended way, better than int()"

    b = bases
    if len(a) > 1:
        b = set(a[1])

    # i bet you do not understand this:
    d = a[0].lower()
    if bytes == str:
        d = ''.join([chr(c) for c in bytearray(d)])
    elif isinstance(d,(bytes,bytearray)):
        d = ''.join([chr(c) for c in bytearray(d)])

    if 0 in b:
        try:
            return intx(d,b-{0})
        except ValueError:
            return None

    if -1 in b and d[:1]=='-':
        return -intx(d[:1],b-{-1})

    if -2 in b:
        return -intx(d,b-{-2})

    # the order of the next 7 does not matter,
    # but is arranged in order of probability

    if 16 in b:
        if d[:2]=='0x':
            return int(d[2:],16)
        if d[:1]=='x':
            return int(d[1:],16)

    if 8 in b:
        if d[:2]=='0o':
            return int(d[2:],8)
        if d[:1]=='o':
            return int(d[1:],8)

    if 2 in b:
        if d[:2]=='0b':
            return int(d[2:],2)
        if d[:1]=='b':
            return int(d[1:],2)

    if 3 in b:
        if d[:2]=='0t':
            return int(d[2:],3)
        if d[:1]=='t':
            return int(d[1:],3)

    if 4 in b:
        if d[:2]=='0q':
            return int(d[2:],4)
        if d[:1]=='q':
            return int(d[1:],4)

    if 32 in b:
        if d[:2]=='0y':
            return int(d[2:],32)
        if d[:1]=='y':
            return int(d[1:],32)

    if 36 in b:
        if d[:1]==':':
            return int(d[1:],36)

    if -8 in b:
        if d[:1]=='0':
            if d[1:2] in octal:
                return int(d[1:],8)

    if 10 in b:
        return int(d,10)

    return None

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def main(args):
    errors = 0

    optm = {}
    for o in ('cols','rows','name'):
        for n in range(1,len(o)+1):
            optm[o[0:n]] = o

    cmd_name = args.pop(0)

    optd = {}
    namz =
    numz =
    while args:
        arg = args.pop(0)
        if not arg:
            errors += error(1,'empty argument',repr(arg))
        elif arg[0:2] in {'--','++','**','//'}:
            opt = arg[2:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,' Unknown assignment option name in',repr(arg))
        elif arg[0:1] in {'-','+','*','/'}:
            opt = arg[1:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'Unknown assignment option name in',repr(arg))
        elif '=' in arg:
            opt, val = arg.split('=',1)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'unknown assignment option name in',repr(arg))
        else:
            num = intx(arg)
            if isinstance(num,int):
                numz.append(num)
            else:
                if len(namz) > 0:
                    args = [ arg ] + args
                    break
                namz.append(arg)

    while True:
    
        if args:
            stuff_in = ' '.join(args)
        else:
            stuff_in = '# this screen session was started at {} by the {} commamd'.format(strftime(datef,localtime(secs())),repr(cmd_name))

            if 'name' in optd:
                namz.append(optd.pop('name'))

        if len(namz) < 1:
            errors += error(1,'no name given')
        if len(namz) > 1:
            errors += error(1,'internal logic error - more than one name - ',repr(namz))
        name = namz[0]

        if len(numz) == 0:
            if 'cols' not in optd or 'rows' not in optd:
                cols, rows = geo()
                if cols is None or rows is None:
                    errors += error(1,'cols and rows not specified and current geometry not available')
                optd['cols'], optd['rows'] = cols, rows
                break
        if len(numz) == 1:
            if 'cols' in optd and 'rows' in optd:
                errors += error(1,'more than two numbers specified - ',repr(numz))
                break
            if 'cols' not in optd and 'rows' not in optd:
                errors += error(1,'just one number specified - unknown if it is cols or rows - ',repr(numz))
                break
            if 'cols' in optd and 'rows' not in optd:
                print('cols specified so',numz[0],'presumed to be rows',file=stderr)
                optd['rows'] = numz[0]
                break
            if 'cols' not in optd and 'rows' in optd:
                print('rows specified so',numz[0],'presumed to be cols',file=stderr)
                optd['cols'] = numz[0]
                break
        if len(numz) == 2:
            cols, rows = numz
            if cols < rows:
                rows, cols = numz
            optd['cols'], optd['rows'] = cols, rows
            break
        if len(numz) > 2:
            errors += error(1,'more than two numbers specified - ',repr(numz))
            break

        break

    cols, rows = str(optd.pop('cols')), str(optd.pop('rows'))

    for k,v in optd.items():
        errors += error(1,'extra/unknown option specified',repr(k),'with value',repr(v))

    if errors or exitcode:
        print('aborting ...',file=stderr)
        print(file=stderr)
        print('syntax:',cmd_name,'<name> [ <columns> <rows> [ <input> ... ] ]',file=stderr)
        print('or:    ',cmd_name,'<name> [ <columns>x<rows> [ <input> ... ] ]',file=stderr)
        print('options: -cols -rows -name')
        return exitcode

    micro = int(secs()*1000000)
    # be sure the temp name can never begin with the real name
    tname = 'y'+str(micro)+name if name[0] == 'x' else 'x'+str(micro)+name

    bash_command = 'stty cols {} rows {};bash -i'.format(cols,rows)
    end_screen_1 = 'exit'                     +chr(13)
    new_screen_2 = 'screen -mDRR '+repr(name) +chr(13)
    
    environ['COLUMHS'] = environ['COLS']    = cols
    environ['LINES']   = environ['ROWS']    = rows

    call(['screen','-S',tname,'-dm','bash','-c',bash_command])
    sleep(1)
    call(['screen','-S',tname,'-X','stuff',new_screen_2])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',howto_detach])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',end_screen_1])
    sleep(0.5)
    call(['screen','-S',name,'-X','stuff',stuff_in+chr(13)])
    return 0

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
if __name__ == '__main__':
    if version_info.major < 3:
        BrokenPipeError = IOError
    try:
        result = main(argv)
        stdout.flush()
    except BrokenPipeError:
        result = 99
    except KeyboardInterrupt:
        print('')
        result = 98
    if result is 0 or result is None or result is True:
        exit(0)
    if result is 1 or result is False:
        exit(1)
    if isinstance(result,str):
        print(result,file=stderr)
        exit(2)
    try:
        exit(int(result))
    except ValueError:
        print(str(result),file=stderr)
        exit(3)
    except TypeError:
        exit(4)

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
#        111111111122222222223333333333444444444455555555556666666666777777777788
#23456789012345678901234567890123456789012345678901234567890123456789012345678901
#=======#=======#=======#=======#=======#=======#=======#=======#=======#=======#
# EOF

and the links were removed.

(Nov-26-2017, 08:42 PM)wavic Wrote: Hm! All good but what is the meaning of running a ping command in the background as you can't see the result? And how is this different from ping -c 4 8.8.4.4 &?

you can get a snapshot peek at a disconnected screen.  i will be posting a script to make that easy in the near future.  you can also attach that screen session.  or run it with output to a file.

ok, i found the reason the post got mangled.  i typed in the tag to begin python code ("python" in square brackets), but forgot the ending tag ("/python" in square brackets) after i pasted in the code.  then i clicked on preview.  that's when it was mangled ... but i didn't see it.   i scrolled down to the end and type in the missing ending tag.   then i did another preview but did not see the missing brackets on lines 186 or 187 or anywhere else they may be missing from.  then i posted the post which was already mangled in the edit area, too.

IMHO, this is a MyBB bug.  even if i mistype something, a preview should not modify it.  i tried to repost it above and made exactly the same mistake (and thought "damn, did it, again").  that led me to check and see the same problem.  this time i will not forget, i promise.  and those 2 lines look OK in the ptreview.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division, generators, print_function, with_statement
"""Create screen sessions with a specified name and optionally a specified width and height.

file          session.py
purpose       create screen sessions with a specified name and
              optionally a specified width and height

method        command
syntax        session <options> [ <session_input> ]

options       name (required), cols (optional), rows (optional)
option forms  {,-,+,*,/,--,++,**,//}name=<name>
              {,-,+,*,/,--,++,**,//}nam=<name>
              {,-,+,*,/,--,++,**,//}na=<name>
              {,-,+,*,/,--,++,**,//}n=<name>
              {-,+,*,/,--,++,**,//}name <name>
              {-,+,*,/,--,++,**,//}nam <name>
              {-,+,*,/,--,++,**,//}na <name>
              {-,+,*,/,--,++,**,//}n <name>
              cols and rows may also be specified in like forms
              name, cols and rows values may also be specified alone

email         10054452614123394844460370234029112340408691

The intent is that this module and command works correctly under both
Python 2 and Python 3.  Please report failures or code improvement to
the author.
"""

__license__ = """
Copyright © 2017, by Phil D. Howard - all other rights reserved

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

The author may be contacted by decoding the number
10054452614123394844460370234029112340408691
(provu igi la numeron al duuma)
"""

# source code configuration:

howto_detach = chr(29)+'d'


import os,fcntl,struct,termios

from os         import environ
from sys        import argv, stderr, stdin, stdout, version_info
from subprocess import call
from time       import localtime, sleep, strftime, time as secs

bases = { -8, -1, 0, 1, 2, 3, 4, 8, 10, 16, 32, 36 }
octal = {'','0','1','2','3','4','5','6','7'}
datef = '%Y-%m-%d %H:%M:%S'

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
exitcode = 0
def error(*args):
    global exitcode
    if not args:
        return 1
    if exitcode < args[0]:
        exitcode = args[0]
    print(*(args[1:]),file=stderr)
    return 1

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def geo(fd=1):
    try:
        h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
    except:
        h,w = None,None
    if not h:
        try:
            fd = os.open('/dev/tty',os.O_WRONLY)
            h,w = struct.unpack('4H',fcntl.ioctl(fd,termios.TIOCGWINSZ,struct.pack('4H',0,0,0,0)))[:2]
            os.close(fd)
        except:
            h,w = None,None
    return w,h

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def intx(*a):
    "Convert digits to int, in an extended way, better than int()"

    b = bases
    if len(a) > 1:
        b = set(a[1])

    # i bet you do not understand this:
    d = a[0].lower()
    if bytes == str:
        d = ''.join([chr(c) for c in bytearray(d)])
    elif isinstance(d,(bytes,bytearray)):
        d = ''.join([chr(c) for c in bytearray(d)])

    if 0 in b:
        try:
            return intx(d,b-{0})
        except ValueError:
            return None

    if -1 in b and d[:1]=='-':
        return -intx(d[:1],b-{-1})

    if -2 in b:
        return -intx(d,b-{-2})

    # the order of the next 7 does not matter,
    # but is arranged in order of probability

    if 16 in b:
        if d[:2]=='0x':
            return int(d[2:],16)
        if d[:1]=='x':
            return int(d[1:],16)

    if 8 in b:
        if d[:2]=='0o':
            return int(d[2:],8)
        if d[:1]=='o':
            return int(d[1:],8)

    if 2 in b:
        if d[:2]=='0b':
            return int(d[2:],2)
        if d[:1]=='b':
            return int(d[1:],2)

    if 3 in b:
        if d[:2]=='0t':
            return int(d[2:],3)
        if d[:1]=='t':
            return int(d[1:],3)

    if 4 in b:
        if d[:2]=='0q':
            return int(d[2:],4)
        if d[:1]=='q':
            return int(d[1:],4)

    if 32 in b:
        if d[:2]=='0y':
            return int(d[2:],32)
        if d[:1]=='y':
            return int(d[1:],32)

    if 36 in b:
        if d[:1]==':':
            return int(d[1:],36)

    if -8 in b:
        if d[:1]=='0':
            if d[1:2] in octal:
                return int(d[1:],8)

    if 10 in b:
        return int(d,10)

    return None

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
def main(args):
    errors = 0

    optm = {}
    for o in ('cols','rows','name'):
        for n in range(1,len(o)+1):
            optm[o[0:n]] = o

    cmd_name = args.pop(0)

    optd = {}
    namz =
    numz =
    while args:
        arg = args.pop(0)
        if not arg:
            errors += error(1,'empty argument',repr(arg))
        elif arg[0:2] in {'--','++','**','//'}:
            opt = arg[2:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,' Unknown assignment option name in',repr(arg))
        elif arg[0:1] in {'-','+','*','/'}:
            opt = arg[1:]
            if '=' in opt:
                opt, val = opt.split('=',1)
            else:
                if not args:
                    errors += error(1,'no argument for',repr(arg))
                val = args.pop(0)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'Unknown assignment option name in',repr(arg))
        elif '=' in arg:
            opt, val = arg.split('=',1)
            if opt in optm:
                optd[optm[opt.lower()]] = val
            else:
                errors += error(1,'unknown assignment option name in',repr(arg))
        else:
            num = intx(arg)
            if isinstance(num,int):
                numz.append(num)
            else:
                if len(namz) > 0:
                    args = [ arg ] + args
                    break
                namz.append(arg)

    while True:
    
        if args:
            stuff_in = ' '.join(args)
        else:
            stuff_in = '# this screen session was started at {} by the {} commamd'.format(strftime(datef,localtime(secs())),repr(cmd_name))

            if 'name' in optd:
                namz.append(optd.pop('name'))

        if len(namz) < 1:
            errors += error(1,'no name given')
        if len(namz) > 1:
            errors += error(1,'internal logic error - more than one name - ',repr(namz))
        name = namz[0]

        if len(numz) == 0:
            if 'cols' not in optd or 'rows' not in optd:
                cols, rows = geo()
                if cols is None or rows is None:
                    errors += error(1,'cols and rows not specified and current geometry not available')
                optd['cols'], optd['rows'] = cols, rows
                break
        if len(numz) == 1:
            if 'cols' in optd and 'rows' in optd:
                errors += error(1,'more than two numbers specified - ',repr(numz))
                break
            if 'cols' not in optd and 'rows' not in optd:
                errors += error(1,'just one number specified - unknown if it is cols or rows - ',repr(numz))
                break
            if 'cols' in optd and 'rows' not in optd:
                print('cols specified so',numz[0],'presumed to be rows',file=stderr)
                optd['rows'] = numz[0]
                break
            if 'cols' not in optd and 'rows' in optd:
                print('rows specified so',numz[0],'presumed to be cols',file=stderr)
                optd['cols'] = numz[0]
                break
        if len(numz) == 2:
            cols, rows = numz
            if cols < rows:
                rows, cols = numz
            optd['cols'], optd['rows'] = cols, rows
            break
        if len(numz) > 2:
            errors += error(1,'more than two numbers specified - ',repr(numz))
            break

        break

    cols, rows = str(optd.pop('cols')), str(optd.pop('rows'))

    for k,v in optd.items():
        errors += error(1,'extra/unknown option specified',repr(k),'with value',repr(v))

    if errors or exitcode:
        print('aborting ...',file=stderr)
        print(file=stderr)
        print('syntax:',cmd_name,'<name> [ <columns> <rows> [ <input> ... ] ]',file=stderr)
        print('or:    ',cmd_name,'<name> [ <columns>x<rows> [ <input> ... ] ]',file=stderr)
        print('options: -cols -rows -name')
        return exitcode

    micro = int(secs()*1000000)
    # be sure the temp name can never begin with the real name
    tname = 'y'+str(micro)+name if name[0] == 'x' else 'x'+str(micro)+name

    bash_command = 'stty cols {} rows {};bash -i'.format(cols,rows)
    end_screen_1 = 'exit'                     +chr(13)
    new_screen_2 = 'screen -mDRR '+repr(name) +chr(13)
    
    environ['COLUMHS'] = environ['COLS']    = cols
    environ['LINES']   = environ['ROWS']    = rows

    call(['screen','-S',tname,'-dm','bash','-c',bash_command])
    sleep(1)
    call(['screen','-S',tname,'-X','stuff',new_screen_2])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',howto_detach])
    sleep(0.25)
    call(['screen','-S',tname,'-X','stuff',end_screen_1])
    sleep(0.5)
    call(['screen','-S',name,'-X','stuff',stuff_in+chr(13)])
    return 0

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
if __name__ == '__main__':
    if version_info.major < 3:
        BrokenPipeError = IOError
    try:
        result = main(argv)
        stdout.flush()
    except BrokenPipeError:
        result = 99
    except KeyboardInterrupt:
        print('')
        result = 98
    if result is 0 or result is None or result is True:
        exit(0)
    if result is 1 or result is False:
        exit(1)
    if isinstance(result,str):
        print(result,file=stderr)
        exit(2)
    try:
        exit(int(result))
    except ValueError:
        print(str(result),file=stderr)
        exit(3)
    except TypeError:
        exit(4)

#-------#-------#-------#-------#-------#-------#-------#-------#-------#-------#
#        111111111122222222223333333333444444444455555555556666666666777777777788
#23456789012345678901234567890123456789012345678901234567890123456789012345678901
#=======#=======#=======#=======#=======#=======#=======#=======#=======#=======#
# EOF

and the post looks OK.

now where did the links go that i put on the file names ... linuxhomepage DOT com FORWARD SLASH free FORWARD SLASH session DOT py

the sha256 checksum is:
6bb17c0daccc59d5cf538686ffebef68692f55be59d71e880ab8e5f5748e6544
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#5
OK, I will try later.
Reply
#6
fyi, i have renamed this to sessionbg.py, and wrote sessionin.py, sessionls.py, and sessionss.py.  on my laptop i have made symlink aliases for them: sbg, sin, sls, and sss.  i do regularly run stuff in background screen sessions.  i have about 9 of them running, right now, including my music.  i hope to have X windows in the background some day by means of the vnc protocol.  i need to figure out a good way to drive programmatic text input to the vnc client, as well as grab snapshots.

sbg = create a screen session in the background, with optional input

sin = do input to a screen session, with control character conversion

sls = list screen sessions with name and id, basically "screen -ls"

sss = snap shot a screen session and output all that were named
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020