迭代地遍历子目录并对其中的所有文件执行命令

时间:2014-05-14 18:51:03

标签: bash variables iteration subdirectory

我正在尝试创建一个脚本,它将从顶级目录进入每个子目录,创建几个目录,在子目录中的每个文件上运行命令,然后重命名并移动文件。我需要它来做这个没有任何额外的输入,比如给出目录的名称或任何东西,因为它几乎必须在数千个目录上运行。这是我写的剧本,我实际上并不理解:

#!/bin/bash
for path in ./*;
do
[ -d "${path}" ] || continue
dirname="$(basename "${path}")"
cd $path
mkdir stats
mkdir dir
files=(./*)
for f in "${files[@]}"
do
do stuff
done
cd dir
mkdir stats
mv *.trimmed_segments* stats/
rename '.fastq.trimmed' '.fastq' ./*
cd ..
mkdir raw
mv *.fastq raw/
mv trimmed/*.fastq ./
cd ..
done

它似乎可以在一些子目录上工作,奇怪的是,然后开始发送并发送一堆关于非常特定的perl脚本的错误。

命令本身很好,如果你进入文件夹并手动输入它们没有问题,所以抛出正确的变量存在某种问题。如果你能看到我的错误,我会非常感激。

3 个答案:

答案 0 :(得分:1)

您是否考虑过将find与exec选项一起使用?

我通常在需要的时候使用它"走过"一条路。

根据您的需要,使用最小/最大深度值。您还可以执行将运行一组命令的脚本。

#Directories
find ./ -mindepth 1 -maxdepth 1 -type d -exec bash -c 'mkdir {}/stats; mkdir {}/dir' \;

#Files
find ./ -maxdepth 1 -type f -exec bash -c 'commands here...' \;

答案 1 :(得分:0)

我认为这本身并不是一个答案,但它会帮助您和未来的回答者调试,如果有足够的其他信息,我会更新它。

这是带缩进和注释的脚本:

#!/bin/bash
for path in ./*; do
  [ -d "${path}" ] || continue
  dirname="$(basename "${path}")"
  cd $path                               # origin/$path
  mkdir stats                            # (this seems not to be used)
  mkdir dir
  files=(./*)
  for f in "${files[@]}"; do
    # do stuff
  done
  cd dir                                 # origin/$path/dir
  mkdir stats
  mv *.trimmed_segments* stats/
  rename '.fastq.trimmed' '.fastq' ./*
  cd ..                                  # origin/$path
  mkdir raw
  mv *.fastq raw/
  mv trimmed/*.fastq ./
  cd ..                                  # origin
done

您可能需要考虑的一种技术是使用()作为grouping commands to create subshells,这也将封装对工作目录的任何更改。就目前而言,您使用了许多cd的相对路径,这使得很难准确判断您的位置 - 特别是如果任何命令失败。 (为此,当任何命令以错误代码退出时,使用set -e立即退出,这可以帮助您确定发生错误的位置。)

#!/bin/bash
set -e                                   # fail immediately on error
for path in ./*; do
  [ -d "${path}" ] || continue
  dirname="$(basename "${path}")"
  (
    cd $path                             # origin/$dirname
    mkdir stats
    mkdir dir
    files=(./*)
    for f in "${files[@]}"; do
      # do stuff
    done
    (
      cd dir                             # origin/$dirname/dir
      mkdir stats
      mv *.trimmed_segments* stats/
      rename '.fastq.trimmed' '.fastq' ./*
    )
    mkdir raw
    mv *.fastq raw/
    mv trimmed/*.fastq ./
  )
done

答案 2 :(得分:0)

另一种技术,如果它是一个任意深度的树,就是创建一个函数,并以递归方式调用它(也就是说,从它自身内部;当它每次完成时,它将返回到它被调用的地方)。 / p>

我还没有尝试过这个,但它看起来像是:

processDir () {
    cd "$1"
    # it's a directory, so what you need to for a directory here
    # ...
    for path in ./*; do
        if [[ -d "${path}" ]]; then
            processDir "${path}"
        else
            # it's a file, not a dir, so do file operations here, if any
            # ...
        fi
    done
}

processDir yourStartDirectory