Unix/ls: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Sidef}}: removed some unneeded semicolons)
(→‎{{header|Rust}}: Fixed error handling)
Line 453: Line 453:
=={{header|Rust}}==
=={{header|Rust}}==
Works only with correct paths or no arguments at all.
Works only with correct paths or no arguments at all.
<lang rust>use std::{env, fmt, fs, process};

<lang rust>use std::env;
use std::io::{self, Write};
use std::fs;
use std::path::Path;
use std::path::Path;


fn main() {
fn main() {
let cur = env::current_dir().unwrap_or_else(|e| exit_err(e, 1));
// ignoring all arguments except the 1st
match env::args().nth(1) { // check if the program received an argument
let arg = env::args().nth(1);
Some(path) => { print_files(Path::new(&path)); }
print_files(arg.as_ref().map_or(cur.as_path(), |p| Path::new(p)))
.unwrap_or_else(|e| exit_err(e, 2));
_ => { print_files( &env::current_dir().unwrap() ); }
// note that current_dir value might be invalid, so it's a Result
}
}
}


#[inline]
fn print_files(path:&Path) {
fn print_files(path: &Path) -> io::Result<()> {
let mut entries: Vec<_> = fs::read_dir(path).unwrap()
.map(|x| x.unwrap().file_name())
for x in try!(fs::read_dir(path)) {
println!("{}", try!(x).file_name().to_string_lossy());
.collect();
entries.sort();
for x in entries {
println!("{}", x.to_string_lossy());
}
}
Ok(())
}

#[inline]
fn exit_err<T>(msg: T, code: i32) -> ! where T: fmt::Display {
writeln!(&mut io::stderr(), "{}", msg).expect("Could not write to stderr");
process::exit(code)
}</lang>
}</lang>



Revision as of 04:18, 12 November 2015

Task
Unix/ls
You are encouraged to solve this task according to the task description, using any language you may know.

Write a program that will list everything in the current folder, similar to the Unix utility “ls[1] (or the Windows terminal command “DIR”). The output must be sorted, but printing extended details and producing multi-column output is not required.

Example output

For the list of paths:

/foo/bar
/foo/bar/1
/foo/bar/2
/foo/bar/a
/foo/bar/b

When the program is executed in `/foo`, it should print:

bar

and when the program is executed in `/foo/bar`, it should print:

1
2
a
b

8th

<lang forth> "*" f:glob ' s:cmp a:sort "\n" a:join . </lang>

Ada

<lang Ada>with Ada.Text_IO, Ada.Directories, Ada.Containers.Indefinite_Vectors;

procedure Directory_List is

  use Ada.Directories, Ada.Text_IO;
  Search: Search_Type; Found: Directory_Entry_Type;
  package SV is new Ada.Containers.Indefinite_Vectors(Natural, String);
  Result: SV.Vector;
  package Sorting is new SV.Generic_Sorting; use Sorting;
  function SName return String is (Simple_Name(Found));
  

begin

  -- search directory and store it in Result, a vector of strings
  Start_Search(Search, Directory => ".", Pattern =>"");
  while More_Entries(Search) loop
     Get_Next_Entry(Search, Found);
     declare
        Name: String := Simple_Name(Found);
     begin
        if Name(Name'First) /= '.' then
           Result.Append(Name);
        end if; -- ingnore filenames beginning with "."
     end;
  end loop; -- Result holds the entire directory in arbitrary order
  
  Sort(Result); -- Result holds the directory in proper order
  -- print Result
  for I in Result.First_Index .. Result.Last_Index loop 
     Put_Line(Result.Element(I));
  end loop;   

end Directory_List;</lang>

AWK

Works with: gawk

"BEGINFILE" is a gawk-extension

<lang AWK>

  1. syntax: GAWK -f UNIX_LS.AWK * | SORT

BEGINFILE {

   printf("%s\n",FILENAME)
   nextfile

} END {

   exit(0)

} </lang>

Sample commands and output under Windows 8:

REM create folders and files
MKDIR c:\foo\bar
CD /D c:\foo\bar
GAWK "BEGIN{x=\"12ab\";for(i=1;i<=length(x);i++){print(i)>substr(x,i,1)}}"
REM run test
CD /D c:\foo
GAWK -f UNIX_LS.AWK * | SORT
bar
CD /D c:\foo\bar
GAWK -f UNIX_LS.AWK * | SORT
1
2
a
b
Works with: gawk

To replicate 'ls .'

gawk -lreaddir 'BEGIN { FS = "/" } {print $2}' .
Works with: gawk

To replicate 'ls examplefile.txt'

gawk -lfilefuncs -lreaddir 'BEGIN { FS = "/"; stat(ARGV[1], fd); if(fd["type"] == "file") {print ARGV[1]; exit} } { print $2}' examplefile.txt

C

C does not have any os-independent way of reading a directory. The following uses readdir and should work on any Unix system. <lang C>

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <limits.h>
  5. include <sys/types.h>
  6. include <dirent.h>
  7. include <unistd.h>

int cmpstr(const void *a, const void *b) {

   return strcmp(*(const char**)a, *(const char**)b);

}

int main(void) {

   DIR *basedir;
   char path[PATH_MAX];
   struct dirent *entry;
   char **dirnames;
   int diralloc = 128;
   int dirsize  = 0;
   
   if (!(dirnames = malloc(diralloc * sizeof(char*)))) {
       perror("malloc error:");
       return 1;
   }
   if (!getcwd(path, PATH_MAX)) {
       perror("getcwd error:");
       return 1;
   }
   if (!(basedir = opendir(path))) {
       perror("opendir error:");
       return 1;
   }
   while ((entry = readdir(basedir))) {
       if (dirsize >= diralloc) {
           diralloc *= 2;
           if (!(dirnames = realloc(dirnames, diralloc * sizeof(char*)))) {
               perror("realloc error:");
               return 1;
           }
       }
       dirnames[dirsize++] = strdup(entry->d_name);
   }
   qsort(dirnames, dirsize, sizeof(char*), cmpstr);
   int i;
   for (i = 0; i < dirsize; ++i) {
       if (dirnames[i][0] != '.') {
           printf("%s\n", dirnames[i]);
       }
   }
   for (i = 0; i < dirsize; ++i)
       free(dirnames[i]);
   free(dirnames);
   closedir(basedir);
   return 0;

} </lang>

C++

Library: Boost

<lang cpp>

  1. include <iostream>
  2. include <set>
  3. include <boost/filesystem.hpp>

namespace fs = boost::filesystem;

int main(void) {

   fs::path p(fs::current_path());
   std::set<std::string> tree;
   for (auto it = fs::directory_iterator(p); it != fs::directory_iterator(); ++it)
       tree.insert(it->path().filename().native());
   for (auto entry : tree)
       std::cout << entry << '\n';

} </lang>

Clojure

<lang clojure>(def files (sort (filter #(= "." (.getParent %)) (file-seq (clojure.java.io/file ".")))))

(doseq [n files] (println (.getName n)))</lang>

D

<lang d>void main() {

   import std.stdio, std.file, std.path;
   foreach (const string path; dirEntries(getcwd, SpanMode.shallow))
       path.baseName.writeln;

}</lang>

EchoLisp

No directory in EchoLisp, which is run in a browser window. Instead, "stores" (folders) and keys in stores (file names) are located in local storage. <lang lisp>

ls of stores (kind of folders)

(for-each writeln (list-sort < (local-stores))) →

   AGES    
   NEMESIS    
   info    
   objects.dat    
   reader    
   system    
   user    
   words    
ls of "NEMESIS" store

(for-each writeln (local-keys "NEMESIS")) →

   Alan    
   Glory    
   Jonah    

</lang>

Forth

This is much easier without the 'sorted output' requirement: <lang forth>256 buffer: filename-buf

each-filename { xt -- } \ xt-consuming variant
 s" ." open-dir throw { d }
 begin filename-buf 256 d read-dir throw while
   filename-buf swap xt execute
 repeat  d close-dir throw ;

\ immediate variant

each-filename[ s" ." postpone sliteral ]] open-dir throw >r begin filename-buf 256 r@ read-dir throw while filename-buf swap [[ ; immediate compile-only
]each-filename ]] repeat drop r> close-dir throw [[ ; immediate compile-only
ls ( -- ) [: cr type ;] each-filename ;</lang>

Given that requirement, we must first generate a sorted array of filenames:

<lang forth>: save-string ( c-addr u -- a )

 dup 1+ allocate throw dup >r place r> ;

require ffl/car.fs

sorted-filenames ( -- car )
 0 car-new { a }
 [: swap count rot count compare ;] a car-compare!
 each-filename[ save-string a car-insert-sorted ]each-filename
 a ;
each-sorted-filename ( xt -- )
 sorted-filenames { a }  a car-execute  [: free throw ;] a car-execute  a car-free ;
ls ( -- )
 [: count cr type ;] each-sorted-filename ;

</lang>

FunL

<lang funl>import io.File

for f <- sort( list(File( "." ).list()).filterNot(s -> s.startsWith(".")) )

   println( f )</lang>
Output:

The above script has been placed in a file called ls.lf which has been placed in the home directory.

$ sudo mkdir -p /foo/bar
$ cd /foo/bar
$ sudo touch 1 2 a b
$ cd ..
$ funl ~/ls
bar
$ cd bar
$ funl ~/ls
1
2
a
b
$ 

Go

<lang go>package main

import ( "fmt" "log" "os" "sort" )

func main() { f, err := os.Open(".") if err != nil { log.Fatal(err) } files, err := f.Readdirnames(0) f.Close() if err != nil { log.Fatal(err) } sort.Strings(files) for _, n := range files { fmt.Println(n) } }</lang>

Haskell

Works with: GHC version 7.8.3

<lang haskell>import Control.Monad import Data.List import System.Directory

dontStartWith = flip $ (/=) . head

main = do

 files <- getDirectoryContents "."
 mapM_ putStrLn $ sort $ filter (dontStartWith '.') files</lang>

J

<lang J> dir '*' NB. includes properties

  > 1 dir '*'  NB. plain filename as per task</lang>

Mathematica

<lang Mathematica>Column[FileNames[]]</lang>

OCaml

<lang ocaml>let () =

 Array.iter print_endline (
   Sys.readdir Sys.argv.(1) )</lang>
Output:
$ cd /foo/bar
$ ocaml ls.ml
1
2
a
b

PARI/GP

GP doesn't have this capability so we can either use the shell or PARI. For the latter see C; for the former: <lang parigp>system("dir/b/on")</lang> in DOS/Windows or <lang parigp>system("ls")</lang> in *nix.

Pascal

This is the example in the Turbo Pascal 4 manual. With Turbo Pascal and old-style DOS file names, all file names come out in capitals, further, names not fitting into the "8.3" style (of up to eight characters followed by an extension of up to three characters) are presented with ad-hoc names fitting that style, so for example, Tab2Comma.exe comes out as TAB2CO~1.EXE. The same source file compiles unchanged via the Free Pascal compiler, whereupon long file names appear with capitals and lower-case letters rather than all-capitals.

When tested via Windows XP, the names came out in sorted order (ignoring case) however in earlier systems the files would be presented in entry order. That is, if files a, c, b were saved, they would be named in that order. Then, if file c were deleted and then a file named x were added, they would be named in the order a, x, b. In this case, a scheme for saving an unknown number of names (of unknown length) would be needed so that they could be sorted. Perhaps some linked-list with an insertionsort for each added name... <lang Pascal> Program ls; {To list the names of all files/directories in the current directory.}

Uses DOS;
var DirInfo: SearchRec;	{Predefined. See page 403 of the Turbo Pascal 4 manual.}
BEGIN
 FindFirst('*.*',AnyFile,DirInfo);	{AnyFile means any file name OR directory name.}
 While DOSerror = 0 do			{Result of FindFirst/Next not being a function, damnit.}
  begin
   WriteLn(DirInfo.Name);
   FindNext(DirInfo);
  end;
END.

</lang>

Perl 6

There is a dir builtin command which returns a list of IO::Path objects. We stringify them all with a hyperoperator before sorting the strings.

<lang perl6>.say for sort ~«dir</lang>


PHP

This will output all the filenames in the current directory.

<lang php> <?php foreach(scandir('.') as $fileName){

   echo $fileName."\n";

} </lang>

Python

<lang python>>>> import os >>> print('\n'.join(sorted(os.listdir('.')))) DLLs Doc LICENSE.txt Lib NEWS.txt README.txt Scripts Tools include libs python.exe pythonw.exe tcl >>> </lang>

Racket

Ooh... warning... if you run the test module (either with DrRacket with the test module automatically running, or with raco test ls.rkt, then the example directory tree is built but not torn down.

<lang racket>#lang racket/base

Racket's `directory-list' produces a sorted list of files

(define (ls) (for-each displayln (directory-list)))

Code to run when this file is running directly

(module+ main

 (ls))

(module+ test

 (require tests/eli-tester racket/port racket/file)
 (define (make-directory-tree)
   (make-directory* "foo/bar")
   (for ([f '("1" "2" "a" "b")])
     (with-output-to-file (format "foo/bar/~a"f) #:exists 'replace newline)))
 (make-directory-tree)
 (define (ls/str dir)
   (parameterize ([current-directory dir]) (with-output-to-string ls)))
 (test (ls/str "foo") => "bar\n"
       (ls/str "foo/bar") => "1\n2\na\nb\n"))</lang>

Both tests pass.

REXX

The following program works under Windows and used the Windows DIR command to list a bare-bones sorted list. <lang rexx>/*REXX program lists contents of current folder (ala mode UNIX's LS). */ 'DIR /b /oN' /*use Windows DIR: sorts & lists.*/

                                      /*stick a fork in it, we're done.*/</lang>

Notes on the options used for the DIR command:

  •   b   is for bare format (no heading information or summary).
  •   o   is for order, and it orders (sorts) by file Name.

Ruby

<lang ruby> Dir.foreach("./"){|n| puts n} </lang> This will output all files including hidden ones e.g. '.' and '..'.

Rust

Works only with correct paths or no arguments at all. <lang rust>use std::{env, fmt, fs, process}; use std::io::{self, Write}; use std::path::Path;

fn main() {

   let cur = env::current_dir().unwrap_or_else(|e| exit_err(e, 1));
   let arg = env::args().nth(1);
   print_files(arg.as_ref().map_or(cur.as_path(), |p| Path::new(p)))
       .unwrap_or_else(|e| exit_err(e, 2));

}

  1. [inline]

fn print_files(path: &Path) -> io::Result<()> {

   for x in try!(fs::read_dir(path)) {
       println!("{}", try!(x).file_name().to_string_lossy());
   }
   Ok(())

}

  1. [inline]

fn exit_err<T>(msg: T, code: i32) -> ! where T: fmt::Display {

   writeln!(&mut io::stderr(), "{}", msg).expect("Could not write to stderr");
   process::exit(code)

}</lang>

Output:
$ mkdir -p foo/bar
$ ./unix_ls
foo
unix_ls
$ cd foo/bar
$ touch a b 1 2
$ cd ../..
$ ./unix_ls foo
bar
$ ./unix_ls foo/bar
1
2
a
b

Scala

Output:
scala> new java.io.File("/").listFiles.sorted.foreach(println)
/bin
/boot
/core
/dev
/etc
/home
/lib
/lib64
/local
/lost+found
/media
/mnt
/opt
/proc
/root
/run
/sbin
/selinux
/srv
/sys
/tmp
/user
/usr
/var 

Sidef

Explicit, by opening the current working directory: <lang ruby>var content = []; Dir.cwd.open.each { |file|

   file ~~ < . .. > && next;
   content.append(file);

}

content.sort.each { |file|

   say file;

}</lang>

Implicit, by using the String.glob method: <lang ruby>'*'.glob.each { |file|

   say file;

}</lang>

Tcl

<lang tcl>puts [join [lsort [glob -nocomplain *]] "\n"]</lang>

zkl

<lang zkl>File.glob("*").sort()</lang> Lists all files and directories in the current directory. If you only want a list of files: <lang zkl>File.glob("*",0x8).sort()</lang>

Output:
L("README","superball","testThemAll.log","zkl.exe","zkl_tests.zip","zkl_vm_src.zip")

The glob method uses Unix shell wild cards.

The globular method recurses down through the directories. It can send results to objects, functions, methods, threads, etc, etc. To get a sorted list of all the directories under the "Src" directory: <lang zkl>File.globular("Src",*,True,0x10,List).sort().concat("\n")</lang>

Output:
Src/Compiler/
Src/Misc/
Src/Test/
Src/Time/
Src/Utils/
Src/ZenKinetic/
Src/ZenKinetic/Frame_O_Matic/
Src/ZenKinetic/GBalls/
Src/ZenKinetic/Twist and Draw/
Src/ZenKinetic/ZEd