是否可以将文件分组移动到需要每四个文件更改一次的目录?

时间:2019-02-25 19:28:10

标签: bash for-loop

我很欣赏标题可能不清楚,但是找不到更好的写法。 本质上,我想知道是否有可能告诉外壳程序

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

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

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

a)移动的文件数,

b)已经包含所有所需文件的目录数,并且

c)下一个要写入的目录(我假设这将涉及目录的一些由外壳分配的数字顺序)。

此外,由于并非所有目录在结束时都会有四个文件与之相关,这使情况变得复杂,因此我需要通过某种方式告诉Shell这些目录已完成(即,它们包含所有与他们相关联。)

谢谢您的时间。

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

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

y =一个字符串,用于标识哪个文件属于哪个组

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

某些组包含的文件较少,但是从来没有一个组包含四个以上的文件。

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

编辑2:因此,从广义上讲,我已经确定了我需要做的事情-如果可以将所有zip文件的名称都放在.txt文件中,而将目标目录的所有名称都放在另一个目录中,则可以告诉外壳程序从directory.txt中读取并将其用作mv命令的目标。我唯一的问题是当'y'更改时如何告诉shell转到下一个目录,但是我想我已经解决了。现在,我需要执行的命令是将file.txt中的第一个文件转换为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系统绝对无关,但是值得庆幸的是,我以目录的顺序创建了文件在directory.txt文件中列出。

2 个答案:

答案 0 :(得分:1)

复制文件描述符以进行救援。
怎么样?

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

0中的stdin的默认fd(文件描述符)。 我们将为fir指定一个新的fd 3作为文件名,为文件名指定另一个fd 4。对于

行,在循环范围内完成此操作
done 3< dirs 4< files

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

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.

答案 1 :(得分:0)

我认为,仅基于我对您的问题的幼稚阅读,您可能会对此稍加思索。如果y是标识组的字符串,那么您只需要根据每个文件中的y将每个文件移动到文件夹y中即可。

类似:

#!/bin/bash

for filename in ./x*; do
    middle=$( echo "$filename" | cut -d\_ -f2 |tr -d "\n" | od -An -t uC)
    mkdir -p $middle;
    mv $filename $middle;
done

这会将每个文件移动到基​​于该文件“ y”的文件夹“ y”中。 如果该目录不存在,它将创建它。应该在与文件相同的目录中运行。

如果我误解了,而您确实确实需要检查进入每个目录的数量,那么只需在读取“ z”值的情况下将其添加到现有目录中的另一个循环就应该很容易了。