reality displays
reality displays

Reputation: 751

how to navigate to subdirectories in a shell script

I am having a directory.This directory has a lot of subdirectories which contain html pages and some c source code files,Makefiles etc etc.The script I am trying to write would be executed from one directory which has all these subdirectories. Following is the set of directories and files you can see

ls
delete.sh  lddbus   Makefile      misc-progs  sbull  scullc  scullp  short       simple  snull  usb
include    LICENSE  misc-modules  pci         scull  sculld  scullv  shortprint  skull   tty

Some of them are directories and some are files in the subdirectories above there are further subdirectories and html pages also which I want to eliminate. The manual way would be do go to each directory and remove the pages via following command

rm *.html*

Since the html page has name ending with ?=/something sort of names. So I decided to write a shell script. But what I am not clear is how will I take directory names as arguments in my shell script.If I decided to use a for loop or some thing similar. In this case what should I be doing? I do not want to use

find . -name '*.html*' -exec rm -f {} \;

As I am doing this for learning purpose.

Upvotes: 1

Views: 7773

Answers (5)

Marek Sapota
Marek Sapota

Reputation: 20591

This code snippet will run ls for every subdirectory in current dir:

for d in * .[!.]* ..?*; do
    test -d "${d}" && ls "${d}"
done

You can adapt it to run a command, for each subdirectory, that you like.

If you want to get deeper in the directory hierarchy, you can wrap this code in a function and rerun it for every subdir.

function f {
    cd "$1"
    # do something in this dir
    for d in * .[!.]* ..?*; do
        cd "$1"
        test -d "$1/$d" && f "$1/$d"
    done
}

f "`pwd`"

For Zsh you'd probably want to set NULL_GLOB option (-G switch), so it doesn't report errors if there are no hidden dirs, in Bash it works by default.

Upvotes: 2

Dennis Williamson
Dennis Williamson

Reputation: 359935

This is based on maarons' answer, but with two advantages:

  • Using */ instead of * in the for loop means it only iterates over directories instead of all files for a potentially big speedup
  • Using pushd and popd makes it easy to support . or no argument to mean the current directory.

Here is the function:

g () {
    local d bb=/dev/null # bit-bucket
    pushd "$1" > $bb
    pwd    # do something (pwd is a placeholder)
    for d in */
    do
        test -d "$PWD/$d" && g "$PWD/$d"    # you could use $(pwd) instead of $PWD
    done
    popd > $bb
}

Of course, using find with -execdir is simpler and gives the same results.:

find -mindepth 1 -type d -execdir sh -c 'cd {}; pwd' \;    # (pwd` is a placeholder)

Upvotes: 0

Giacomo
Giacomo

Reputation: 11247

find is the way to go. You will not learn anything useful choosing the wrong way to solve a problem.

Print a list of files you want to remove

find /your/dir -type f -iname '*.html*'

and delete them

find /your/dir -type f -iname '*.html*' -delete

find is a powerful command, learn to use it.

Another way to improve the command you don't want to use:

find /your/dir -type f -name '*.html*' -exec rm -f {} +

(hint: man find to learn how -exec ; and -exec + differ)

Note that shell scripting is just using in the best useful way the small programs like find, cat, ls, wc, etc. Knowing these utilities thoroughly is a necessary requisite to learn shell scripting.

Upvotes: 1

Thomas Jung
Thomas Jung

Reputation: 33092

With bash 4 (or zsh) you can use globstar "**" to match recursively.

shopt -s globstar
echo **/*html*

With a directory setup like this:

mkdir -p {a..b}/{c..d}
touch {a..b}/{c..d}/{e..f}.{htmlx,other}

It will result in:

a/c/e.htmlx a/c/f.htmlx a/d/e.htmlx a/d/f.htmlx b/c/e.htmlx b/c/f.htmlx b/d/e.htmlx b/d/f.htmlx

Upvotes: 3

SiegeX
SiegeX

Reputation: 140257

Create a function like so:

myfunc()
{
  for dir; do
      if [[ -d "$dir" ]]; then
          echo rm -rf "${dir}/*.html*"
      else
          echo "No such dir '$dir'"
      fi
  done
}

Then call the function by passing the directories you want to remove .html from

Output

$ myfunc /foo /etc /root
No such dir '/foo'
rm -rf /etc/*.html*
rm -rf /root/*.html*

Once you are satisfied with the output, remove the echo to have it really delete the files.

Upvotes: 0

Related Questions