Anyone aware of a method/class/library that will allow me to easily reproduce the results of the *nix ls -l command in Java? Calling ls directly is not an option due to platform independence.
eg.
$ls -l myprogram.exe
-rwxrwxrwx 1 auser None 1261568 Nov 15 17:41 C:\myprogram.exe
Here is a tutorial on getting a directory listing in java with sample source code.
Here's an implementation of ls -R ~, which lists files recursively starting in the home directory:
import java.io.*;
public class ListDir {
public static void main(String args[]) {
File root;
if (args.length > 0) root = new File(args[0]);
else root = new File(System.getProperty("user.dir"));
ls(root);
}
/** iterate recursively */
private static void ls(File f) {
File[] list = f.listFiles();
for (File file : list) {
if (file.isDirectory()) ls(file);
else System.out.println(file);
}
}
}
This provides what you are looking for:
You can use the java.nio.file package.
I think we don't have ready to use Java class in java stadard library.
But you can develop a tool like *nix ls -l tool by using classes in java.io package.
Related
Is there a way to access the file inside archive while ignoring file name case using TrueZip?
Imagine following zip archive with content:
MyZip.zip
-> myFolder/tExtFile.txt
-> anotherFolder/TextFiles/file.txt
-> myFile.txt
-> anotherFile.txt
-> OneMOREfile.txt
This is how it works:
TPath tPath = new TPath("MyZip.zip\\myFolder\\tExtFile.txt");
System.out.println(tPath.toFile().getName()); //prints tExtFile.txt
How to do the same but ignore all case, like this:
// note "myFolder" changed to "myfolder" and "tExtFile" to "textfile"
TPath tPath = new TPath("MyZip.zip\\myfolder\\textfile.txt");
System.out.println(tPath.toFile().getName()); // should print tExtFile.txt
Code above throws FsEntryNotFoundException ... (no such entry)
It works for regular java.io.File, not sure why not for TFile of TrueZip or I am missing something?
My goal is to access each file just using only lowercase for files and folders.
Edit: 24-03-2017
Let's say I would like to read bytes from file inside mentioned zip archive MyZip.zip
Path tPath = new TPath("...MyZip.zip\\myFolder\\tExtFile.txt");
byte[] bytes = Files.readAllBytes(tPath); //returns bytes of the file
This snippet above works, but this one below does not (throws mentioned -> FsEntryNotFoundException). It is the same path and file just in lowercase.
Path tPath = new TPath("...myzip.zip\\myfolder\\textfile.txt");
byte[] bytes = Files.readAllBytes(tPath);
You said:
My goal is to access each file just using only lowercase for files and folders.
But wishful thinking will not get you very far here. As a matter of fact, most file systems (except Windows types) are case-sensitive, i.e. in them it makes a big difference if you use upper- or lower-case characters. There you can even have the "same" file name in different case multiple times in the same directory. I.e. it actually makes a difference if the name is file.txt, File.txt or file.TXT. Windows is really an exception here, but TrueZIP does not emulate a Windows file system but a general archive file system which works for ZIP, TAR etc. on all platforms. Thus, you do not have a choice whether you use upper- or lower-case characters, but you have to use them exactly as stored in the ZIP archive.
Update: Just as a little proof, I logged into a remote Linux box with an extfs file system and did this:
~$ mkdir test
~$ cd test
~/test$ touch file.txt
~/test$ touch File.txt
~/test$ touch File.TXT
~/test$ ls -l
total 0
-rw-r--r-- 1 group user 0 Mar 25 00:14 File.TXT
-rw-r--r-- 1 group user 0 Mar 25 00:14 File.txt
-rw-r--r-- 1 group user 0 Mar 25 00:14 file.txt
As you can clearly see, there are three distinct files, not just one.
And what happens if you zip those three files into an archive?
~/test$ zip ../files.zip *
adding: File.TXT (stored 0%)
adding: File.txt (stored 0%)
adding: file.txt (stored 0%)
Three files added. But are they still distince files in the archive or just stored under one name?
~/test$ unzip -l ../files.zip
Archive: ../files.zip
Length Date Time Name
--------- ---------- ----- ----
0 2017-03-25 00:14 File.TXT
0 2017-03-25 00:14 File.txt
0 2017-03-25 00:14 file.txt
--------- -------
0 3 files
"3 files", it says - quod erat demonstrandum.
As you can see, Windows is not the whole world. But if you copy that archive to a Windows box and unzip it there, it will only write one file to a disk with NTFS or FAT file system - which one is a matter of luck. Very bad if the three files have different contents.
Update 2: Okay, there is no solution within TrueZIP for the reasons explained in detail above, but if you want to work around it, you can do it manually like this:
package de.scrum_master.app;
import de.schlichtherle.truezip.nio.file.TPath;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
public class Application {
public static void main(String[] args) throws IOException, URISyntaxException {
TPathHelper tPathHelper = new TPathHelper(
new TPath(
"../../../downloads/powershellarsenal-master.zip/" +
"PowerShellArsenal-master\\LIB/CAPSTONE\\LIB\\X64\\LIBCAPSTONE.DLL"
)
);
TPath caseSensitivePath = tPathHelper.getCaseSensitivePath();
System.out.printf("Original path: %s%n", tPathHelper.getOriginalPath());
System.out.printf("Case-sensitive path: %s%n", caseSensitivePath);
System.out.printf("File size: %,d bytes%n", Files.readAllBytes(caseSensitivePath).length);
}
}
package de.scrum_master.app;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.nio.file.TPath;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
public class TPathHelper {
private final TPath originalPath;
private TPath caseSensitivePath;
public TPathHelper(TPath tPath) {
originalPath = tPath;
}
public TPath getOriginalPath() {
return originalPath;
}
public TPath getCaseSensitivePath() throws IOException, URISyntaxException {
if (caseSensitivePath != null)
return caseSensitivePath;
final TPath absolutePath = new TPath(originalPath.toFile().getCanonicalPath());
TPath matchingPath = absolutePath.getRoot();
for (Path subPath : absolutePath) {
boolean matchFound = false;
for (TFile candidateFile : matchingPath.toFile().listFiles()) {
if (candidateFile.getName().equalsIgnoreCase(subPath.toString())) {
matchFound = true;
matchingPath = new TPath(matchingPath.toString(), candidateFile.getName());
break;
}
}
if (!matchFound)
throw new IOException("element '" + subPath + "' not found in '" + matchingPath + "'");
}
caseSensitivePath = matchingPath;
return caseSensitivePath;
}
}
Of course, this is a little ugly and will just get you the first matching path if there are multiple case-insensitive matches in an archive. The algorithm will stop searching after the first match in each subdirectory. I am not particularly proud of this solution, but it was a nice exercise and you seem to insist that you want to do it this way. I just hope you are never confronted with a UNIX-style ZIP archive created on a case-sensitive file system and containing multiple possible matches.
BTW, the console log for my sample file looks like this:
Original path: ..\..\..\downloads\powershellarsenal-master.zip\PowerShellArsenal-master\LIB\CAPSTONE\LIB\X64\LIBCAPSTONE.DLL
Case-sensitive path: C:\Users\Alexander\Downloads\PowerShellArsenal-master.zip\PowerShellArsenal-master\Lib\Capstone\lib\x64\libcapstone.dll
File size: 3.629.294 bytes
I dont have TrueZip installed but I was also wondering how it would work in normal Path, so I implemented below way quite similar #kriegaex solution, you can try using caseCheck(path):
public class Main {
/**
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
Path path = Paths.get("/home/user/workspace/JParser/myfolder/yourfolder/Hisfolder/a.txt");
Instant start = Instant.now();
Path resolution;
try{
resolution = caseCheck(path);
}catch (Exception e) {
throw new IllegalArgumentException("Couldnt access given path", e);
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("Path is: " + resolution + " process took " + duration.toMillis() + "ms");
}
/**
* #param path
* #return
* #throws IOException
*/
private static Path caseCheck(Path path) throws IOException {
Path entryPoint = path.isAbsolute() ? path.getRoot() : Paths.get(".");
AtomicInteger counter = new AtomicInteger(0);
while (counter.get() < path.getNameCount()) {
entryPoint = Files
.walk(entryPoint, 1)
.filter(s -> checkPath(s, path, counter.get()))
.findFirst()
.orElseThrow(()->new IllegalArgumentException("No folder found"));
counter.getAndIncrement();
}
return entryPoint;
}
/**
* #param s
* #param path
* #param index
* #return
*/
private static final boolean checkPath(Path s, Path path, int index){
if (s.getFileName() == null) {
return false;
}
return s.getFileName().toString().equalsIgnoreCase(path.getName(index).toString());
}
}
I am trying to understand I/O in java. This is the code I have.
What I need to do is:
1) Should I write java list "C:\windows" on cmd and I need to see all the files inside of Windows.
2) I didn't understand File dir = new File(args[0]); What does this args[0] why we use it?
public class App {
public static void main(String []args)
{
try {
if (args.length != 1) {
System.err.println("Wrong usage");
System.exit(-1);
}
File dir = new File(args[0]);
if (!dir.isDirectory()) {
System.err.println("seems there is no directory ");
System.exit(-1);
}
File [] files = dir.listFiles();
System.setProperty("user.home", args[0]);
for (File file : files) {
if (file.isDirectory())
System.out.printf("%s<DIR>%n", file.getName());
else
System.out.printf("%s%n", file.getName());
}
}
catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
The answer you need is already in your question. After you compile the App.java will have a result call App.class, this is your class. To run it from the command you can use the java command line tool. It something like
java App C:/Windows
Where App is your application class without the .class. The C:/Windows is the parameter for your program, which is captured by the args[0].
First, you need to compile the java class
javac -g App.java
Then you can run it using:
java App "c:\\windows"
Below is the program that prints all the files & folders from the given path.
import java.io.File;
public class ListDirectoryRecursive{
public static void listRecursive(File dir){
if(dir.isDirectory()){
File[] items = dir.listFiles();
for(File item : items){
System.out.println(item.getAbsoluteFile());
if(item.isDirectory()){
listRecursive(item);
}
}
}
}
public static void main(String[] args){
/* Unix path is: /usr/home/david/workspace/JavaCode */
File dir = new File("C:\\Users\\david\\workspace\\JavaCode");
listRecursive(dir);
}
}
How do i make this java program run on Unix? What is the standard approach to make this program portable?
Edit: I guess, we know on any OS, user home directory is part of the environment setting with values like "c:\users\david" in windows and "/user/home/david" in Unix.
Take the directory as an argument via args:
File dir = new File(args[1]);
(checking of course that args.length is sufficient).
Then you can simply do
java ListDirectoryRecursive C:\Users\david\workspace\JavaCode
on Windows, and
java ListDirectoryRecursive ~david/workspace/JavaCode
on UNIX. This has the distinct advantage of allowing you use this program to list any directory, rather than being a hardcoded path.
Make the hardcoded absolute path C:\\Users\\david\\workspace\\JavaCode relative.
please dont harcode
File dir = new File("C:\\Users\\david\\workspace\\JavaCode");
use System.getProperty("user.home"); to get /usr/home/david or C:\Users\david\ directory
like
String path = System.getProperty("user.home") + File.separator + "workspace" + File.separator + "JavaCode";
File dir = new File(path);
thanks overexchange
How do I execute
rd /s /q c:\folder
in Java?
It woks perfectly on the command-line.
According to dtsazza's answer:
Runtime.getRuntime().exec("cmd.exe /k rd /s /q c:\\folder");
It works perfectly under WinXP SP3. The /k parameter shows, that a command will follow that has to be executed out of the cmd.exe.
Good luck with that!
Check the Runtime.exec method, which lets you call external processes. (Bear in mind that you lose some platform independence as this would rely on the machine having the rd command installed and on the path.)
A better option might be to do the same thing in pure Java - the following should be equivalent:
private void deleteDirectory(File directory)
{
for (File entity : directory.listFiles())
{
if (entity.isDirectory())
{
deleteDirectory(entity);
}
else
{
entity.delete();
}
}
directory.delete();
}
deleteDirectory(new File("C:\\folder"));
Adding error-checking as required. :-)
Check out FileUtils in Apache Commons-IO; in particular, the deleteDirectory and deleteQuietly methods, which can both recursively delete a directory.
Look at File.delete(String path) method, i.e.:
new File("c:\\folder").delete();
If the /s (recursive) deletion is important, then (untested):
public void deltree(File file) {
if (file.isDirectory()) {
for (File f : file.listFiles()) {
deltree(f);
}
}
file.delete();
}
public void deltree(String path) {
deltree(new File(path));
}
invoked as:
deltree("c:\\folder");
You only can delet empty directories in Java. First you have to delete the files and sub directories.
public static boolean removeDirectoryRecursively(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
boolean success = removeDirectoryRecursively(new File(dir, children[i]));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
If you want to execute it exactly the way you specified, you can do
Process p=Runtime.getRuntime().exec("cmd.exe /k rd /s /q c:\\folder");
If you have two jars in your classpath that contain different versions of the same class, the classpath order becomes critical.
I am looking for a tool that can detect and flag such potential conflicts in a given classpath or set of folders.
Certainly a script that starts:
classes=`mktemp`
for i in `find . -name "*.jar"`
do
echo "File: $i" > $classes
jar tf $i > $classes
...
done
with some clever sort/uniq/diff/grep/awk later on has potential, but I was wondering if anyone knows of any existing solutions.
Looks like jarfish will do what you want with its "dupes" command.
The Tattletale tool from JBoss is another candidate: "Spot if a class/package is located in multiple JAR files"
Classpath Helper is an Eclipse plug-in that helps a little bit.
I think it wouldn't be too hard to write a tool for your self.
You can get the classpath entries with System.getProperty("java.class.path");
And then walk through the jars, zips, or directories listed there and collect all the information about the classes and findout those that might cause trouble.
This task would take 1 or 2 days at most. Then you can load this class directly in your application and generate a report.
Probably java.class.path property wont's show all the classes if you run in some infrastructure with complex custom class loading ( for instance I once saw an app that load the classes from the LDAP ) but it would certainly work for most of the cases.
Heres a tool you might find useful, I've never use it my self, but give it a try and let us know the result.
http://www.jgoodies.com/freeware/jpathreport/features.html
If you are going to create your own tool, here is the code I use for the same shell script posted before, but that I use on my Windows machine. It runs faster when there are tons of jar files.
You can use it and modify it so instead of recursively walk a directory, read the class path and compare the .class time attribute.
There is a Command class you can subclass if needed, I was thinking in the -execute option of "find"
This my own code, so it was not intended to be "production ready", just to do the work.
import java.io.*;
import java.util.zip.*;
public class ListZipContent{
public static void main( String [] args ) throws IOException {
System.out.println( "start " + new java.util.Date() );
String pattern = args.length == 1 ? args[0] : "OracleDriver.class";// Guess which class I was looking for :)
File file = new File(".");
FileFilter fileFilter = new FileFilter(){
public boolean accept( File file ){
return file.isDirectory() || file.getName().endsWith( "jar" );
}
};
Command command = new Command( pattern );
executeRecursively( command, file, fileFilter );
System.out.println( "finish " + new java.util.Date() );
}
private static void executeRecursively( Command command, File dir , FileFilter filter ) throws IOException {
if( !dir.isDirectory() ){
System.out.println( "not a directory " + dir );
return;
}
for( File file : dir.listFiles( filter ) ){
if( file.isDirectory()){
executeRecursively( command,file , filter );
}else{
command.executeOn( file );
}
}
}
}
class Command {
private String pattern;
public Command( String pattern ){
this.pattern = pattern;
}
public void executeOn( File file ) throws IOException {
if( pattern == null ) {
System.out.println( "Pattern is null ");
return;
}
String fileName = file.getName();
boolean jarNameAlreadyPrinted = false;
ZipInputStream zis = null;
try{
zis = new ZipInputStream( new FileInputStream( file ) );
ZipEntry ze;
while(( ze = zis.getNextEntry() ) != null ) {
if( ze.getName().endsWith( pattern )){
if( !jarNameAlreadyPrinted ){
System.out.println("Contents of: " + file.getCanonicalPath() );
jarNameAlreadyPrinted = true;
}
System.out.println( " " + ze.getName() );
}
zis.closeEntry();
}
}finally{
if( zis != null ) try {
zis.close();
}catch( Throwable t ){}
}
}
}
I hope this helps.
If you dislike downloading and installing stuff you can use this one line command to find jar conflicts with standard gnu tools. It is rudimentary but you can expand it as you will.
ls *.jar | xargs -n1 -iFILE unzip -l FILE | grep class | sed "s,.* ,," | tr "/" "." | sort | uniq -d | xargs -n1 -iCLASS grep -l CLASS *.jar | sort -u
(it is a bit slow to run if you have a lot of jars)
Explanation:
It lists all the files in all the jars, greps for class files, finds dupes, then greps the original jars to see where they appeared. It could be made more efficient with a more complicated script.
jarclassfinder is another eclipse plugin option