108

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

13 Answers 13

158
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%
2
34

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
  • 4
    The pipe to sed is redundant: it can be done in awk: df -h | awk '{gsub(/%/,""); print $5}' Commented Jul 15, 2013 at 6:33
  • @jasonwryan Then I like Stephane's solution better. Commented Jul 15, 2013 at 6:35
28

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)

0
12

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. Commented Dec 15, 2020 at 18:25
  • Got it, easy to fix. gimme a minute... Commented Dec 15, 2020 at 18:46
  • should we do && echo "" in order to get rid of EOF char that might be printed out Commented Mar 13, 2021 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). Commented Mar 14, 2021 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. Commented Mar 14, 2021 at 8:58
11

In awk, you could do one of

awk '{sub(/%$/,"",$5); print $5}'
awk '{print substr($5, 1, length($5)-1)}'
5
echo "123" | perl -ple 'chop'
12
3
  • 2
    Honestly, this one is the most human-readable, which will always get my vote. Commented Feb 21, 2020 at 8:07
  • 1
    The only issue is it won't remove a newline character :( Commented Feb 21, 2020 at 8:10
  • 1
    @thekingoftruth, it works fine for me! ( -l already removes the newline, chop removes next char) Commented Dec 15, 2020 at 18:58
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 Commented Apr 1, 2017 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. Commented Apr 1, 2017 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 '%'. Commented Dec 10, 2018 at 10:36
  • Plus I believe only harmful / wrong answers should ever get downvoted below 0. Commented Dec 10, 2018 at 10:38
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.

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

0

Using Raku (formerly known as Perl_6)

~$ printf '123\n456\n789' | raku -pe '.=chop;'
12
45
78

#OR:

~$ printf '123\n456\n789' | raku -ne '.chop.put;'
12
45
78

OR:

~$ printf '123\n456\n789' | raku -pe 's/.$//;'
12
45
78

#OR:

~$ printf '123\n456\n789' | raku -ne 'put S/.$//;'
12
45
78

Just like Perl, Raku can perform sed-like substitution using the -pe (autoprinting) command-line flags, or awk-like substitution using the -ne (non-autoprinting) flags. The Raku code above works fine even if the input text contains a blank line. Note the fourth example uses Raku's new S/// "big-S" non-destructive substitution notation, which "leaves the original string intact and returns the resultant string."


OP's specific example parsing the output of df -P:

~$ df -P | raku -ne 'put .words[4].subst("%");'

#OR:

~$ df -P | raku -ne 'put .words[4].trans("%" => "");'

To skip the first line (like the excellent answer from @StéphaneChazelas), use Raku's new (pre-incrementing) anonymous variable (++$):

~$ raku -ne  'if ++$ > 1 {put .words[4].subst("%") }'

#OR:

~$ raku -ne  'if ++$ > 1 {put .words[4].trans("%" => "") }'

https://docs.raku.org/language/5to6-nutshell
https://docs.raku.org/syntax/S%2F%2F%2F%20non-destructive%20substitution

-2

df -h | awk {'print $5'}

Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        4,0M     0  4,0M   0% /dev
tmpfs            16G  1,6G   15G  10% /dev/shm
tmpfs           6,3G  2,3M  6,3G   1% /run
efivarfs        148K   61K   83K  43% /sys/firmware/efi/efivars
/dev/nvme0n1p3  930G  874G   55G  95% /
/dev/nvme0n1p3  930G  874G   55G  95% /home
/dev/nvme0n1p2  974M  332M  576M  37% /boot
tmpfs            16G  135M   16G   1% /tmp
/dev/nvme0n1p1  599M   43M  556M   8% /boot/efi
tmpfs           3,2G  204K  3,2G   1% /run/user/1000
/dev/nvme0n1p3  930G  874G   55G  95% /swap

df -h | awk {'print $5'} | grep -v Use | sed 's|.$||g'

0
10
1
43
95
95
37
1
8
1
95

awk {'print $5'} = only prints the 5th word

grep -v Use = removes lines with the word 'Use'

sed 's|.$||g' = removes the last character

2
  • (1) Explaining awk {'print $5'} is silly, since the OP obviously already knows what it means.  (But the braces should be inside the quotes'{ print $5 }' — as everybody else has done it.)  (2) grep -v Use is inappropriate; the OP clearly shows that the “Use” line is part of the expected/desired output. … (Cont’d) Commented Aug 15, 2024 at 0:08
  • (Cont’d) …  (3) Your sed 's|.$||g' is exactly the same as Stéphane Chazelas’ sed 's/.$//' answer (also given by Bernhard and Guru), except it’s meaningless to use the ‘g’ (global) modifier on an anchored substitution (one that begins with ^ or ends with $). Commented Aug 15, 2024 at 0:08

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.