Listing files in another directory

Posted by: on

Problem

The other day I was editing a YAML file and wanted to include a list of filenames from a directory other than the current working directory. A simple ls does not do what I intended as it will recurse the remote directory listing all files and directories and, as it happened, the ‘files’ I wanted to list were actually directories (FYI, they were screenflow captures that MacOS helpfully presents as if they are files but are in fact composite directories). Since a simple ls yields undesired output I needed another solution.

Solution

Two solutions spring to mind:

  1. ls -d ../raw/screenflow/20200218* | xargs basename

  2. (cd ../raw/screenflow && ls -d1 20200218*)

Getting these directly into the file I was editing was simple enough. I was using vim so a simple :r!<command> read the output of the <command> straight into the buffer.

Discussion

Solution 1

The first solution pipes the output of the ls command into the basename command (using xargs to feed the ls output onto the command line as parameter). In its raw form the ls -d command lists matching files but (importantly for this application) does not recurse into directories. So far, so good, the output from this command does however list the full matching path. The basename is used to extract the last element of the path, specifically the Screenflow directory names.

1
2
3
4
5
6
7
8
$ ls -d ../raw.screenflow/20200218*
../raw/screenflow/202002181358.screenflow   ../raw/screenflow/202002181429.screenflow   ../raw/screenflow/202002181432.screenflow   ../raw/screenflow/202002181435.screenflow

$ ls -d ../raw/screenflow/20200218* | xargs basename
202002181358.screenflow
202002181429.screenflow
202002181432.screenflow
202002181435.screenflow

The shortcoming of this solution being that if the ls output is long (many matching paths or long paths) the xargs basename may break with the ‘argument list too long’ error. That said, the command line limit is usually very long (typically several thousand characters, check with getconf ARG_MAX).

Solution 2

The second solution is, I think, more elegant and will not fall foul of command line length limitations. The parentheses tell the bash shell to create a new subprocess in which the enclosed commands will be run. Using the subprocess means that any side-effects of the commands (for example changing the current directory with cd) will not effect our current session.

The ls -d1 tells the ls command to ‘not recurse into directories’ (-d) and the list one entry per line ('-1), rather than space separated on the line. As we changed our working directory to the target directory the paths contain only the matching file/directory names, exactly what we want.

1
2
3
4
5
$ (cd ../raw/screenflow && ls -d1 20200218*)
202002181358.screenflow
202002181429.screenflow
202002181432.screenflow
202002181435.screenflow