首页 > 解决方案 > 是否可以将文件分组移动到需要每四个文件更改一次的目录?

问题描述

我很欣赏标题可能不清楚,但我找不到更好的措辞。本质上,我想知道是否可以告诉外壳

a) 将前四个文件移动到目录 a

b) 将第二组四个文件移动到目录 b 等大约一千个文件。

但是,目录 a 和目录 b(以及所有其他目录)的名称没有共同点,所以我知道我必须创建一个脚本来监视

a) 移动的文件数,

b) 已经包含所有需要的文件的目录数量,以及

c)接下来要写入哪个目录(我假设这将涉及某种由 shell 分配的目录数字顺序)。

此外,这很复杂,因为并非所有目录在完成时都会有四个与之关联的文件,所以我需要某种方式告诉 shell 这些目录已经完成(即它们包含与它们关联的所有文件)。

感谢您的时间。

编辑:需要移动的文件是 .zip 文件,格式为“x_y_z.zip”,其中

x = 所有文件通用的字符串

y = 标识哪个文件属于哪个组的字符串

z = 每个组中每个第 n 个文件唯一的字符串。

一些组包含的文件较少,但没有一个组中的文件超过四个。

我需要做的是逐个文件地浏览目录,并将共享“y”的 .zip 文件移动到包含该组且仅包含该组的另一个目录。我遇到的问题是目录名称不能包含“y”,因此我需要一种方法让 shell 在“y”发生更改时通知并将文件移动到列表中的下一个目录。

编辑 2:所以我已经大致确定了我需要做的事情 - 如果我可以将所有 zip 文件的名称放在一个 .txt 文件中,并将目标目录的所有名称放在另一个文件中,我可以告诉 shell从 directory.txt 中读取并将其用作 mv 命令的目标。我遇到的唯一问题是当 'y' 更改时如何告诉 shell 转到下一个目录,但我想我已经解决了这个问题。我现在只需要一个命令将 file.txt 中的第一个文件 mv 到 directory.txt 中的第一个目录。这是我到目前为止所拥有的:

!/bin/bash

cd ~/Downloads/ZIP
ls -rt *.zip > file.txt

a=1

for i in *.zip
do
    mv 'line a of file.txt' 'line a of directory.txt' 
    let a=a+1
done

ls -rt 命令是必要的,因为虽然文件确实具有某种识别系统,但它与我正在使用的 id 系统完全无关,但幸运的是我按照目录中列出的顺序创建了文件目录.txt 文件。

标签: bashfor-loop

解决方案


复制文件描述符来救援。
这个怎么样?

last=''                          # pre-sets the comparator to empty
while read -u 4 file             # read a filename 
do IFS=_ read x y z <<< "$file"  # splits filename on _ for y
   if [[ "$y" != "$last" ]]      # when it has changed
   then read -u 3 dir            # reads from the dirs file
        last="$y"                # updates comparator
   fi
   mv "$file" "$dir/"            # does the move
done 3< dirs 4< files            # this assigns the streams to fd's

标准输入的默认 fd(文件描述符)为 0。我们指定一个新的 fd 为 3,专门作为 dirs 的文件,另一个作为 4 的文件名文件。这是在循环范围内完成的

done 3< dirs 4< files

您可以更改文件名。我使用dirsandfiles进行测试。

read -u 3 dir从 fd #3 显式读取,因此它不消耗任何其他内容,例如文件名。
read -u 4 file从 fd #4 显式读取,因此它不消耗任何其他内容,例如目录。
这使它们同时具有可读性。

为了您的方便,我的手册页:

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t  timeout]  [-u  fd]  [name
   ...]
  One line is read from the standard input, or from the
  file descriptor fd supplied as an argument to the -u 
  option, and the first word is assigned to the first name,
  the second word to the second name, and so on, with 
  leftover words and their intervening separators assigned
  to the last name.  If there are fewer words read from the 
  input stream than names, the remaining names are assigned
  empty values. The characters in IFS are used to split
  the line into words. The backslash character (\) may be
  used to remove any special meaning for the next character
  read and for line continuation.

Options, if supplied, have the following meanings:
  -u fd      Read input from file descriptor fd.

并且为了完整性...

  -a aname   The  words are assigned to sequential indices
             of the array variable aname, starting at 0. 
             aname is unset before any new values are
             assigned.  Other name arguments are ignored.
  -d delim   The first character of delim is used to 
             terminate the input line, rather than newline.
  -e         If the standard input is coming from a
             terminal, readline (see READLINE above) is used
             to obtain the  line. Readline uses the current
             (or default, if line editing was not previously
             active) editing settings.
  -i text    If readline is being used to read the line,
             text is placed into the editing buffer before
             editing begins.
  -n nchars  read returns  after reading nchars characters
             rather than waiting for a complete line of 
             input, but honor a delimiter if fewer than 
             nchars characters are read before the
             delimiter.
  -N nchars  read returns after reading exactly nchars
             characters rather than waiting for a complete
             line of input, unless EOF is encountered or
             read times out. Delimiter characters
             encountered in the input are not treated
             specially and do not cause read to return until
             nchars characters are read.
  -p prompt  Display prompt on standard error, without a
             trailing newline, before attempting to read any
             input. The prompt is displayed only if input is
             coming from a terminal.
  -r         Backslash does not act as an escape character. 
             The backslash is considered to be  part  of the                     
             line. In particular, a backslash-newline pair 
             may not be used as a line continuation.
  -s         Silent mode.  If input is coming from a
             terminal, characters are not echoed.
  -t timeout Cause read to time out and return failure if a
             complete line of input is not read within
             timeout seconds. timeout may be a decimal
             number with a fractional portion following the
             decimal point. This  option  is only effective
             if read is reading input from a terminal, pipe,
             or other special file; it has no effect when
             reading from regular files. If timeout is 0,
             read  returns  success if input is available on
             the specified file descriptor, failure
             otherwise. The exit status is greater than 128
             if the timeout is exceeded.

推荐阅读