我正在尝试创建一个脚本,它将从顶级目录进入每个子目录,创建几个目录,在子目录中的每个文件上运行命令,然后重命名并移动文件。我需要它来做这个没有任何额外的输入,比如给出目录的名称或任何东西,因为它几乎必须在数千个目录上运行。这是我写的剧本,我实际上并不理解:
#!/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脚本的错误。
命令本身很好,如果你进入文件夹并手动输入它们没有问题,所以抛出正确的变量存在某种问题。如果你能看到我的错误,我会非常感激。
答案 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