I want to extract the access day of a file in a shell script. I tried by writing the following script but it doesn't work. When I echo accessday, the output is "staff".

accessday=$(ls -lu $file | cut -d ' ' -f 6)

Don't parse ls.

This is a job for stat.

To get the last access time in human readable format:

stat -c '%x' file.txt

in seconds since epoch:

stat -c '%X' file.txt
  • Why don't I parse ls ? – Out Of Bounds Mar 29 '16 at 3:24
  • 1
    @OutOfBounds check unix.stackexchange.com/q/128985/68757 – heemayl Mar 29 '16 at 3:25
  • btw, your own question provides a good example of exactly why you shouldn't parse ls. the output format is not fixed, and not reliable. – cas Mar 29 '16 at 3:31
  • 1
    also btw, the above stat command works with GNU stat. for FreeBSD or Mac OS X stat, you'd use stat -f '%a' file.txt (for access time in seconds since epoch) or stat -f '%Sa' file.txt (for access time in strftime() format) – cas Mar 29 '16 at 3:38

If you look at the typical output of ls -lu,

-rw-r--r--  1 someuser  somegroup  74 Mar 29 05:21 filename

you'll notice that a total of five space characters precedes the group name. Your specification of field 6 in the cut command thus returns the group ("staff" in your case).

Also, the access day would be field 7, not 6 in your notation.

One solution would be to use awk, since it handles multiple occurences of the field separator the way you expected by default:

accessday=$(ls -lu $file | awk '{print $7}')

Based on your question I would use stat to normalize the output, then cut to only display the date that you wanted.

stat -c '%x' FILENAME | cut -c 9,10


Parsing the output of ls is difficult and not always possible. If you want to extract a timestamp, it is sometimes a necessary fallback (it's the only portable way to do so: not all systems have a stat command), but you get a human-readable timestamp which is hard to convert to a machine-readable one. At least get the difficult parts out of the way: pass the -o and -g options to omit the user and group fields (these can contain spaces on some systems, making them impossible to delimit in the output of ls). Then the timestamp occupies fields 4–6 (numbering from 1).

Don't forget double quotes around variable expansions.

access_time=$(ls -dlogu -- "$file" | awk '1 {print $4, $5, $6}')

If you want the day of the month, you need to distinguish the two output formats, for timestamps within the past 6 months or not. Set the locale to C to get a reliable output format. In this format, the day of the month is always the second timestamp field.

access_day=$(ls -dlogu -- "$file" | awk '1 {print $5}')

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.