Skip to content

Commit 2a4040a

Browse files
committed
TailCommand for file end content display
- To output the last part of files. - Integrated support for -n (number of lines) and -c (number of bytes) flags. - Implemented functionality to handle multiple files with headers and ensured efficient file reading. - Included error handling for flag requirements and file existence, along with cross-platform path normalization.
1 parent 662c46c commit 2a4040a

File tree

2 files changed

+129
-2
lines changed

2 files changed

+129
-2
lines changed
Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,130 @@
11
package com.unixtools.command.filecontent;
22

3-
public class TailCommand {
4-
3+
import com.unixtools.core.Command;
4+
5+
import java.io.*;
6+
import java.nio.file.Path;
7+
import java.nio.file.Paths;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
public class TailCommand implements Command {
12+
private static final String VALID_FLAGS = "nc";
13+
private int numberOfLines = 10;
14+
private int numberOfBytes = -1;
15+
private boolean isBytesFlagUsed = false;
16+
17+
@Override
18+
public void execute(String[] args) {
19+
List<String> flags = new ArrayList<>();
20+
List<String> filePaths = new ArrayList<>();
21+
parseArguments(args, flags, filePaths);
22+
23+
if (!areFlagsValid(flags)) {
24+
System.out.println("Invalid flags. Valid flags are: " + VALID_FLAGS);
25+
return;
26+
}
27+
28+
if (filePaths.isEmpty()) {
29+
System.out.println("tail: No file specified.");
30+
return;
31+
}
32+
33+
for (String filePath : filePaths) {
34+
if (filePaths.size() > 1) {
35+
System.out.println("==> " + filePath + " <==");
36+
}
37+
displayFileContents(normalizePath(filePath));
38+
}
39+
}
40+
41+
private void parseArguments(String[] args, List<String> flags, List<String> paths) {
42+
for (int i = 0; i < args.length; i++) {
43+
if (args[i].startsWith("-")) {
44+
switch (args[i]) {
45+
case "-n":
46+
numberOfLines = Integer.parseInt(args[++i]);
47+
break;
48+
case "-c":
49+
isBytesFlagUsed = true;
50+
numberOfBytes = Integer.parseInt(args[++i]);
51+
break;
52+
default:
53+
flags.add(args[i]);
54+
break;
55+
}
56+
} else {
57+
paths.add(args[i]);
58+
}
59+
}
60+
}
61+
62+
private boolean areFlagsValid(List<String> flags) {
63+
return flags.stream().allMatch(flag -> VALID_FLAGS.contains(flag.replace("-", "")));
64+
}
65+
66+
private void displayFileContents(String filePath) {
67+
try {
68+
if (isBytesFlagUsed) {
69+
readLastBytes(filePath, numberOfBytes);
70+
} else {
71+
readLastLines(filePath, numberOfLines);
72+
}
73+
} catch (IOException e) {
74+
System.out.println("Error reading file: " + e.getMessage());
75+
}
76+
}
77+
78+
private void readLastLines(String filePath, int numLines) throws IOException {
79+
RandomAccessFile file = new RandomAccessFile(filePath, "r");
80+
long fileLength = file.length();
81+
List<String> lines = new ArrayList<>();
82+
StringBuilder sb = new StringBuilder();
83+
boolean lastCharWasCR = false;
84+
85+
for (long filePointer = fileLength - 1; filePointer >= 0; filePointer--) {
86+
file.seek(filePointer);
87+
char ch = (char) file.readByte();
88+
89+
if (ch == '\n') {
90+
if (lastCharWasCR) {
91+
sb.deleteCharAt(0);
92+
}
93+
lines.add(sb.reverse().toString());
94+
sb.setLength(0);
95+
if (lines.size() == numLines) {
96+
break;
97+
}
98+
lastCharWasCR = false;
99+
} else if (ch == '\r') {
100+
lastCharWasCR = true;
101+
} else {
102+
lastCharWasCR = false;
103+
}
104+
sb.append(ch);
105+
}
106+
107+
for (int i = lines.size() - 1; i >= 0; i--) {
108+
System.out.print(lines.get(i));
109+
}
110+
111+
System.out.println();
112+
file.close();
113+
}
114+
115+
private void readLastBytes(String filePath, int numBytes) throws IOException {
116+
RandomAccessFile file = new RandomAccessFile(filePath, "r");
117+
long fileLength = file.length();
118+
file.seek(fileLength - numBytes);
119+
byte[] bytes = new byte[numBytes];
120+
file.readFully(bytes);
121+
System.out.println(new String(bytes));
122+
file.close();
123+
}
124+
125+
private String normalizePath(String path) {
126+
String normalizedPath = path.replace("\\", "/");
127+
Path pathObj = Paths.get(normalizedPath).normalize();
128+
return pathObj.toString();
129+
}
5130
}

src/com/unixtools/core/CommandFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public static Command getCommand(String commandName) {
5454
return new CatCommand();
5555
case "head":
5656
return new HeadCommand();
57+
case "tail":
58+
return new TailCommand();
5759
default:
5860
return null;
5961
}

0 commit comments

Comments
 (0)