在循环中创建目录并将文件移动到这些目录

在循环中创建目录并将文件移动到这些目录

考虑一个包含 N 个文件的目录。

我可以按字母顺序列出文件并按以下方式存储列表

ls > list

然后我想在同一目录中创建n个子目录,可以通过以下方式完成

mkdir direc{1..n}

现在我想将前 m 或前 5 (1-5) 个文件从 移动listdirec1,接下来的 5 个文件即 6-10 移动到 ,direc2依此类推。

这对你们来说可能是一项微不足道的任务,但目前我无法做到。请帮忙。

答案1

list=(*)     # an array containing all the current file and subdir names
nd=5         # number of new directories to create
((nf = (${#list[@]} / nd) + 1))  # number of files to move to each dir
                                 # add 1 to deal with shell's integer arithmetic

# brace expansion doesn't work with variables due to order of expansions
echo mkdir $(printf "direc%d " $(seq $nd))

# move the files in chunks
# this is an exercise in arithmetic to get the right indices into the array
for (( i=1; i<=nd; i++ )); do
    echo mv "${list[@]:(i-1)*nf:nf}" "direc$i"
done

测试后删除两个“echo”命令。

或者,如果您想每个目录包含固定数量的文件,这更简单:

list=(*)
nf=10
for ((d=1, i=0; i < ${#list[@]}; d++, i+=nf)); do
    echo mkdir "direc$d"
    echo mv "${list[@]:i:nf}" "direc$d"
done

答案2

#!/bin/sh
d=1                # index of the directory
f=0                # number of files already copied into direc$d
for x in *; do     # iterate over the files in the current directory
  # Create the target directory if this is the first file to be copied there
  if [ $f -eq 0 ]; then mkdir "direc$d"; fi
  # Move the file
  mv -- "$x" "direc$d"
  f=$((f+1))
  # If we've moved 5 files, go on to the next directory
  if [ $f -eq 5 ]; then
    f=0 d=$((d+1))
  fi
done

有用的参考:

答案3

d=0; set ./somedirname                         #init dir counter; name
for f in ./*                                   #glob current dir
do   [ -f "$f" ] && set "$f" "$@" &&           #if "$f" is file and...
     [ "$d" -lt "$((d+=$#>5))" ]  || continue  #d<(d+($#>5)) or continue
     mkdir "$6$d" && mv "$@$d"    || !  break  #mk tgtdir and mv 5 files or
     shift 5                                   #break with error
done

上面的命令利用了 shell 的 arg 数组将字符串连接到其头部和尾部的能力。例如,如果您编写了一个函数:

fn(){ printf '<%s>\n' "42$@"; }

...并称其为:

fn show me

...它会打印:

<42show>
<me>

...因为您可以添加或附加到第一个或最后一个元素(分别)在 arg 数组中,只需将其引号括在您的前/附加字符串周围即可。

arg 数组在这里也有双重作用,因为它还充当计数器 - $#shell 参数总是让我们确切地知道到目前为止我们已经堆叠了多少个元素。

但是......这是一步一步:

  1. d=0; set ./somedirname
    • $d每创建一个新目录,var 就会加一。这里它被初始化为零。
    • ./somedirname是你喜欢的样子。不过,前缀./很重要 - 它不仅可以将所有操作根植到当前目录,还允许您指定您想要的任何类型的名称(如果你想疯狂地使用换行符或以连字符开头,你可以安全地 - 但这可能是不可取的)。因为 argname 总是以./no 命令开头,所以不会将其误解为命令行中的选项。
  2. for f in ./*
    • 这将开始一个循环(如果有的话)*当前目录中的匹配项。同样,每个匹配项都以 为前缀./
  3. [ -f "$f" ]
    • 验证每次迭代的匹配肯定是常规文件(或一个链接)和...
  4. set "$f" "$@"
    • 将 shell 数组中的匹配项堆叠在另一个前面。这样,./somedirname始终位于数组的尾部。
  5. [ "$d" -lt "$((d+=$#>5))" ]
    • $d如果数组元素超过 5 个,则加 1 ,"$@"同时测试增量结果。
  6. || continue
    • 如果任何一个[ -f "$f" ] set ... [ "$d" -lt...命令未返回 true,则循环将继续进行下一次迭代,并且不会尝试完成循环的其余部分。这样既高效安全的。
  7. mkdir "$6$d"
    • 因为该子句确保我们只有在our现在处于并且 的值刚刚增加 1 的continue情况下才能达到这一点。因此,对于匹配和移动的第一组 5 个文件,这会为第五个文件创建一个名为 和的目录,依此类推。重要的是,这个命令$# > 5./somedirname$6$d./somedirname1./somedirname5失败如果任何具有目标路径名的路径名已存在。换句话说,这个命令是仅有的如果确实不存在同名的目录,则成功。
  8. mv "$@$d"

    • 这会扩展数组,同时将 的值附加$d到最后一个元素的尾部 - 这是目标目录名称。所以它扩展如下:

    mv ./file5 ./file4 ./file3 ./file2 ./file1 ./somedirname1

    • ...这正是您想要发生的事情。
  9. || ! break

    • 如果前两个命令中的任何一个由于任何原因未成功完成,则for循环break将停止。发送的返回值!的布尔值倒数break(通常为零),因此break返回 1。这样,如果前面的任何命令中发生任何错误,循环将返回 false。这很重要 -for循环 - 与while/until循环不同 - 并不意味着测试,仅意味着迭代。如果没有显式测试这两个命令的返回,shell 不一定会因错误而停止 - 并且set -e可能会完全杀死父 shell。相反,这确保了有意义的返回,并且如果出现任何问题,循环将不会继续迭代。

    • 乍一看,这似乎是这里唯一会停止的答案,例如,如果mkdir ./somedirname不返回 true - 所有其他答案将继续循环(并且可能会重复该错误,或​​者更糟糕的是,将当前目录中的文件移动到现有目录中,并可能覆盖那里同名的其他文件)。在循环中使用任意文件名,您应该总是测试两者源文件是否存在因为目标的存在。

  10. shift 5
    • shift会删除 shell arg 数组中的前 5 个元素 - 它将放回./somedirname$1重置数组状态以进行下一次迭代。

答案4

使用这个 awk 程序,您可以创建 shell 命令,如果有疑问,可以提前检查它们是否正确......

awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }' list

如果您对输出管道没意见,则将整个命令放入sh.另外,如果您想避免额外的文件“列表”,您可以动态创建它;那么整个程序将是......

ls  |  awk -v n=5 '{ printf "mv \"%s\" %s\n", $0, "direc" int((NR-1)/n)+1 }'  |  sh

如果更改设置 n=5,则可以定义 5 以外的其他值。

如果您还想动态创建目标目录,这里有一个变体......

ls  |  awk -v n=5 '
         NR%n==1 { ++c ; dir="direc"c ; print "mkdir "dir }
         { printf "mv \"%s\" %s\n", $0, dir }
'  |  sh

相关内容