skip to Main Content

I want to remove the extension of specific files with a given extension.

So for instance, in a directory foobar, we have foo.txt, bar.txt foobar.jpg.

Additionally, the extension that I’ve put in to be removed is txt

After calling the program, my output should be foo bar foobar.jpg

Here is my code so far:

#!/bin/bash
echo "Enter an extension"
read extension
echo "Enter a directory"
read directory
for file in "$directory"/*; do      //
        if [[ $file == *.txt ]]
        then
                echo "${file%.*}"
        else
                echo "$file"

        fi

done

However when I run this on a given directory, nothing shows up.

I’m assuming that there is a problem with how I referred to the directory ( in the line where I placed a //) and I’ve tried to research on how to solve it but to no avail.

What am I doing wrong?

3

Answers


  1. If files do exist in a valid directory you’ve entered then they should show up — with one exception. If you are using ~/ (shorthand home directory) then it will be treated as plain text in your for loop. The read variable should be substituted into another variable so the for loop can treat it as a directory (absolute paths should work normally as well).

    #!/bin/bash
    
    echo "Enter an extension"
    read -r extension
    echo "Enter a directory"
    read -r directory
    dir="${directory/#~/$HOME}"
    for file in "$dir"/*; do
            if [[ $file == *."$extension" ]]
            then
                    echo "${file%.*}"
            else
                    echo "$file"
            fi
    done
    
    Login or Signup to reply.
  2. You can simplify your for-loop:

    for file in "$directory"/*; do
        echo "${f%.$extension}";
    done
    

    The % instructions removes only matching characters. If nothing matches, the original string (here f) is returned.

    Login or Signup to reply.
  3. When you write bash scripts it’s more common to pass arguments to your script via command line arguments rather than by reading it from standard input via read program.

    Passing arguments via command line:

    #!/bin/bash
    
    # $# - a bash variable  which holds a number of arguments passed 
    # to script via command line arguments
    
    # $0 holds the name of the script
    
    if [[ $# -ne 2 ]]; then # checks if exactly 2 arguments were passed to script
        echo "Usage: $0 EXTENSION DIRECTORY"
        exit -1;
    fi
    
    echo $1; # first argument passed to script
    echo $2; # second arugment passed to script
    

    This approach is more efficient because a subprocess is spawn for read command to run and there is no subprocess spawn for reading command line arguments.

    There is no need to manually loop through directory, you can use find command to find all files with given extension within given directory.

    find /path/to/my/dir -name '*.txt' 
    
    find $DIRECTORY -name "*.$EXTENSION" 
    # note that  single quotes in this context would prevent $EXTENSION
    #  variable to be resolved, so double quotes are used " "
    
    # find searches for files inside $DIRECTORY and searches for files 
    # matching pattern '*.$EXTENSION'
    

    Note that to avoid bash filename expansion sometimes it is required to wrap actual pattern in single quotes ' ' or double quotes " ". See Bash Filename Expansion

    So now your script can look like this:

    #!/bin/bash
    if [[ $# -ne 2 ]]; then 
        echo "Usage: $0 EXTENSION DIRECTORY"
        exit -1;
    fi
    
    $EXTENSION = $1 #  for better readability
    $DIRECTORY = $2
    
    for file in `find $DIRECTORY -name "*.$EXTENSION"`; do
       mv $file ${file%.$EXTENSION}
    done
    
    

    Construct ${file%.$EXTENSION} is called Shell Parameter Expansion it searches for occurrence of .$EXTENSION inside file variable and deletes it.

    Notice that in the script it is easy to pass extension as directory and vice versa.

    We can check if second argument is in fact directory, we can use following construction:

    if ! [[ -d $DIRECTORY ]]; then
        echo $DIRECTORY is not a dir
        exit -1
    fi
    

    This way we can exit from the script earlier with more readable error.

    To sum up entire script could look like this:

    #!/bin/bash
    if [[ $# -ne 2 ]]; then 
        echo "Usage: $0 EXTENSION DIRECTORY"
        exit -1;
    fi
    
    EXTENSION=$1 #  for better readability
    DIRECTORY=$2
    
    if ! [[ -d $DIRECTORY ]]; then
        echo $DIRECTORY is not a directory.
        exit -1
    fi
    
    for file in `find $DIRECTORY -name "*.$EXTENSION"`; do
       mv $file ${file%.$EXTENSION}
    done
    

    Example usage:

    $ ./my-script.sh txt /path/to/directory/with/files
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search