10

How to cut off lines that are longer than some width, and marking the lines that were chopped off with an ellipsis?

Only lines that were actually shortened should be marked, but not lines that had just the right lenght in the first place.

I would like to use the command in a pipeline.

2
  • Do you want to use the three characters ... or the single character ? Sep 14, 2014 at 12:17
  • Good point; For this question, let's use the three ".", like "...", as this may be more difficult than one ellipsis character. Sep 14, 2014 at 12:19

6 Answers 6

15

This will truncate the string, chop off an additional three characters, and add "..." if the length is longer than the value you supply as a parameter.

other_programs | \
awk -v len=40 '{ if (length($0) > len) print substr($0, 1, len-3) "..."; else print; }'
1
  • That's good, it makes the lines with ellipsis as long as the longest lines! And handles the calculation of the length too. Sep 14, 2014 at 13:20
10

Try this:

awk -F '' '{if (NF > 70) {print substr($0, 0, 71)"..."} else print $0}'

If NF is too high, the simpler way:

awk '{if (length($0) > 70) {print substr($0, 0, 71)"..."} else print $0}'

or a shorter version:

awk 'length > 70{$0=substr($0,0,71)"..."}1'
5
  • Sorry about that quote. Copy-pasta selection mistake.
    – muru
    Sep 14, 2014 at 11:26
  • 3
    This will cause error in some implements of awk, if length greater than max NF. Example, mawk has max NF 32767.
    – cuonglm
    Sep 14, 2014 at 11:38
  • Whoever added the third version, it doesn't have ellipsis.
    – muru
    Sep 14, 2014 at 15:25
  • 1
    Sorry, miss typo, updated.
    – cuonglm
    Sep 14, 2014 at 15:27
  • @Gnouc thanks for the shorter command. I'm not very sure of awk shortcuts, so I usually default to longer versions. :)
    – muru
    Sep 16, 2014 at 23:15
6

Some possibilities:

  • with sed

    sed -E 's/(.{N})(.{1,})$/\1.../' file
    
  • slightly more elegantly with perl (using lookbehind)

    perl -pe 's/(?<=.{N}).{1,}$/.../' file
    

where N is the number of characters after which you wish to replace with the ellipsis.

2
  • -E is meant to be -e, I assume? man sed does not know -E
    – phil294
    Sep 1, 2017 at 0:02
  • @Blauhirn -E stands for Extended Regular Expression - it's the POSIX equivalent of the GNU sed -r or --regex-extended flag. It's mentioned in the info page I think (info sed then navigate to Invoking sed -- Command-Line Options) Sep 1, 2017 at 1:37
5

awk, sed, and perl, as presented in the other answers, excel at text processing and are probably the best tools for the job.

But you can also do this in pure bash (i.e., "without leaving the shell"), if you like:

n=70
while read -r; do
    if ((${#REPLY}<=n))
        then printf '%s\n' "$REPLY"
        else printf '%s...\n' "${REPLY:0:$((n-3))}"
    fi
done < filename

Replace 70 with the maximum desired length, and filename with the input file.

To use this on the right side of a pipe (i.e., to pipe another command's output to it), remove < filename and either set n beforehand or enclose the whole thing in { ... ;}:

{ n=70
while read -r; do
    if ((${#REPLY}<=n))
        then printf '%s\n' "$REPLY"
        else printf '%s...\n' "${REPLY:0:$((n-3))}"
    fi
done ;}

(This bracket-enclosed version also works fine in other contexts, including with redirection as above. The brackets are unnecessary in that use case, but not harmful.)

This looks like:

ek@Ilex:~$ help read | head -5 | { n=70
> while read -r; do
>     if ((${#REPLY}<=n))
>         then printf '%s\n' "$REPLY"
>         else printf '%s...\n' "${REPLY:0:$((n-3))}"
>     fi
> done ;}
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N n...
    Read a line from the standard input and split it into fields.

    Reads a single line from the standard input, or from file descr...
    if the -u option is supplied.  The line is split into fields as...

Note that, in common with the other solutions that have been presented so far, this will fail to perfectly limit output width in the presence of characters that display more than one column wide, such as horizontal tabs.

0
0

Another perl solution:

perl -ple '$_ = sprintf "%.70s...", $_ if length > 70' file
0

The accepted answer written as a function with example, also putting the ... in the middle of the string rather than the end:

truncate_string() { 
    echo "$@" | \
    awk -v len=15 '{ if (length($0) > len) print substr($0, 1, len-3) "..." substr($0, length($0) - len, length($0)); else print; }'
}

example:

parse_branch() {
    branch=$(git symbolic-ref --short HEAD || hg branch)
    truncate_string "$branch"
}

NB I do not recall but since it was long ago but I might have used this on Mac? Or was it linux (vagrant??)

3
  • 1
    The idea is OK, however I highly discourage overloading a common Linux utility, truncate which can truncate files to a certain size. If the custom function is not loaded for some reason (environment not loading, trying to run script remotely where the function is not available), some truncate will still run, probably with an error but it will confuse anyone who uses it.
    – karatedog
    Oct 6, 2022 at 8:06
  • 1
    Thanks! I didn't know that there was a built in function called exactly truncate in Linux :-) At the time I wrote this I was on a Mac however and now I am on windows, so don't know it it's the same way there, either way I will update the answer, a good reminder to always name aliases to something more custom sounding :)
    – OZZIE
    Oct 7, 2022 at 6:26
  • could have been linux also because I used vagrant back in the day also.. hmm.. and we had linux servers... ehh no idea haha
    – OZZIE
    Oct 7, 2022 at 6:29

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.