A Guide to Searching through the Command Line
Tags: beginners, tutorial, productivity, learning
Published: 2023-08-09 00:01:03

## `locate`

- The `locate` command performs a search of pathnames across our machine that match a given substring and then prints out any matching names.
- You have to install this command as it does not comes pre-installed
- It is nice and speedy because it uses a pre-generated database file rather than searching the entire machine.
- For example, `locate chick` will perform a search for all files that contain chick in their name
- It does not matter which directory you are in as you can locate folders which are in other directories
- The **-i** option tells locate to ignore casing
- The **-l** or --limit option will limit the number of entries to a number.

## `find`

- The locate command is nice and easy, but it can only do so much! The **find** command is far more powerful! Unlike locate, find does not use a database file.
- By default, `find` on its own will list every single file and directory nested in our current working directory and also nested in the sub-directories.
- We can also provide a specific folder. `find friends/` would print all the files and directories inside the friends directory (including nested folders)
- You can also find empty files by running `find -empty`
    
    ### Finding by Type
    
    - We can tell find to only find by file type: only print files, directories, symbolic links, etc using the -type option.
    - `find -type f` will limit the search to files
    - `find -type d` will limit the search to directories
    
    ### Finding by Name
    
    - We can provide a specific pattern for `find` to use when matching filenames and directories with the **-name** option. We need to enclose our pattern in quotes.
    - To find all files on our Desktop that end in the .txt extension, we could run `find ~/Desktop -name "*.txt"`
    - Use the -iname option for a case insensitive search
    
    ### Finding by Size
    
    - We can use the **-size** option to find files of a specific size. For example, to find all files larger than 1 gigabyte we could run `find -size +1G`
    - To find all files under 50 megabytes, we could run `find -size -50M`
    - To find all files that are exactly 20 kilobytes, we could run `find -size 20k`
    
    ### Finding by Time
    
    - **mtime**, or modification time, is when a file was last modified AKA when its contents last changed.
    - **ctime**, or change time, is when a file was last changed. This occurs anytime mtime changes but also when we rename a file, move it, or alter permissions.
    - **atime**, or access time, is updated when a file is read by an application or a command like cat.
    - We can use the `-mtime num` option to match files/folders that were last modified num*24 hours ago.
    - `find -mmin -20` matches items that were modified LESS than 20 minutes ago.
    - `find -mmin +60` matches items that were modified more than 60 minutes ago
        
        ```bash
        find -mmin -30
        ```
        
    - `find -amin n` will find files that were last accessed n minutes ago. We can specify +n for "greater than n minutes ago" and -n for "less than n minutes ago"
    - `find -cmin -20` matches items that were changed LESS than 20 minutes ago.
    - `find -cmin +60` matches items that were changed more than 60 minutes ago
    
    ### Logical Operators
    
    - We can also use the **-and**, **-or**, and **-not** operators to create more complex queries.
        
        ```bash
        find -type -f -not -name "*.html" # any file except .html
        ```
        

## User-Defined Actions

- We can provide find with our own action to perform using each matching pathname.
- The syntax is `find -exec command {} ;`
- The {} are a placeholder for the current pathname (each match), and the semicolon is required to indicate the end of the command.
- To delete every file that starts with contains "broken" in its file name, we could run:
    
    ```bash
    find -name "*broken*" -exec rm '{}' ';'
    ```
    
    Note that we need to wrap the {} and ; in quotes because those characters have special meanings otherwise
    

## `xargs`

- When we provide a command via `-exec`, that command is executed separately for every single element.
- We can instead use a special command called `xargs` to build up the input into a bundle that will be provided as an argument list to the next command.
    
    ```bash
    find -name "*.txt" -exec ls '{}' ';'
    find -name "*.txt" | xargs ls
    ```
    
- This example finds four individual chapter files (chapter1, chapter2, chapter3, and chapter4) and then passes them to the cat command, which then outputs the combined contents to a file called fullbook.txt.
    
    ```bash
    find -name "chapter[1-4].txt" | xargs cat > fullbook.txt
    ```
    
- xargs reads items from standard input, separated by blanks (spaces or newlines) and then executes a command using those items
- The mkdir command expects us to pass arguments. It doesn't work with standard input, so this example does NOT make any folders for us
- We can instead add in the xargs command, which will accept the standard input coming from echo and pass them as arguments to mkdir.
    
    ```bash
    echo "hello" "world" | mkdir # mkdir: missing operand
    echo "hello" "world" | xargs mkdir
    ls # hello world two folders made
    ```
    

# Grep

- The `grep` command searches for patterns in each file's contents. Grep will print each line that matches a pattern we provide.
- For example, `grep "chicken" animals.txt` will print each line from the animals.txt file that contains the pattern "chicken"
    
    ```bash
    grep PATTERN FILE
    ```
    
- Use the **-i** option with grep to make the search case insensitive.
- `grep -i "Chapter" book.txt` will print all matching lines from the book.txt file that contain the word "chapter" in any casing.
- Use the **-w** option to ensure that grep only matches words, rather than fragments located inside of other words.
- `grep -w "cat" book.txt` would match cat but not catheter!
- Use the **-r** option to perform a recursive search which will include all files under a directory, subdirectories and their files, and so on.
- `grep -r "chicken"` will search the current working directory and any nested directories for lines that contain "chicken"
- The **-c** option tells grep to print the number of matches instead of printing the actual matches
- The **-o** option tells grep to only print out the matches, rather than the entire line containing each match.
- We can also provide regular expressions to grep by using the **-E** option