41

I need a file (preferably a .list file) which contains the absolute path of every file in a directory.

Example dir1: file1.txt file2.txt file3.txt

listOfFiles.list:

/Users/haddad/dir1/file1.txt
/Users/haddad/dir1/file2.txt
/Users/haddad/dir1/file3.txt

How can I accomplish this in linux/mac?

10 Answers 10

50

You can use find. Assuming that you want only regular files, you can do:

find /path/to/dir -type f > listOfFiles.list

You can adjust the type parameter as appropriate if you want other types of files.

6
  • 11
    +1 for pointing a more future-proof solution that ls. This find does recurse the subdirectories, for non-recursive you need to add -maxdepth 1 before -type argument.
    – kubanczyk
    Mar 8 '16 at 21:19
  • @AndyDalton How to get the same to an array in bash Jan 8 '18 at 8:56
  • adding '| sort' before redirection will also sort the output
    – Thesane
    May 12 '20 at 6:41
  • find "$(pwd)" -type f > listOfFiles.txt will list files w.r.t. working directory. Note: the file listOfFiles.txt will also be listed in this approach.
    – Hari
    Jan 3 at 13:22
  • @Hari . might be easier to read than "$(pwd)". Jul 30 at 12:40
38
ls -d "$PWD"/* > listOfFiles.list
8
  • would this work in red hat linux? Mar 8 '16 at 19:07
  • 4
    That command works in any Linux or UNIX operating system. If you want to get one file per line, you need to use ls -d -1 $PWD/*
    – MelBurslan
    Mar 8 '16 at 19:11
  • 1
    if your file names are long or terminal width is narrow,, yes, that will be the case, but say you maximized the terminal window to occupy the whole screen or your file names (including the path) are really short, that will not hold true. -1 option guarantees you get one filename per line
    – MelBurslan
    Mar 8 '16 at 19:18
  • 7
    @MelBurslan 's addition is only needed if output is to a termiinal. ls detects if output is is to a file or terminal.
    – Runium
    Mar 8 '16 at 19:58
  • 11
    this will fail if there are many thousands of files in the directory, i.e. enough to exceed the maximum command line size (made more likely by the fact that the shell is expanding the filenames with full path). @Andy Dalton's find answer is a better solution, as it won't fail no matter how many files are to be listed.
    – cas
    Mar 8 '16 at 22:39
14

Note that in:

ls -d "$PWD"/* > listOfFiles.list

It's the shell that computes the list of (non-hidden) files in the directory and passes the list to ls. ls just prints that list here, so you could as well do:

printf '%s\n' "$PWD"/*

Note that it doesn't include hidden files, includes files of any type (including directories) and if there's no non-hidden file in the directory, in POSIX/csh/rc shells, you'd get /current/wd/* as output. Also, since the newline character is as valid as any in a file path, if you separate the file paths with newline characters, you won't be able to use that resulting file to get back to the list of file reliably.

With the zsh shell, you could do instead:

print -rNC1 $PWD/*(ND-.) > listOfFiles.list

Where:

  • -rC1 prints raw on 1 Column.
  • -N, output records are NUL-delimited instead of newline-delimited (lines) as NUL is the only character that can't be found in a file name.
  • N: expands to nothing if there's no matching file (nullglob)
  • D: include hidden files (dotglob).
  • -.: include only regular files (.) after symlink resolution (-).

Then, you'd be able to do something like:

xargs -0 rm -f -- < listOfFiles.list

To remove those files for instance.

You could also use the :P modifier in the glob qualifiers to get the equivalent of realpath() on the files expanded from the globs (gets a full path exempt of any symlink component):

print -rNC1 -- *(ND-.:P) > listOfFiles.list
2
  • 3
    Using printf has the added bonus that you won't get a 'command line too long' error if you have thousands of files as printf is not run as a separate process. Mar 10 '16 at 9:07
  • 2
    @AdrianPronk, yes, except in shells where printf is not built-in like pdksh and some of its derivatives or most versions of the Bourne shell. One drawback compared to ls -d is that if there's no non-hidden file in there, it will print /path/to/* while ls will give you an error about that file not existing instead. Mar 10 '16 at 9:44
11

To see just regular files --

find "$PWD" -type f  > output
4

Another way with tree, not mentioned here, it goes recursively and unlike find or ls you don't have any errors (like: Permission denied, Not a directory) you also get the absolute path in case you want to feed the files to xargs or other command

tree -fai /pathYouWantToList >listOfFiles.list

the options meaning:

-a     All  files  are  printed.  By default tree does not print hidden files (those beginning with a dot
       `.').  In no event does tree print the file system constructs `.'  (current  directory)  and  `..'
       (previous directory).

-i     Makes tree not print the indentation lines, useful when used in conjunction with the -f option.

-f     Prints the full path prefix for each file.

To install tree:

sudo apt install tree on Ubuntu/Debian

sudo yum install tree on CentOS/Fedora

sudo zypper install tree on OpenSUSE

3
  • 1
    tree: command not found
    – rogerdpack
    Mar 23 '18 at 21:28
  • @rogerdpack sudo apt install tree on Ubuntu sudo yum install tree on CentOS sudo zypper install tree on OpenSUSE Mar 24 '18 at 0:27
  • 2
    brew install tree on Mac
    – oOEric
    Feb 11 '19 at 7:42
3

You can just use realpath or readlink this naughty way:

ls | xargs realpath

When ls prints to a TTY it formats the file names in columns, but when it's writing to a file, pipe, or other non-TTY it behaves like ls -1 and prints one file name per line. You can check this by running ls | cat in place of ls.[1]

  • xargs build and execute command lines from standard input.
  • realpath: return the canonicalized absolute pathname
  • readlink: read value of a symbolic link
  • Use realpath -- to make it treat everything that follows as parameters instead of options, if files could have " -something".
  • If some files have spaces you could:
    ls | xargs -I {} realpath -- {}
    
5
  • realpath: command not found
    – rogerdpack
    Mar 23 '18 at 21:28
  • @rogerdpack do you have coreutils package installed? I get that info with dpkg -S /usr/bin/realpath. Check out this.
    – Pablo A
    Mar 24 '18 at 2:52
  • Wonderful. First solution I found that works recursively: ls -R $PWD | xargs realpath | grep yaml
    – Feocco
    Dec 11 '20 at 17:07
  • @Feocco not if your filenames contain spaces...
    – roaima
    Dec 11 '20 at 21:40
  • @roaima I added a slightly more complex alternative with for those cases
    – Pablo A
    Dec 12 '20 at 4:46
2

In a past Linux environment, I had a resolve command that would standardize paths, including making a relative path into an absolute path. I can't find it now, so maybe it was written by someone in that organization.

You can make your own script using functions in the Python or Perl standard libraries (and probably other languages too).

resolve.py :

#!/bin/env python

import sys
import os.path

for path in sys.argv:
    print os.path.abspath(path)

resolve.pl :

#!/bin/env perl

use warnings;
use Cwd qw ( abs_path );

foreach (@ARGV) {
    print abs_path($_), "\n";
}

Then, you would solve your problem with:

resolve.py * > listOfFiles.list

With this command, you can also do things like this:

cd /root/dir1/dir2/dir3
resolve.py ../../dir4/foo.txt
# prints /root/dir1/dir4/foo.txt
1

Recursive files can be listed by many ways in linus. Here i am sharing one liner script to clear all logs of files(only files) from /var/log/ directory and second check recently which logs file has made an entry.

First:-

find /var/log/ -type f  #listing file recursively 

Second:-

for i in $(find $PWD -type f) ; do cat /dev/null > "$i" ; done #empty files recursively 

Third use:-

ls -ltr $(find /var/log/ -type f ) # listing file used in recent

note: for directory location you can also pass $PWD instead of /var/log.

0

To list the full path of all commands (apps/programs) accessible to the user... (revised to address most, but not all limitations outlined in the comments)

eval ls -d $(echo $PATH | sed -e 's|^:|.:|'  -e 's|:$|:.:|'  -e 's|:|/{.,}[[:word:]]* |g') 2>/dev/null | sort

NOTE
The PATH variable would normally have a colon (:) either at the beginning or at the end, but not both. A colon at the beginning or end signifies to search the current directory as well. Standard practice is for it to be at the end so as to never override standard utility programs. The sed substitutions here handle either case.

Explanation...

  • eval
    Frankly, I don't know why eval is needed here, but it is. Without it, I get...

ls: cannot access './{.,}[[:word:]]': No such file or directory
ls: cannot access '/home/alpha/bin/{.,}[[:word:]]
': No such file or directory
ls: cannot access '/usr/local/sbin/{.,}[[:word:]]': No such file or directory
ls: cannot access '/usr/local/bin/{.,}[[:word:]]
': No such file or directory
ls: cannot access '/usr/sbin/{.,}[[:word:]]': No such file or directory
ls: cannot access '/usr/bin/{.,}[[:word:]]
': No such file or directory
ls: cannot access '/sbin/{.,}[[:word:]]': No such file or directory
ls: cannot access '/bin/{.,}[[:word:]]
': No such file ordirectory

  • ls -d
    List directories. The -d option doesn't really just list directories. By appending /* to each directory (see below), we will get the contents of the directories.
  • $( ... ) Perform the commands inside parens and replace the $( ... ) with the results for ls to use.
    • $(echo :$PATH | sed -e 's|^:|.:|' -e 's|:$|:.:|' -e 's|:|/{.,}[[:word:]]* |g')
      • Produces a space-separated list of directory patterns like...
        ./{.,}[[:word:]]* /home/alpha/bin/{.,}[[:word:]]* /usr/local/sbin/{.,}[[:word:]]* /usr/local/bin/{.,}[[:word:]]* /usr/sbin/{.,}[[:word:]]* /usr/bin/{.,}[[:word:]]* /sbin/{.,}[[:word:]]* /bin/{.,}[[:word:]]*
        
      • echo $PATH
        The PATH environment variable contains a colon-separated list of all directories that will be searched for commands.
      • sed
        Stream editor utility. The following -e substitution options are processed sequentially so that the results of each are available to the next. Thus, the :.: inserted by the second one allow the colons be replaced by the pattern in the third.
      • -e 's|^:|.:|'
        If PATH starts with a colon(:), replace it with .:
      • -e 's|:$|:.:|'
        If PATH ends with a colon(:), replace it with :.:
      • -e 's|:|/{.,}[[:word:]]* |g
        Replace every colon (:) with /{.,}[[:word:]]*. This pattern includes files beginning with a dot (.) followed by any number of alphanumeric or underscore characters. The extra comma (,) in {.,} is a quirk of bash 5.0.18 as it is expecting a comma separated list or range. We are using brace substitution here to make the dot (.) optional.
      • 2>/dev/null
        Discard error messages.
  • | sort
    Optionally, sort the output from the previous command.
4
  • That assumes the components of $PATH don't contain space, tab, newline and wildcard characters (and backslash in bash 5.0) and that $PATH doesn't contain the current directory (as in PATH=/bin:/usr/bin:). That will also not list the commands whose name starts with a .. You're also not adding /* to the last component. In zsh, you'd just do: print -roC1 $commands. Dec 19 '20 at 13:35
  • Standard practice is to not include the current directory in $PATH. Also, if you don't know why you need eval, then you probably should not be using eval as it may be doing things that you are not aware of. Using eval to perform the task of listing files in a directory seems rather odd to me. It's also unclear why you are involving $PATH here at all.
    – Kusalananda
    Dec 22 '20 at 8:40
  • @StéphaneChazelas Thank You! I've revised it to address most of these limitations. At the moment, don't know of a practical way to handle tab, newline, etc. in a command list like this. Your improvements are welcome. Dec 22 '20 at 8:42
  • @Kusalananda I make every effort not to use eval but it is required here. I would welcome any working modification that eliminates it. Dec 22 '20 at 8:46
0

I find bash find $(pwd) works well. Also, ls | xargs realpath works. You can add any other flags to the latter example as well as the first.

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.