diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Stat.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Stat.java index cf8270e328..8c624cc017 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Stat.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/shell/Stat.java @@ -40,8 +40,10 @@ * %o: Block size
* %r: replication
* %u: User name of owner
- * %y: UTC date as "yyyy-MM-dd HH:mm:ss"
- * %Y: Milliseconds since January 1, 1970 UTC
+ * %x: atime UTC date as "yyyy-MM-dd HH:mm:ss"
+ * %X: atime Milliseconds since January 1, 1970 UTC
+ * %y: mtime UTC date as "yyyy-MM-dd HH:mm:ss"
+ * %Y: mtime Milliseconds since January 1, 1970 UTC
* If the format is not specified, %y is used by default. */ @InterfaceAudience.Private @@ -62,9 +64,10 @@ public static void registerCommands(CommandFactory factory) { "octal (%a) and symbolic (%A), filesize in" + NEWLINE + "bytes (%b), type (%F), group name of owner (%g)," + NEWLINE + "name (%n), block size (%o), replication (%r), user name" + NEWLINE + - "of owner (%u), modification date (%y, %Y)." + NEWLINE + - "%y shows UTC date as \"yyyy-MM-dd HH:mm:ss\" and" + NEWLINE + - "%Y shows milliseconds since January 1, 1970 UTC." + NEWLINE + + "of owner (%u), access date (%x, %X)." + NEWLINE + + "modification date (%y, %Y)." + NEWLINE + + "%x and %y show UTC date as \"yyyy-MM-dd HH:mm:ss\" and" + NEWLINE + + "%X and %Y show milliseconds since January 1, 1970 UTC." + NEWLINE + "If the format is not specified, %y is used by default." + NEWLINE; protected final SimpleDateFormat timeFmt; @@ -127,6 +130,12 @@ protected void processPath(PathData item) throws IOException { case 'u': buf.append(stat.getOwner()); break; + case 'x': + buf.append(timeFmt.format(new Date(stat.getAccessTime()))); + break; + case 'X': + buf.append(stat.getAccessTime()); + break; case 'y': buf.append(timeFmt.format(new Date(stat.getModificationTime()))); break; diff --git a/hadoop-common-project/hadoop-common/src/site/markdown/FileSystemShell.md b/hadoop-common-project/hadoop-common/src/site/markdown/FileSystemShell.md index 0a594abe0c..71eec75be9 100644 --- a/hadoop-common-project/hadoop-common/src/site/markdown/FileSystemShell.md +++ b/hadoop-common-project/hadoop-common/src/site/markdown/FileSystemShell.md @@ -676,11 +676,11 @@ stat Usage: `hadoop fs -stat [format] ...` -Print statistics about the file/directory at \ in the specified format. Format accepts permissions in octal (%a) and symbolic (%A), filesize in bytes (%b), type (%F), group name of owner (%g), name (%n), block size (%o), replication (%r), user name of owner(%u), and modification date (%y, %Y). %y shows UTC date as "yyyy-MM-dd HH:mm:ss" and %Y shows milliseconds since January 1, 1970 UTC. If the format is not specified, %y is used by default. +Print statistics about the file/directory at \ in the specified format. Format accepts permissions in octal (%a) and symbolic (%A), filesize in bytes (%b), type (%F), group name of owner (%g), name (%n), block size (%o), replication (%r), user name of owner(%u), access date(%x, %X), and modification date (%y, %Y). %x and %y show UTC date as "yyyy-MM-dd HH:mm:ss", and %X and %Y show milliseconds since January 1, 1970 UTC. If the format is not specified, %y is used by default. Example: -* `hadoop fs -stat "%F %a %u:%g %b %y %n" /file` +* `hadoop fs -stat "type:%F perm:%a %u:%g size:%b mtime:%y atime:%x name:%n" /file` Exit Code: Returns 0 on success and -1 on error. diff --git a/hadoop-common-project/hadoop-common/src/test/resources/testConf.xml b/hadoop-common-project/hadoop-common/src/test/resources/testConf.xml index 64677f86f7..6a3d53ad2d 100644 --- a/hadoop-common-project/hadoop-common/src/test/resources/testConf.xml +++ b/hadoop-common-project/hadoop-common/src/test/resources/testConf.xml @@ -919,15 +919,19 @@ RegexpComparator - ^( |\t)*of owner \(%u\), modification date \(%y, %Y\).( )* + ^( |\t)*of owner \(%u\), access date \(%x, %X\).( )* RegexpComparator - ^( |\t)*%y shows UTC date as "yyyy-MM-dd HH:mm:ss" and( )* + ^( |\t)*modification date \(%y, %Y\).( )* RegexpComparator - ^( |\t)*%Y shows milliseconds since January 1, 1970 UTC.( )* + ^( |\t)*%x and %y show UTC date as "yyyy-MM-dd HH:mm:ss" and( )* + + + RegexpComparator + ^( |\t)*%X and %Y show milliseconds since January 1, 1970 UTC.( )* RegexpComparator diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java index c82c0455ee..27d41b4d32 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java @@ -36,12 +36,12 @@ import com.google.common.base.Supplier; import com.google.common.collect.Lists; + import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Level; import org.junit.Test; - import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org.apache.hadoop.fs.permission.AclEntry; @@ -65,6 +65,7 @@ import org.apache.hadoop.test.PathUtils; import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.util.StringUtils; +import org.apache.hadoop.util.Time; import org.apache.hadoop.util.ToolRunner; import org.junit.rules.Timeout; import org.junit.AfterClass; @@ -115,6 +116,7 @@ public static void setup() throws IOException { GenericTestUtils.getTestDir("TestDFSShell").getAbsolutePath()); conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_XATTRS_ENABLED_KEY, true); conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, true); + conf.setLong(DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY, 1000); miniCluster = new MiniDFSCluster.Builder(conf).numDataNodes(2).build(); miniCluster.waitActive(); @@ -2002,8 +2004,12 @@ public void testStat() throws Exception { DFSTestUtil.createFile(dfs, testFile2, 2 * BLOCK_SIZE, (short) 3, 0); final FileStatus status1 = dfs.getFileStatus(testDir1); final String mtime1 = fmt.format(new Date(status1.getModificationTime())); + final String atime1 = fmt.format(new Date(status1.getAccessTime())); + long now = Time.now(); + dfs.setTimes(testFile2, now + 3000, now + 6000); final FileStatus status2 = dfs.getFileStatus(testFile2); final String mtime2 = fmt.format(new Date(status2.getModificationTime())); + final String atime2 = fmt.format(new Date(status2.getAccessTime())); final ByteArrayOutputStream out = new ByteArrayOutputStream(); System.setOut(new PrintStream(out)); @@ -2036,17 +2042,19 @@ public void testStat() throws Exception { out.toString().contains(String.valueOf(octal))); out.reset(); - doFsStat(dfs.getConf(), "%F %a %A %u:%g %b %y %n", testDir1, testFile2); + doFsStat(dfs.getConf(), "%F %a %A %u:%g %b %x %y %n", testDir1, testFile2); n = status2.getPermission().toShort(); octal = (n>>>9&1)*1000 + (n>>>6&7)*100 + (n>>>3&7)*10 + (n&7); assertTrue(out.toString(), out.toString().contains(mtime1)); + assertTrue(out.toString(), out.toString().contains(atime1)); assertTrue(out.toString(), out.toString().contains("regular file")); assertTrue(out.toString(), out.toString().contains(status2.getPermission().toString())); assertTrue(out.toString(), out.toString().contains(String.valueOf(octal))); assertTrue(out.toString(), out.toString().contains(mtime2)); + assertTrue(out.toString(), out.toString().contains(atime2)); } private static void doFsStat(Configuration conf, String format, Path... files)