Linux将文件循环移动到其子目录

时间:2015-04-11 02:23:00

标签: linux bash

您能否建议在循环中将文件从一个位置移动到子目录的有效方法。

例如:

/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

5 个答案:

答案 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

我使用findxargs的原因是为了节省一些流程;您也可以逐个移动每个${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)

我的一行bash

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批量复制文件。完整的文件列表和目录列表永远不会存储在内存中,因此可以很好地扩展。