8

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). Commented Apr 30, 2015 at 7:47

3 Answers 3

8

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.

In zsh, you can also do:

print -rC1 -- *.txt(e['[[ $REPLY < ${max}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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .