您能否建议在循环中将文件从一个位置移动到子目录的有效方法。
例如:
/MY_PATH/User1/1234/Daily/abc.txt
至/MY_PATH/User1/1234/Daily/Archive/abc.txt
/MY_PATH/User2/3456/Daily/def.txt
至/MY_PATH/User2/3456/Daily/Archive/def.txt
/MY_PATH/User1/1111/Daily/hij.txt
至/MY_PATH/User1/1111/Daily/Archive/hij.txt
/MY_PATH/User2/2222/Daily/def.txt
至/MY_PATH/User2/2222/Daily/Archive/def.txt
我是以这种方式开始的,但需要你的建议和最佳方式来写它:
#!/bin/bash
dir1="/MyPath/"
subs= `ls $dir1`
for i in $subs; do
mv $dir1/$i/*/Daily $dir1/$i/*/Daily/Archive
done
答案 0 :(得分:1)
尝试以下方法:
dir1="/MyPath"
for d in "$dir1"/*/*/Daily/; do
[[ -d $d ]] || break # break, if no subdirectories match
for f in "$d"/*; do # loop over files in */*/Daily/
[[ -f "$f" ]] || continue # skip non-files or if nothing matches
mv "$f" "$d"/Archive/
done
done
"$dir1"*/*/Daily/
匹配$dir1
的所有孙子子目录;感谢终止/
,只有目录匹配;请注意,因此$d
以/
结尾。
$d
因此以/
结尾,严格来说,在与其合成路径时不需要/
(例如"$d"/*
)但正如@ 4ae1e1在评论中指出的那样,这样做并没有伤害并且有助于提高可读性。 [[ -d $d ]] || break
确保如果没有孙子目录匹配则退出循环(默认情况下,没有匹配的glob(模式)将传递给到循环)。for f in "$d"*
遍历$d
中的所有条目(文件和/或子目录。):
[[ -f "$f" ]] || continue
确保只处理文件,或者如果 nothing 匹配,则退出循环。mv "$f" "$d"/Archive/
然后将每个文件移动到subdir。 Archive
。答案 1 :(得分:1)
假设目录结构如您在示例中所示,即
MY_PATH/
subdir-level-1/
subdir-level-2/
Daily/
files
Archive/
以下是您可以做的事情:
shopt -s nullglob # defend against globbing failure -- inspired by mklement0's answer
root="/MyPath"
for dir in "${root}"/*/*/Daily/; do
mkdir -p "${dir}/Archive" # if Archive might not exist; to be pedantic you should look at David C. Rankin's answer for error handling, but usually we know what we're doing so that's not necessary
find "${dir}" -maxdepth 1 -type f -print0 | xargs -0 mv -t "${dir}/Archive"
done
我使用find
和xargs
的原因是为了节省一些流程;您也可以逐个移动每个${dir}
中的文件。
更新:@ mklement0建议find "${dir}" -maxdepth 1 -type f -print0 | xargs -0 mv -t "${dir}/Archive"
可以进一步改进
find "${dir}" -maxdepth 1 -type f -exec mv -t "${dir}/Archive" +
这是一个非常好的观点。
答案 2 :(得分:1)
for dir in $(
find MY_PATH -mindepth 3 -maxdepth 3 -type d -name Daily
);do
mkdir -p $dir/Archives
find $dir -maxdepth 1 -mindepth 1 ! -name Archives \
-exec mv -t $dir/Archives {} +
done
快速测试:
mkdir -p MY_PATH/User{1,2,3,4}/{1234,2346,3333,2323}/Daily
touch MY_PATH/User{1,2,3,4}/{1234,2346,3333,2323}/Daily/{abc,bcd,def,feg,fds}.txt
for dir in $( find MY_PATH -mindepth 3 -maxdepth 3 -type d -name Daily );do
mkdir -p $dir/Archives; find $dir -maxdepth 1 -mindepth 1 ! -name Archives \
-exec mv -t $dir/Archives {} + ; done
ls -lR MY_PATH
这似乎符合OP的要求
有一个解决方案,它可以在路径的某个地方使用空间......
编辑包含@ mklement0&#39的明确建议。
while IFS= read dir;do
mkdir -p "$dir"/Archives
find "$dir" -maxdepth 1 -mindepth 1 ! -name Archives \
-exec mv -t "$dir/Archives" {} +
done < <(
find MY_PATH -mindepth 3 -maxdepth 3 -type d -name Daily
)
相同的演示;
mkdir -p MY_PATH/User{1,2,3,"4 3"}/{1234,"23 6",3333,2323}/Daily
touch MY_PATH/User{1,2,3,"4 3"}/{1234,"23 6",3333,2323}/Daily/{abc,"b c",def,hgz0}.txt
while read dir;do mkdir -p "$dir"/Archives;find "$dir" -maxdepth 1 -mindepth 1 \
! -name Archives -exec mv -t "$dir/Archives" {} +; done < <(
find MY_PATH -mindepth 3 -maxdepth 3 -type d -name Daily )
ls -lR MY_PATH
答案 3 :(得分:0)
您需要检查,如果不存在,则在将文件移动到Archive
之前创建目标目录。如果您无法创建目录(由于权限或其他原因),则跳过此步骤。以下内容不对深度进行任何限制,但会省略包含Archive
作为中间子目录的任何目录:
oldifs="$IFS"
IFS=$'\n'
for i in $(find /MY_PATH -type f); do
[[ "$i" =~ Archive ]] && continue
[ -d "${i%/*}/Archive" ] || mkdir -p "${i%/*}/Archive"
[ -d "${i%/*}/Archive" ] || {
printf "error: unable to create '%s'\n" "${i%/*}/Archive"
continue
}
mv -fv "$i" "${i/Daily/Daily\/Archive}"
done
IFS="$oldifs"
运行时输出
$ bash archive_daily.sh
mv -fv /MY_PATH/User1/1111/Daily/hij.txt /MY_PATH/User1/1111/Daily/Archive/hij.txt
mv -fv /MY_PATH/User1/1234/Daily/abc.txt /MY_PATH/User1/1234/Daily/Archive/abc.txt
mv -fv /MY_PATH/User2/3456/Daily/def.txt /MY_PATH/User2/3456/Daily/Archive/def.txt
mv -fv /MY_PATH/User2/2222/Daily/def.txt /MY_PATH/User2/2222/Daily/Archive/def.txt
注意:您可以通过调整填充for循环的find
调用来限制/收紧文件选择(例如-name
或-iname
)。这只是检查/移动每个文件到其Archive
文件夹。要仅限制使用.txt
扩展名的文件,您可以指定find /MY_PATH -type f -name "*.txt"
。要仅限制/MY_PATH/User1
和/MY_PATH/User2
目录中扩展名为.txt
的文件,请使用find /MY_PATH/User[12] -type f -name "*.txt"
。
注2:在循环文件名时,路径&amp;文件名不应包含当前语言环境的非标准字符。当然,你不应该在你的文件名中使用'\n'
作为字符。需要设置IFS
以防止路径或文件名中的空格分词。
答案 4 :(得分:-1)
由于您说高效,所以带有子shell的任何东西都会以有趣的方式失败并且有很多条目。你最好使用xargs
:
#!/bin/bash
dir1="/MyPath/"
find $dir1 -name Daily -type d -depth 3 | while read i
do
pushd .
cd $i
mkdir Archive
find . -type f -depth 1 | xargs -J {} mv {} Archive
popd
done
外部find
将查找您的Daily
目录。它非常具体,因为它们必须位于某个深度和目录,而不是常规文件。结果将通过管道输入read
,其中输入每个目录,创建Archive
,并使用xargs ... mv
批量复制文件。完整的文件列表和目录列表永远不会存储在内存中,因此可以很好地扩展。