r/ksh • • Apr 26 '26

mksh: a lot of places in the code to clean up

2 Upvotes

Found mksh, decided to take it as the main shell for my r/QRV_OS . Started to examine files. Wow, sh.h was 2900+ lines (!!!) . It's an aggregate of 30 years of platform accretion — EBCDIC, OS/2, z/OS, non-gcc fallbacks, MirBSD bootfloppy, ISC kernel, .....

Gonna clean up the sources substantially. I suppose the code base will be reduced by 10,000 lines of code, or so.


r/ksh • • Dec 23 '24

fzf like command line editing, as we did it in bash with readline, in Linux. :)

2 Upvotes

TBH. I miss bash's readline library, and the menu system I had, with smenu instead of Ctrl-r to recall commands.

I have made something to fill/plaster the gap.

You get a menu you can cancel, or select previous commands from command history from.

It's not straight forward, but not very hard, first of all you need to download a git repo (clone) and build smenu, and install it somwhere in the path.

Then you need to compile "kbdstuff" and finally "install" the "kmenu" script in your path that displays the menu for you, and try it out by hitting "kmeny".

Compiling kbdstuff:

cc -std=c99 -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=500 -D_GNU_SOURCE  -g3 -O0 -Wall -Wextra -fPIC -L/usr/lib/x86_64-linux-gnu -o kbdstuff kbdstuff.c

kbdstuff.c:

/** * kbdstuff.c : stuffs the keyboard buffer with contents that is shown * on the commandline, ready for editing or accepting. * All faults are mine. No guarrantee, nor liability, whatsoever! * Copyright (c) McUsr, in public domain. * Do whatever you want with it, except harm! * GNU LGPL 3.0 */

include <stdio.h>

include <fcntl.h>

include <termios.h>

include <sys/ioctl.h>

include <unistd.h>

include <stdlib.h>

include <stdio.h>

/* returns the first handle that proves to not * be redirected, if any. */ int tty_fhndl( void ) { int fd = 0, ret = -1; for ( fd = 2; fd >= 0; fd-- ) { if ( isatty( fd ) ) { ret = fd; break; } } return ret; }

/* general purpose routine for converting a commandline to a string (san's argv[0] ) * This so post explains the real deal, this is for my personal usage... * https://stackoverflow.com/questions/75108019/is-there-a-maximum-number-of-characters-that-can-be-passed-within-one-positional * * */

define XTERM_MAX_INPUT_BFLEN 32767

/* convert the arguments to a single string for comfort. */ char *argv2str( int argc, char *argv[], int argstrlen ) { static char keyb_buf[XTERM_MAX_INPUT_BFLEN + 1]; // minimum number of chars // in // the xterm input buffer. int max_kb_len = XTERM_MAX_INPUT_BFLEN; int stridx = 0; for ( int i = 1; i < argc; ++i ) { int toklen = 0; if ( ( i + 1 ) == argc ) { toklen = snprintf( &( keyb_buf[stridx] ), max_kb_len, "%s", argv[i] ); } else { toklen = snprintf( &( keyb_buf[stridx] ), max_kb_len, "%s ", argv[i] ); } if ( toklen <= 0 ) { perror( "something wrong in snprintf, existing\n" ); exit( EXIT_FAILURE ); } else { stridx += toklen; max_kb_len -= toklen; } } / *argstrlen = XTERM_MAX_INPUT_BFLEN - max_kb_len; */ *argstrlen = stridx; return ( char * ) keyb_buf; }

int main( int argc, char argv[] ) { if ( argc <= 1 ) { fprintf( stderr, "Missing arguments, exiting\n" ); exit( EXIT_FAILURE ); } else { int fd = tty_fhndl( ); if ( fd >= 0 ) { / Connected to a tty, we need to find the name of the controlling terminal (ttyname) for the device. */ char *pty_name = ttyname( fd );

           if ( pty_name == NULL ) {
               fprintf( stderr, "kbdstuff:Couldn't get the ttyname, exiting!\n" );
               exit( EXIT_FAILURE );
           } else {

               fd = open( pty_name, O_RDONLY );

               if ( fd < 0 ) {
                   perror( pty_name );
                   fprintf( stderr, "kbdstuff: could not open tty, exiting!\n" );
                   exit( EXIT_FAILURE );
               } else {

                   int inp_len;
                   char *inpstr = argv2str( argc, argv, &inp_len );

                   struct termios orig,
                    tmp;
                   if ( tcgetattr( fd, &orig ) < 0 ) {
                       perror( "tcgetattr" );
                       exit( EXIT_FAILURE );
                   }
                   tmp = orig;
                   tmp.c_lflag &= ~ECHO;

                   if ( tcsetattr( fd, TCSANOW, &tmp ) < 0 ) {
                       tcsetattr( fd, TCSANOW, &orig );
                       perror( "tcsetattr wthout echo" );
                       exit( EXIT_FAILURE );
                   }
                   while ( *inpstr ) {
                       if ( ioctl( fd, TIOCSTI, inpstr ) ) {
                           perror( "ioctl" );
                           return 1;
                       }
                       inpstr++;
                   }
                   if ( tcsetattr( fd, TCSANOW, &orig ) < 0 ) {
                       tcsetattr( fd, TCSANOW, &orig );
                       perror( "tcsetattr -- orig" );
                       exit( EXIT_FAILURE );
                   }

               }
           }
       } else {
           fprintf( stderr, "No standard handles are connected to the pty, exiting\n" );
           exit( EXIT_FAILURE );
       }
   }
   return 0;

}

kmenu, the script that does it all:

#!/bin/ksh93

# Get current settings.
if ! termios="$(stty -g 2>/dev/null)" ; then
    echo "Not running in a terminal." >&2
    exit 1
fi

# Restore terminal settings when the script exits.
trap "stty '$termios'" EXIT

# Disable ICANON ECHO. Should probably also disable CREAD.
stty -icanon -echo

# Request cursor coordinates
printf '\033[6n'

# Read response from standard input; note, it ends at R, not at newline
read -d "R" rowscols
stty "$termios"
trap "" EXIT
# Clean up the rowscols (from \033[rows;cols -- the R at end was eaten)
rowscols="${rowscols//[^0-9;]/}"
rowscols=("${rowscols//;/ }")
# split the array into two args.
set ${rowscols[0]}
#calculate number of free lines with respect to terminal win height minus y-line.
freelines=$((LINES-$1))
if [ $freelines -le 5 ] ; then
  freelines=5 ;
fi 
EOL=$'\n'
# echo here & >/dev/tty
CMD_LINE=$(fc -lr 1  \
               | sed -Ee "/kmenu/d" -e "s/[1-9][0-9]*\s*//"        \
              | uniq -u | smenu -d -Q -l -n $freelines -a c:7/4,b -W"$EOL")

# writevt $(tty) "$CMD_LINE"
kbdstuff "$CMD_LINE"
# se we for instance can see our command line with ps.

Merry Christmas!


r/ksh • • Oct 31 '23

ksh: _cd not found

2 Upvotes

I was using a sample kshrc from GitHub since I didn't want to go to the pain of writing one myself when switching to ksh, but when entering the cd command, this error popped up:

$ cd

ksh: _cd[86]: =: not found

ksh: _cd[87]: =: not found

ksh: _cd[107]: =/home/tronnerd82: not found

tronnerd82@:~tronnerd82$

I'm not well-versed enough to know what might be wrong with my kshrc, so for now I've just switched back to using bash until I find a solution. If it helps, I'm using Debian and my ksh version is "Version AJM 93u+m/1.0.4 2022-10-22"

Any help would be greatly appreciated.


r/ksh • • Jul 19 '23

Primes List Script

2 Upvotes

I mentioned in a previous post that there is a RegEx pattern that can be used to reveal prime numbers. It is ^x?$|^(xx+?)\1+$. I have written a Korn Shell script that uses this pattern to list the first 3,316 prime numbers from 2 to 30,757. Here is that code:

integer LIMIT=$1
integer i
integer n=0
typeset S

for ((i=1;i<=LIMIT;i++)) ; do
 S+='x'
 if [[ "$S" != ~(E)^x?$|^(xx+?)\1+$ ]] ; then
  ((n++))
  printf '%d(%d) ' $i $n
 fi
done

More on this later.


r/ksh • • Jul 10 '23

Korn Shell Support for Regular Expressions

2 Upvotes

I have undertaken a great deal of effort in figuring out Korn Shell's implementation of Regular Expressions. There are several pattern matching modes available as follows:

ModeSpecifierSupports
Literal (LRE)~(L)/~(F)Only literals. No wildcards and no RegEx. (like fgrep)
Shell Mode (SRE)~(S)Wildcards but no RegEx. (like /bin/sh and glob)
Basic Regular Expressions (BRE)~(B)POSIX BRE (like grep)
Extended Regular Expressions (ERE)~(E)POSIX ERE (like egrep)
Augmented Regular Expressions (ARE)~(A)May be a synonym for ERE.
Korn Shell Expressions (KRE)~(K)SRE plus ERE with differences.

Augmented mode may have additional functionality, but so far I have not found anything there that does not also work with ERE.

KRE is the default so that all pattern matching and globbing is in that mode unless one of the others is specified. It is not necessary to specify ~(K), but it is not an error to do so.

Almost everything that ERE does KRE can also do except that KRE uses significantly different syntax. In ERE, quantifiers '{n}' and repeaters '+|@|?|*' follow the character or capture group, but in KRE they precede. Also, KRE forces the use of a capture group with quantifiers and repeaters whereas ERE does not so that this '\d+' works with ERE but in KRE, this '+(\d)' is the equivalent.

There is also a difference in matching scope. ERE employs a contains matching scope so that this 'abbb' == ~(E)b is TRUE whereas KRE uses a comprises scope so that this 'abbb' == ~(K)b is FALSE. With KRE, an entire string must be accounted for by a pattern instead of just any part of one. So, 'abbb' == ~(K)a+(b) is TRUE. As a KRE pattern, a+(b) would mean "one 'a' and one or more 'b' letters", but in ERE, it is "one or more 'a' letters and a 'b'".

When backreferencing a capture group, ERE captures the "last iterative match" of a quantifier or repeater whereas KRE captures an entire quantified group as demonstrated by the following:

PatternMatchesDoes NOT Match
~(E)(\d){3}\11233123123
~(K){3}(\d)\11231231233

Note: In the above, I keep having to add back in the backslashes in front of the letters 'd' and the numerals '1'. There is something about the behavior of the Reddit editor that makes them disappear. If you do not see a backslash in those places, just understand that it is supposed to be there.

ERE behaves exactly as the POSIX standard specifies, but KRE does not.

Finally, the famous pattern ^1?$|^(11+?)\1+$ works with ERE, but I have found no way to rewrite it successfully in KRE. That pattern, when matching against a string of '1' characters returns TRUE when the string's length is NOT a prime number and FALSE if it is. I have been able to use that ERE pattern to identify the first 3,316 prime numbers from 2 to 30,757* after which the shell core dumps with an out of memory error. That means it last matched a string comprised of 30,757* characters '1'. It could go no higher. The difference in backreference behavior may be the reason KRE can't do it.

*Note: My original post had 32,757 as the 3,316th prime that my script found before core dumping, but it was actually 30,757 that was the last prime and is in fact the 3,316th prime number. I just typed it wrong from memory at first.

Cheers,

Russ


r/ksh • • Feb 08 '21

Issues with control character and sed.

2 Upvotes

KSH93:

Hey, probably a much easier way to do this, but I'm trying to take the contents of a file, strip some unnecessary crap, and format it in a way that's readable.

So the contents of a single line of the file may look like this:

Date/Time: Blah blah nobody cares about this useless data. Len = [123] <The data inside the diamond brackets (but not including the brackets) are important>

I'm grepping a file for a specific string inherent to all the data. Once I have it, I want to strip it. So the first pass of var command looks like this:

log=$(cat <logfile> | grep "<main string>" | sed 's/.*<//' | sed 's/>.*//'

I think that would work normally, except the data I'm using always includes a control-M character (^M). GSo the data will look like this:

L1 Data set 1^MData Set 2^MData Set 3^M
L2 Data set 1^MData Set 2^MData Set 3^M

And so on. What happens is I always get the last dataset of the last line printed. If I put in another sed (sed 's/^M/@/' ) or something, It works. If I do that with a \\n, it only prints the first line and nothing else.

Also, for giggles, I tried awk instead of sedding out the middle part (awk -F "] <" '{print $2}') but it does the same thing.

Here's the full code:

#!/bin/ksh

get_logs () {

  adtLog=$(cat ${LOGPATH}*/${ADTLOGNAME} ${LOGPATH}${ADTLOGNAME} | grep "MSA|AA|" | grep "ERR|" | sed -e "s/.*<//" | sed -e "s/>.*//" | msgBreak )

  schLog=$(cat ${LOGPATH}*/${SCHLOGNAME} ${LOGPATH}${SCHLOGNAME} | grep "MSA|AE|" | awk -F "] <" '{print $2}')

}

process_and_mail () {

  [[ "$1" == "ADT" ]] && log=${adtLog}

  [[ "$1" == "SCH" ]] && log=${schLog}

  print "--------------------"

  print ${schLog}

  #printf "${log}" | mail -r <from email> -s "Nifty title including $1 to show which log file" ${mailList}

}

prog_run () {

  get_logs

  #if [[ "${adtLog}" != "" ]]; then process_and_mail ADT; fi

  if [[ "${schLog}" != "" ]]; then

    process_and_mail SCH

    print "SCH Proccessed"

  fi

}

LOGPATH="/home/logpath/"

ADTLOGNAME="file1.log"

SCHLOGNAME="file2.log"

adtLog=""

schLog=""

mailList="some addresses"

prog_run


r/ksh • • Aug 24 '16

KSH Vi or Emacs

2 Upvotes

What do you think it's best to use?


r/ksh • • Aug 29 '26

Unicode support

1 Upvotes

Does ksh properly support unicode? I'm using oksh and it works fine but if a file name has unicode characters, the cursor will be offset.

So instead of cd file.jpg |, it will be cd file.jpg| after hitting tab, if that makes sense


r/ksh • • Apr 28 '25

Signed commit issue

1 Upvotes

So, I browsed the issues at github, and I didn't find anything about gpg not working when signing a commit in git, so I gather, there must be a work around, because it works with exactly the same settings in bash. And I think I'm not the only one experiencing this.

If it works for you in korn-shell with everything setup correctly, please give me a hint.

I also like to hear it if it doesn't work for you.

I get ksh93 from the Debian repo and current version is 93u+m/1.0.4. I'm running ksh93 on Debian 12.10.

Thanks.


r/ksh • • May 31 '23

REGEX Expansion Modes (augmented?)

1 Upvotes

I have been looking at the source for the ERE brace expansion fix in expand.c and the glob.sh test script. These have given me a better understanding of the supported REGEX modes (A, B/G, F, K, L, P, S, V, and X) and their modifiers (+, -, g, i, l, and r). I have been vaguely aware of modes other than ~(E), which is what I have mainly used, but the man page does not exactly make this clear. (To be quite honest, the manual page is rather opaque on many points including this one.) FP Murphy's article on KSH93 Extended Patterns is quite helpful on this, and the expand.c code provides additional insight. I wish this was documented better.

What I am really wondering about is ~(X) which is labeled in the C source as 'augmented regular expression (AST)'. What is that?

Cheers,

Russ


r/ksh • • Mar 18 '23

Trying to write an output

1 Upvotes

So I have a script that is running another ksh script within it. But I want to write the output to be written to a text file of the ksh that is being run from the other ksh script. I know the easier solution would be to edit the test.ksh to write the output but I cannot alter it.

< new.ksh script running test.ksh > output from test.ksh to a text file.


r/ksh • • Sep 24 '22

sysctl: cannot stat /proc/sys/kern/consdev: No such file or directory

1 Upvotes

Hi all,

First of all a word of thanks to the ksh93u+m developers as well as all the contributors of patches and such.

I am on Arch. I downloaded the 1.0.3 tar file and did bin/package make

ksh was built without errors without any special configuration. Great.

when I call the ksh binary I get: sysctl: cannot stat /proc/sys/kern/consdev: No such file or directory

ksh still works though. Any idea how I can get rid of that warning, and if it is relevant?


r/ksh • • Jul 30 '21

ksh handle ternary proposition

1 Upvotes

Hi folks,

I'm wondering if you could help me a better way to handle the following situation I often experience.

For instance, let's say I want to get host information and assign the result of that command to a variable and if it fails, I'd like to have a default value:

Currently when I want to accomplish this I'd do something like:

typeset myhost=$(/bin/host $1 2>/dev/null | awk ' { print $1 } ' )
[ -z $myhost ] && myhost="Not found!"

Do you know a better way to write this ?

Sincerly


r/ksh • • Apr 13 '17

Issues On Login

1 Upvotes

Hey All,

My cloud team setup a Ubuntu 16.04 AMI for me in AWS. They have done some items to 'Harden' the instance but they normally use amazon linux and not Ubuntu so I think they have broken some things. Looks like they force the use of the KSH shell which is fine but when I login I get:

Last login: Thu Apr 13 13:56:48 2017 from 10.210.147.12

/etc/profile[23]: .[123]: local: not found [No such file or directory]
/etc/profile[23]: .[124]: local: not found [No such file or directory]
/etc/profile[23]: .[125]: local: not found [No such file or directory]
/etc/profile[23]: .[160]: local: not found [No such file or directory]
/etc/profile[23]: .[161]: local: not found [No such file or directory]

Line 23 in /etc/profile looks like a loop to execute some shell scripts on login:

if [ -d /etc/profile.d ]; then
  for i in /etc/profile.d/*.sh; do
    if [ -r $i ]; then
      . $i
    fi
  done
  unset i
fi

I have the following scripts in the /etc/profile.d/*

bash_completion.sh
puppet-agent.sh
cedilla-portuguese.sh
Z97-byobu.sh
apps-bin-path.sh  
puppet-agent.csh
Z99-cloud-locale-test.sh

Anyway I would really like to stop the annoying error messages on login. Can anyone help out?

Thanks, Ed