0

I run into this scenario a lot:

  1. I'm in a big directory, and I am looking for a specific file that I forgot the exact name of, so I type ls to see all files in there.

  2. I see the file that I want

  3. I need to pass this file to the command-line as an argument. The file name is complex.

What I currently do is just highlight the ls output using the cursor/mouse, right click, and hit copy then paste it to my commandline prompt. I'd rather avoid this if there is a better way. I am aware of tab autocompletion, but sometimes its a situation where there are e.g. abcd abce abcf abcg abcz23 type of files, so the tab completion can be a bit unwieldy, unless you also have some advice there. Even if for example, I was able to copy the output of ls to the clipboard, I would need some way to narrow it down to my desired file so as to not copy other filenames to the clipboard also.

3

Check out fzf, it's perfect for tasks like find-one-match-in-a-big-list.

# Launches an interactive selector listing all the contents of the current
# directory and prints the selected item, storing it in $result
$ result=$(ls | fzf)
3

You could use bash's select construct with a "liberal" wildcard pattern (even *) that you're sure will include the filename you want.

Change to the directory you want (or fully-path the wildcard), then:

select mine in *
do
  your-command "$mine"
done

Enter the number corresponding to the file you want, and bash will run your command with that filename as an argument. Lather, rinse, and repeat as many times as you want, then hit Control-D to end the select loop. Of course, if you have a better idea of the filename, you can tighten the wildcard.

Using select keeps the filename safe from unintentional expansion or splitting, both of which you'd risk from copy/pasting with the mouse. For the former, consider a directory with two files: ab and ab*. If you copy/paste the ab* file, you'll instead get both files back, with ab first! For the latter, consider a directory with three files, a, b, and "a b". If you try to copy/paste the "a b" file, you'll instead pass the other files, a and b!

If it looks like too much typing, you can convert it to a "one-liner":

select mine in *; do your-command "$mine"; done
0

If you are using bash, you could invoke edit-and-execute-command (see the manpage). This will cause bash to open the current command line in your $VISUAL or $EDITOR of choice. Then you could use the editor to get the output of ls.

This is highly dependent on your configuration, but by default C-xC-e will do this. If you have bash set to vi mode then esc v will do it.

When you save and edit your editor, bash will execute the command.

Other shells have similar capabilities.

0

Load the output of ls into a bash array, then display the array with line numbering, and then use the line number to address the chosen line. Let me explain step by step:

  1. read the list of files into an array 'a':

readarray -t < <(ls) a

  1. display the array with line numbering starting from zero:

printf '%s\n' "${a[@]}"|nl -v 0

  1. use the xth line from the array:

./start_my_prog "${a[x]}"

Example:

$ ls -l  # just to show my example files in the directory
total 0
-rw-rw-r-- 1 chris chris 0 May  8 22:48  file1
-rw-rw-r-- 1 chris chris 0 May  8 22:48 'file 2 with spaces'
-rw-rw-r-- 1 chris chris 0 May  8 22:48 "LongFile-name-don't remember.txt"
-rw-rw-r-- 1 chris chris 0 May  8 22:48  something-else.txt

$ readarray -t < <(ls) a
$ printf '%s\n' "${a[@]}"|nl -v 0
     0  file1
     1  file 2 with spaces
     2  LongFile-name-don't remember.txt
     3  something-else.txt
$ echo "${a[2]}"
LongFile-name-don't remember.txt

It's up to you to create an alias for the commands like so:

alias myls='readarray -t < <(ls) a; printf "%s\n" "${a[@]}"|nl -v 0'

And use it:

$ myls
     0  file1
     1  file 2 with spaces
     2  LongFile-name-don't remember.txt
     3  something-else.txt
$ echo ${a[1]}
file 2 with spaces
0

Warning

This answer might give unexpected results, since it depends on piping the output of ls which is not recommended. For details, scroll down to the section labelled Edit 2.

Command Substitution

Command substitution $(command) will create a subshell, execute the command in the subshell, and then return the output of the subshell to the main shell.

The syntax would be:

main_command_to_run $(ls | grep 'name_of_file')

A common use case (for me at least) is when I am unzipping tar.gz files.

tar -xvzf $(ls | grep 'tar')

Some context - in this case, I know there is only one tar file in the current directory so I do not need to input the whole name of the tar.gz file

Of course, it would be a good idea to run ls | grep 'name_of_file' first to test that you end up getting only one result, unless you are pretty confident that the file name is unique.

EDIT 1

As @roaima pointed out, if your file name has spaces, you will need to add double quotes to the expression: main_command_to_run "$(ls | grep 'name_of_file')"

Using wildcards (globbing)

An alternative to using the $(ls | grep 'name_of_file') syntax would be via globbing. Wildcard characters such as '?', '*', and '[]' among others are used to match characters.

To provide an example using asterisks, an asterisk ('*') is a wildcard that matches zero or more characters. So a possible use case for a file named 'foo.tar.gz' would be, as @roaima kindly supplies,:
tar -xvzf *.tar*

EDIT 2

As @Hermann pointed out also, piping the output of ls is unreliable for a variety of reasons. See the following links for an explanation as to why:
Link 1
Link 2
Link 3
Link 4

6
  • 2
    If your filenames contain spaces (as shown in the OP's example) you would need to double quote the $(...) in order to keep the result as a single word. You also don't need ls | grep tar when you can just use a glob such as ls *tar*. And then you can just dispense with the subshell entirely, so main_command_to_run *name_of_file* or tar -xvzf *.tar* – roaima Nov 24 '20 at 17:01
  • @roaima I think the OP meant that tab auto-completion would be unworkable for files named 'abcd', 'abce', etc, as only the final character is different for those files. But thanks for pointing out about the need for double quotes and the alternative of using glob anyway. I'll add that on as an edit, if that's fine by you. – Bongotz Nov 26 '20 at 16:47
  • 1
    As someone who regularly deals with files and directories with spaces in them: Never ever pipe the output of ls anywhere. It is not reliable. There may be some situations where it is safe and therefore okay, but not good for a general advice. Always prefer bash expansion. – Hermann Nov 26 '20 at 16:57
  • @Hermann Would you recommend globbing over piping the output of ls then? I realised that you have a point in that the output of ls can be tricky to deal with – Bongotz Nov 26 '20 at 17:22
  • Some links I found that might be useful - Link 1 Link 2 Link 3 Link 4 – Bongotz Nov 26 '20 at 17:25
-1

you should be able to use: ls -lrt | grep(char string) > your code. Where character string is your files name or characters that will enable grep to find your file name.

if file name was xxx.rhbd.yuv.txt you could run

ls -lrt | grep rhdb > your code.

If multiple files are found, each will be sent to your code.

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.