demonking
demonking

Reputation: 2654

Sorting folders with bash

My root folder structure is like this:

My Family - Holiday
My Birthday[15.11]
Name-Name[1]
Name1
Name2
...

Now, I want every folder which contains a .info file to move to another directory.

Here's my code:

#!/bin/sh

for folder in *
do
    echo $folder
    if [ -e "$folder/*.info" ]
    then
            echo $folder
            mv  $folder ./Finished
    fi
done

The echoes are only for testing. I have found that every time a non-escaped character is in the name that the if is failing. How can I fix this?

Upvotes: 1

Views: 168

Answers (3)

ghoti
ghoti

Reputation: 46816

Bash wants you to quote your variables.

#!/bin/bash

if [[ "$1" = "-v" ]]; then
    Verbose=true
    vopt="-v"
    shift
else
    Verbose=false
    vopt=""
fi

for folder in *; do
    $Verbose && printf "%s" "$folder"
    if [[ -e "$folder/*.info" ]]; then
            if mv "$vopt" "$folder" ./Finished/; then
                $Verbose && echo -n " ... done"
            fi
    fi
    $Verbose && echo ""
done

Note that it's a good idea to end your target directory with a slash. That way, if for some reason the Finished directory disappears, you'll get an error rather than silently renaming the first $folder to Finished, then moving all the other matches into the first match.

Note also that I'm using printf for some of the debugging output just in case one of your $folders starts with a hyphen.

UPDATE #1: you now have debugging controlled with the -v option.

UPDATE #2: I just realized that you are checking for the existence of *.info, literally. Note:

ghoti@pc ~$ mkdir foo
ghoti@pc ~$ touch foo/\*.info
ghoti@pc ~$ ls -la foo
total 0
-rw-r--r--   1 ghoti  ghoti    0  8 Aug 07:45 *.info
drwxr-xr-x   3 ghoti  ghoti  102  8 Aug 07:45 .
drwx------+ 10 ghoti  ghoti  340  8 Aug 07:44 ..
ghoti@pc ~$ [[ -e "foo/*.info" ]] && echo yes
yes
ghoti@pc ~$ mv foo/\*.info foo/bar.info
ghoti@pc ~$ [[ -e "foo/*.info" ]] && echo yes
ghoti@pc ~$

If what you really want to find is "any file ending in .info", then [[ -e is not the way to go. Pls confirm before I work more on this answer. :)

UPDATE #3:

Here's a version that finds moves your folder if the folder conains any .info file. Note that this does not grep the output of ls.

[ghoti@pc ~/tmp1]$ cat doit 
#!/bin/bash

if [[ "$1" = "-v" ]]; then
    Verbose=true
    vopt="-v"
    shift
else
    Verbose=false
    vopt=""
fi

for folder in *; do

    infofile="$(
        if [[ -d "$folder" ]]; then
            cd "$folder"
            for file in *.info; do
              if [[ -f "$file" ]]; then
                  echo "$file"
                  break
              fi
            done
        fi
    )"

    if [[ -f "$folder/$infofile" ]]; then
        mv "$vopt" "$folder" ./Finished/
    elif $Verbose; then
        printf "%s ... skipped\n" "$folder"
    fi

done
[ghoti@pc ~/tmp1]$ find . -print
.
./doit
./baz
./foo
./foo/bar.info
./Finished
[ghoti@pc ~/tmp1]$ ./doit -v
Finished ... skipped
baz ... skipped
doit ... skipped
foo -> ./Finished/foo
[ghoti@pc ~/tmp1]$ 

Upvotes: 6

TOC
TOC

Reputation: 4446

Try this:

    PATH_TO_FOLDER="/YOUR_FOLDER"

    for f in `ls $PATH_TO_FOLDER`;
    do
        # Check filename

        if [ $(echo $f | grep -Ec ".info$") -ne 1 ]
        then
            echo "We don't care"
        else
            ## move the file
        fi
    done

Upvotes: -1

William Pursell
William Pursell

Reputation: 212198

The primary issue is that you cannot use test -e *.info. You can parse the output of ls to check for the file. There are issues with parsing ls, but they are much less significant than many people make them out to be. If you do not allow newlines in filenames, the following should work:

#!/bin/sh

for dir in *; do
    if ls -f "$dir" | grep -q '\.info$'; then
        mv "$dir" ./Finished
    fi
done

Do note that it is essential that no filenames have an embedded newline, since this will incorrectly identify a file named foo.info\nbar as if there were a single file named foo.info. In reality, this is unlikely to be a significant issue. If this is an issue, you will not want to use the shell for this, although you could do:

#!/bin/sh

for dir in *; do
    for f in "$dir"/*; do
        case "$f" in 
            *.info) mv "$dir" ./Finished; break;;
        esac
    done
done

Upvotes: 3

Related Questions