I have a variable assigned to a returned string:

ytd_wk=$(cat file.csv | grep $(date +'%Y') | tail -1)

I want to substring last 2 characters:


Is there any way to use one-liner to achieve this? I have tried below but got bad substitution error:

ytd_wk=${$(cat file.csv | grep $(date +'%Y') | tail -1):(-2)}

  • 2
    How do you define “one-liner”?  You could just put your first two commands together on one line, separated by && or ;. – G-Man Says 'Reinstate Monica' Nov 20 '17 at 21:03

Try to use:

grep "$(date +'%Y')" file.csv | tail -1 | sed 's/.*\(..$\)/\1/'

You don't need cat because grep can get data both from output of another program or from certain file. The last method more efficient because it uses only one command and this is more fast and consume less system resources.

The full solution:

ytd_wk=$(grep "$(date +'%Y')" file.csv | tail -1 | sed 's/.*\(..$\)/\1/')

You can omit tail with GNU sed '$!d':

grep "$(date +'%Y')" file.csv | sed -r '$!d;s/.*(..$)/\1/'


grep "$(date +'%Y')" file.csv | sed -e '$!d' -e 's/.*\(..$\)/\1/'
| improve this answer | |
  • 2
    +1 for not using cat in your answer — although it would have been better it you had explained why cat is unnecessary. – G-Man Says 'Reinstate Monica' Nov 20 '17 at 21:00

You could use awk, combining the cat, grep, sed, and tail of other suggestions:

awk -v year=$(date +'%Y') '$0 ~ year {line=$0} END {print substr(line,length(line)-1,2)}' file.csv

Writing this out step-by-step

  • -v year=$(date +'%Y') sets the awk variable year to the current year
  • $0 ~ year { line=$0 } this is applied to each line of the file in turn. If there is a match to the year in the current line it saves it in the awk variable line
  • END { print substr(line,length(line)-1,2) } at the end of the file (after the last line has been read and processed) this prints the last two characters of the most recently saved line. It prints a blank line if there was no earlier successful match.
| improve this answer | |

This is more efficient, especially if file.csv is large and the desired line is nearer to the end:

ytd_wk="$(tac file.csv | grep -m 1 $(date +'%Y') | grep -o '..$')"

How it works: tac outputs file.csv backwards, and grep -m 1 finds the first instance of the pattern, which is fed to grep -o which outputs only the last two chars.

| improve this answer | |

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.