1

I'm creating a script that prompts a user to enter a file name and search for the lines in the file, the longest line, and the shortest line. Here's what I have.

#!/bin/bash

echo -n "Enter a filename:"
read file
if [ -e "$file" ]
then
wc -l $file; echo "lines" && wc -L $file; echo "longest"
echo "$file exists"
else
echo "$file does not exist"
fi

This is the content of the text file I am using for the test. Hopefully this will help.

~$ cat simple.txt
I have too many things to do today.
It's going to take to long to get everything done.
My day is very busy.
Why such a long face.

Please excuse my formatting and or structure. I am at the early stages of learning shell script and appreciate any help.

4
  • I assume you'd like all indices of shortest/longest line(s) if multiple? Nov 17, 2015 at 16:55
  • Also: do empty lines count (would be len 0) please clarify. Nov 17, 2015 at 17:01
  • Cool, but no answer(s) to my question(s). Nov 17, 2015 at 17:11
  • The sample text I created has no empty lines so something should register with the script. I've posted the contents above.
    – DSH72
    Nov 17, 2015 at 17:19

3 Answers 3

2

If you're willing to use a bit of Python in your code, finding the length of the shortest line is easy:

python3 -c 'import sys; print(min(map(len, sys.stdin)))' < "$file"

print and min should be reasonably obvious. map(len, sys.stdin) applies the len (length) function to each entry in sys.stdin, which is each line in the standard input. Since this counts the newline in length of a line, your minimum length will be 1 more than what you'd expect. To fix that:

python3 -c 'import sys; print(min(map(len, sys.stdin.read().splitlines())))' < "$file"

Otherwise, a combination of awk, sort, and head:

awk '{print length}' "$file" | sort | head -1

head -1 prints only the first entry, which, after sorting, would be the shortest line length. Or, entirely in bash:

{    
    IFS= read -r line
    min=${#line}
    while IFS= read -r line
    do
        length=${#line}
        ((min > length)) && min=$length
    done
} < "$file"

echo "shortest line's length is $min"

Here, I used { } to enclose the code section. This way, < "$file" applies to all of that section, so that both the first read and the while loop read from it.

7
  • It doesn't seem to exclude "\n" as a length. Nov 17, 2015 at 16:51
  • @JacobVlijm no, but that's just a -1 away.
    – muru
    Nov 17, 2015 at 16:52
  • In the test, all my files' shortest line is 1 :) Nov 17, 2015 at 16:53
  • @JacobVlijm fixed using stdin.read().splitlines()
    – muru
    Nov 17, 2015 at 16:57
  • Wouldn't it make more sense if empty lines are skipped? (OP should make clear) Nov 17, 2015 at 17:01
1

Without using a script, using just AWK:

awk 'NR==1{x=$0}{length($0)<length(x)&&x=$0;length($0)>length(y)&&y=$0}END{print "Shortest: "x"\nLongest: "y}' in

If you want it to prompt for a filename:

awk 'NR==1{x=$0}{length($0)<length(x)&&x=$0;length($0)>length(y)&&y=$0}END{print "Shortest: "x"\nLongest: "y}' "$(read -p "Enter a filename:" x; printf "$x\n")"

Splitting it into two different commands:

awk 'NR==1{x=$0}length($0)<length(x){x=$0}END{print x}' in # shortest line
awk 'length($0)>length(x){x=$0}END{print x}' in # longest line

If you want them to prompt for a filename:

awk 'NR==1{x=$0}length($0)<length(x){x=$0}END{print x}' "$(read -p "Enter a filename:" x; printf "$x\n")" # shortest line
awk 'length($0)>length(x){x=$0}END{print x}' "$(read -p "Enter a filename:" x; printf "$x\n")" # longest line
$ cat in
I have too many things to do today.
It's going to take to long to get everything done.
My day is very busy.
Why such a long face.
$ awk 'NR==1{x=$0}{length($0)<length(x)&&x=$0;length($0)>length(y)&&y=$0}END{print "Shortest: "x"\nLongest: "y}' in
Shortest: My day is very busy.
Longest: It's going to take to long to get everything done.
$ awk 'NR==1{x=$0}{length($0)<length(x)&&x=$0;length($0)>length(y)&&y=$0}END{print "Shortest: "x"\nLongest: "y}' "$(read -p "Enter a filename:" x; printf "$x\n")"
Enter a filename:in
Shortest: My day is very busy.
Longest: It's going to take to long to get everything done.
1

In your script there's:

wc -l $file; echo "lines" && wc -L $file; echo "longest"

You could change it with:

LENGTH=$(<"$file" wc -l)
LONGEST=$(while read thisline; do echo "$(<<<"$thisline" wc -L) $thisline";done <"$file"|grep -v '^0 '|sort -t" "|tail -1|awk '{print $1}')
echo "$LENGTH lines, $LONGEST longest"

That's it, with some fix to your script output.

The while create a new flux of data, containing the length of each line in the file. The grep exclude empty lines (that's 0 chars in length); there are no empty line in your case, but prevent is better than cure. Then sort put line lengths in ascending order, and tail get the last line, that's the highest number (the longest line length). awk is there to clean the output (that came out from the wc, <length original_line>) taking only the number at the beginning.

1
  • Can you add some explanation?
    – muru
    Nov 21, 2015 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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