86

I want to remove last character from a line:

[root@ozzesh ~]#df -h | awk  '{ print $5 }'
Use%
22%
1%
1%
59%
51%
63%
5%

Expected result:

Use
22
1
1
59
51
63
5
2

11 Answers 11

130
sed 's/.$//'

To remove the last character.

But in this specific case, you could also do:

df -P | awk 'NR > 1 {print $5+0}'

With the arithmetic expression ($5+0) we force awk to interpret the 5th field as a number, and anything after the number will be ignored.

Note that GNU df (your -h is already a GNU extension, though not needed here) can also be told to only output the disk usage percentage:

df --output=pcent | tail -n +2 | tr -cd '0-9\n'

(tail skips the headers and tr removes everything but the digits and the line delimiters).

On Linux, see also:

findmnt -no USE%
3
30

With sed, this is pretty easy:

$ cat file
Use%
22%
1%
1%
59%
51%
63%
5%
$ sed 's/.$//' file
Use
22
1
1
59
51
63
5

The syntax is s(ubstitute)/search/replacestring/. The . indicates any character, and the $ the end of the line. So .$ will remove the last character only.

In this case, your complete command would look:

df -h | awk '{ print $5}' | sed 's/.$//'
2
  • 2
    The pipe to sed is redundant: it can be done in awk: df -h | awk '{gsub(/%/,""); print $5}'
    – jasonwryan
    Jul 15 '13 at 6:33
  • @jasonwryan Then I like Stephane's solution better.
    – Bernhard
    Jul 15 '13 at 6:35
21

I have two solutions :

  1. cut: echo "somestring1" | rev | cut -c 2- | rev

    Here you reverse the string and cut the string from 2nd character and reverse again.

  2. sed : echo "somestring1" | sed 's/.$//'

    Here you will search for regular expression .$ which means any characters followed by a last character and replace it with null // (between the two slashes)

11

In awk, you could do one of

awk '{sub(/%$/,"",$5); print $5}'
awk '{print substr($5, 1, length($5)-1)}'
4
df -h | awk 'NR > 1{ print $5 }' | cut -d "%" -f1
4
  • 1
    This answer could be more helpful to others beyond OP if you could provide a bit of explanation regarding how it works and possibly why this might be better than the alternatives
    – Fox
    Apr 1 '17 at 20:09
  • Welcome to Unix Stackexchange! You can take the tour to get a feel for how this site works. When giving an answer it is preferable to give some explanation as to WHY your answer is the one the reader wants. This means it is best if you give some explanation of how it works. If you look above, you will see that all of the highly voted answers explain the code. Apr 1 '17 at 20:10
  • Upvoted for the use of cut -d '%' -f1 which is the correct answer to get everything in the line up to the first '%'.
    – Titou
    Dec 10 '18 at 10:36
  • Plus I believe only harmful / wrong answers should ever get downvoted below 0.
    – Titou
    Dec 10 '18 at 10:38
3
echo "123" | perl -ple 'chop'
12
3
  • Honestly, this one is the most human-readable, which will always get my vote. Feb 21 '20 at 8:07
  • 1
    The only issue is it won't remove a newline character :( Feb 21 '20 at 8:10
  • @thekingoftruth, it works fine for me! ( -l already removes the newline, chop removes next char)
    – JJoao
    Dec 15 '20 at 18:58
2

another approach:

mapfile -t list < <(df -h)
printf '%s\n' "${list[@]%?}"

Turn it into a function:

remove_last() {
  local char=${1:-?}; shift
  mapfile -t list < <("$@")
  printf '%s\n' "${list[@]%$char}"
}

Then call it like this:

remove_last '%' df -h

mapfile is a bash4 feature.

The catch here is that you must supply a character to remove; if you want it to just be whatever the last character happens to be then you must pass '?' or ''. quotes required.

2

Did you know that head and tail can work on a character basis instead of per line?

$ printf "I don't like periods." | head -c-1
I don't like periods

(BTW, printf is used here to prevent printing the "new line" character at the end of the line. If your output has a new line character and you want to remove that as well as the last non-whitespace character, use head -c-2).

This is basically the same as "Guru"'s solution using cut, but without the funky rev back and forth that they need to use because cut has no syntax for "last n things".

To iterate over a stream of lines and remove the last character of each, one might do something like this:

some-command | while read line; do echo $(head -c-2 <<<"$line"); done
6
  • 2
    This will remove the last character of a file, not the last character of every line.
    – thanasisp
    Dec 15 '20 at 18:25
  • Got it, easy to fix. gimme a minute...
    – Guss
    Dec 15 '20 at 18:46
  • should we do && echo "" in order to get rid of EOF char that might be printed out
    – alper
    Mar 13 '21 at 15:31
  • @alper - when you say "get rid of EOF" you mean "add missing EOL"? If so, and you mean in the first example, then yes - echo "" prints out an zero-length string, after which the echo implementation auto-adds \n (which the printf in my example didn't on purpose, as is explained in the BTW).
    – Guss
    Mar 14 '21 at 8:57
  • @alper - that being said, if you want to mimic the auto-newline echo behavior, I prefer to use it by having echo actually print the entire thing - as I demonstrated in the second example.
    – Guss
    Mar 14 '21 at 8:58
1
$ df -h | awk '{print $5}' | cut -d '%' -f1 
1

Try with this:

df -h | awk  '{ print $5 }' | sed "s/%//"

The normal use is: (ie)

VALUE=987654321
echo X123456789X | sed "s/123456789/${VALUE}/"

Response should be: X987654321X

0
sed -ie '$d' filename

here -i is to write changes
      e means expression
      $ means last line
      d means delete

Note:Without -e option $ wont work

Optional: To delete 1st and last line use sed -ie '1d;$d' filename

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.