-2

I want to find all sequences that are in alphabetical order in txt file, without sorting.

Example: assuming the text file looks like this:

aedftg
wqof
abcdef
oufn
pqrs
aabcd

the output should be

abcdef
pqrs

also without repeating characters like aabcd.

1
5

One option, hard-coded for English:

grep -ix 'a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*' input

Credit to NinjaBearMonkey for spelling out the idea that was germinating in my head; I modified the regex to allow for repeated characters (aabcc).

Another option:

Using sort to check the locale-specific collation order is a better way to go, so that you don't have to hard-code the alphabet. As mikejonesey did, this means you have to split the line's characters onto separate lines. I've done this with a shell script (using a modern shell that supports substring expansion) to avoid the call to sed, and which also uses sort's -c option to indicate if the input is already sorted or not:

while IFS= read -r line
do
  for((i=0; i < ${#line}; i++))
  do
    printf '%s\n' "${line:i:1}"
  done | sort -c 2>/dev/null && printf '%s\n' "$line"
done

If sort finds a discrepancy, it outputs to stderr, but we only care about the return code, so I drop stderr.

A demonstration with a non-English input (the $ characters are the shell prompts):

$ cat input
αβγα
αβγ
$ ./sorted2 < input
αβγ
$
8
  • Noting that this will match an empty line, which one could claim is vacuously sorted.
    – Jeff Schaller
    Oct 25 '16 at 1:19
  • also, -x option to match whole line instead of using ^$
    – Sundeep
    Oct 25 '16 at 1:23
  • Or inspired by this, you could do something like grep -ix "$(printf '%c*' {a..z})" Oct 25 '16 at 1:54
  • I would love to find a locale-specific way to expand an alphabet; could have sworn I saw something from Stephane on this, but will have to keep looking.
    – Jeff Schaller
    Oct 25 '16 at 2:03
  • i want use grep like your 1st answer but output without repeating characters(aabc) Nov 19 '16 at 18:34
1

Just for completeness, an awk-based solution, where the usual relation of pattern and data are swapped:

awk 'BEGIN{ref="abcdefghijklmnopqrstuvwxyz"} ref ~ $0' file.txt

This will interpret the lines in the file as regular expressions against which the "reference" string of sorted characters from a to z is matched. Only if there is a match (which means the string in the line is a sub-string of a ... z) the line will be printed.

2
  • I'd expect the OP want to match acgz like the accepted answer does. Oct 29 '20 at 15:56
  • @StéphaneChazelas You may be right, but the OP didn't clearly specify it (and the examples only included gap-less sequences) ...
    – AdminBee
    Oct 29 '20 at 16:15
1
perl -MPOSIX -Mopen=locale -lne '
   print unless m{(.)(.)(?(?{strcoll($1, $2) < 0})(*FAIL))}' < file

Would print the lines of file except those that contain a character that does not collate after the previous one in the user's locale.

1
  • That is a rather clever approach, but you may want to add a more in-depth explanation on the collation order; I could imagine that not many people are aware of the implications of locale here ...
    – AdminBee
    Oct 29 '20 at 17:41
0

With perl

$ cat ip.txt 
aedftg
aabcd
wqof
abcdef
oufn
pqrs   
   
$ perl -F -lane '$m=1; foreach (1..$#F){ $m=0 && last if $F[$_] le $F[$_-1] } print if $m' ip.txt
abcdef
pqrs
  • Input line is split character wise into @F array
  • If any character occurs alphabetically prior (or same character) to the character before it, the match variable $m is set to 0 and loop is quit immediately
  • Note this solution is partially case-sensitive, Pqrs will match but pQrs will fail

If pqrs is changed to pQrs and still needs to be matched:

$ perl -lne '(@s) = split//,tr/A-Z/a-z/r; $m=1; foreach (1..$#s){ $m=0 && last if $s[$_] le $s[$_-1] } print if $m' ip.txt 
abcdef
pQrs


Alternate solutions with sort and uniq

# partially case sensitive
perl -MList::Util=uniq -F -lane 'print if $_ eq join "", uniq sort @F'

# case insensitive
perl -MList::Util=uniq -lne '$ip=lc; print if $ip eq join "", uniq sort split //,$ip'
2
  • i use this. output also give 'aabcd'. i want output without this Nov 19 '16 at 17:46
  • @Mahela.Prasad a simple change from lt to le solves it.. suggest to edit your question to reflect it as well
    – Sundeep
    Nov 20 '16 at 1:38
0
cat homework.txt | while read aline; do 
    stringORIG=$(echo -n "$aline" | sed 's/\(.\)/\1\n/g')
    stringABC=$(echo -n "$aline" | sed 's/\(.\)/\1\n/g' | sort)
    if [ "$stringORIG" == "$stringABC" ]; then
        echo "$aline"
    fi
done

.

abcdef
pqrs

ps... if you don't want to use the sort command, you can convert each char to int and have an if statement to verify the last char is less than the next...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.