I have a directory called all_files. My script is in the main directory main_dir (all_files is a subdirectory of main_dir).

all_files contains pdf files and only 1 docx file. I want to output all the pdf files' filenames in a reverse alphabetical order. I tried this:

tail -r all_files

But that includes also that docx file that I want to exclude and it doesn't do the job. Does anyone know how to do this?

  • 1
    By "output" do you mean output the file names, or their contents? – steeldriver Oct 20 '19 at 17:49

ls accepts -r/--reverse to reverse the order of its output. Since its output is in alphabetical order by default, reversing it should do what you want:

ls -r all_files/*.pdf

That shows the prefix all_files/ on each filename. If you don't want to do that, you can use:

(cd all_files && ls -r -- *.pdf)

The parentheses cause the command to be run in a subshell so that the effect of changing directory to all_files does not persist after the command has run (i.e., you stay where you were afterward). The additional -- argument to ls indicates the end of options, which makes it work even in the weird case where one of the .pdf files has a name that starts with - and that could thus otherwise be interpreted as an option to ls rather than as a filename argument.

If you want to list one filename per line regardless of whether or not ls is outputting to a terminal (and regardless of the width of the terminal), you can pass ls the -1 option as well:

ls -1r all_files/*.pdf
(cd all_files && ls -1r -- *.pdf)

(Passing the -1 and -r options as two separate arguments also works, and they may be passed in either order. That is, -1r, -r1, -1 -r, and -r -1 all work and have the same effect.)

Note that -r/--reverse should not be confused with -R/--recursive, which would list files in all subdirectories too, and in their subdirectories, and so forth. Some programs accept an -r option to mean "recursive," but (at least in Ubuntu) ls -r performs a reverse listing, not a recursive listing. You can write it out as ls --reverse if you prefer. For example:

ls -1 --reverse all_files/*.pdf

Since you have all .pdf files except one file with a .docx extension, ls -r on the directory as shown above should be sufficient. However, in the related situation where there were files named many ways but you only wanted to omit those named ending in .docx, you could use this instead:

ls -r all_files/!(*.docx)
(cd all_files && ls -r -- !(*.docx))

That uses a Bash feature called "extended globbing." This is enabled by default in interactive shells, but not enabled by default in shell scripts. You can check if it is enabled with shopt extglob, enable ("set") it with shopt -s extglob, and disable ("unset") it with shopt -u extglob. You can use a subshell to enable it for just one command, as shown for cd above. With that technique, the above two commands become:

(shopt -s extglob; ls -r all_files/!(*.docx))
(shopt -s extglob; cd all_files && ls -r -- !(*.docx))
  • That's great thanks. However, it displays the output in three columns. Is there a way to have each name in one line? – Jim6834 Oct 20 '19 at 18:33
  • @Jim6834 Yes, you can pass the -1 option to ls. I'll edit this shortly to cover that. – Eliah Kagan Oct 20 '19 at 18:35

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.