7

One of my folders contains files in the following format:

3_20150412104422154033.txt
3_2015041211022775012.txt
3_20150412160410171639.txt
3_20150412160815638933.txt
3_20150413161046573097.txt
3_20150413161818852312.txt
3_20150413163054600311.txt
3_20150413163514489159.txt
3_2015041321292659391.txt
3_20150414124528747462.txt
3_20150414125110440425.txt
3_20150414134437706174.txt
3_20150415085045179056.txt
3_20150415100637970281.txt
3_20150415101749513872.txt

I want to retrieve those files having a date value less than or equal to my input date value.

For example, if I give "3_20150414" which is (3_YYYYMMDD), I want the output to be the file names

3_20150412104422154033.txt
3_2015041211022775012.txt
3_20150412160410171639.txt
3_20150412160815638933.txt
3_20150413161046573097.txt
3_20150413161818852312.txt
3_20150413163054600311.txt
3_20150413163514489159.txt
3_2015041321292659391.txt
3_20150414124528747462.txt
3_20150414125110440425.txt
3_20150414134437706174.txt

I can list the files by issuing a command like this:

ls -l | grep '20150413\|20150414' |awk '{print $NF}'

But I am struggling to find a <= match.

1
  • 1
    +1 for using the YYYYMMDD format, which allows date strings to be treated as numbers (and to compare correctly). Apr 30 '15 at 7:47
7

You can use awk and its string comparison operator.

ls | awk '$0 < "3_20150415"'

In a variable:

max=3_20150414 export max
ls | LC_ALL=C awk '$0 <= ENVIRON["max"] "z"'

concatenating with "z" here makes sure that the comparison is a string comparison, and allows any time on that day since in the C locale, digits sort before z.

0
2

grep doesn’t have a ≤ operator per se, but there’s a kludgy way to fake it.  You want all dates between the year 0 (or the year 1; whichever the first year was) through 20150414.  (I’ll assume that BC dates are off the table.)  Break this range into subranges that can be matched by regular expressions:

  • Year 0 through 1999 — all years begin with 0 or 1, so grep for [01].
    (All regular expressions will be assumed to be anchored at the beginning of the line, right after the “3_”.)
  • Year 2000 through 2009 — regex 200.
  • Year 2010 through 2014 — regex 201[0-4].
  • Year 2015, month 1 through month 3 — 20150[1-3].
  • Year 2015, month 4, day 1 through 9 — 2014040
  • Year 2015, month 4, day 10 through 14 — 2014041[0-4]

and then put them all together:

grep -E '3_([01]|200|201[0-4]|20150[1-3]|2015040|2015041[0-4])'

ls -l, of course, gives you lots of information about the files (mode, owner, mod time, etc.) which you don’t need, so you use awk '{print $NF}' to strip it out and leave only the filename.  This is inefficient and error prone (it breaks if a filename has a space or tab in it).  Parsing the output from ls is never a great idea, but you can make it a little safer by making it a little simpler: just don’t get the information you don’t want or need, and then you don’t need to discard it.

ls | grep -E '3_([01]|200|201[0-4]|20150[1-3]|2015040|2015041[0-4])'

should be good enough.

But building that six-part regular expression is tedious and error prone, and difficult (although not impossible) to script.  Here’s a cleaner way:

ls | awk 'substr($1, 3, 8) <= 20150414'

This extracts the eight characters starting with the 3rd position (i.e., after the “3_”) and compares it to 20150414 as two eight-digit numbers.

0
1

Possible solution using your example:

ls -l | grep '3_2015' | awk -v d='3_20150414' '{ s = substr($NF, 1, 10); if (length(s) == 10 && s <= d) print $NF; }'

I changed a little your grep pattern and pass searched value as argument to awk where we just compare strings to output desired things.
Also there is a lot of articles why you should not parse ls so I change it a little with find:

find . -type f -name '*3_2015*' -printf "%f\n" | awk -v d='3_20150414' '{ s = substr($NF, 1, 10); if (length(s) == 10 && s <= d) print $NF; }'
0

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.