【问题标题】:How to move files from X sub_dir to Y sub_dir如何将文件从 X sub_dir 移动到 Y sub_dir
【发布时间】:2016-03-03 07:14:07
【问题描述】:

我有主目录 X,在里面我有很多子目录,比如 A、B、C、D。我有主目录 Y,在其中我有许多与 X 主目录子目录同名的子目录,如 A、B、C、D。

现在我只需要将文件从 X 主目录子目录移动到 Y 主目录子目录。

例如:

在主目录X里面,子目录A有100个文件

B 有 1 个文件

C 有 5 个文件

而 D 有 50 个文件...

我的光标应该在 main DIR X ,从那里我需要将所有子目录文件移动到 Y 主目录内的同名子目录(A、B、C、D)。

如何在 SHELL SCRIPTING(KSH) 或 unix 中执行此操作??

【问题讨论】:

    标签: linux shell unix ksh


    【解决方案1】:

    注意:从根本上修改以提出更简单的解决方案。底部的单线(更复杂)。

    符合 POSIX 的解决方案

    #!/bin/sh
    
    # Specify source and target directories (example values)
    dirFrom='/tmp/from'
    dirTo='/tmp/to'
    
    # Use globbing to find all subdirectories - note the trailing '/'
    # to ensure that only directories match.
    for subdir in "$dirFrom"/*/; do
    
      # Extract the mere name.
      # Note that using ${subdir##*/} is NOT an option here, because $subdir ends in '/'.
      name=$(basename -- "$subdir")
    
      # Make sure a subdirectory of the same
      # name exists in the target dir.
      mkdir -p "$dirTo/$name"
    
      # Move all items in the source subdir. to the analogous target subdir.
      mv "$subdir"* "$dirTo/$name/"
    
    done  
    

    因此,上述内容也适用于ksh,以及其他与POSIX 兼容的shell,例如bashzsh

    此解决方案存在两个潜在问题

    • 隐藏的项目(名称以. 开头的项目)被忽略
    • 如果没有子目录,脚本将会中断。或者如果它们中的任何一个是 empty,因为不匹配任何内容的通配模式(例如 *)会保留原样,从而导致传递不存在的路径到mv

    更强大的 Bash 版本

    Bash - 与 ksh 不同,不幸的是 - 具有解决这两个问题的 选项

    • shopt -s dotglob 导致在执行 globbing 时包含隐藏项。
    • shopt -s nullglob 导致不匹配任何内容的模式扩展为 empty 字符串。
    #!/usr/bin/env bash
    
    # Specify source and target directories (example values)
    dirFrom='/tmp/from'
    dirTo='/tmp/to'
    
    shopt -s dotglob   # include hidden items when matching `*`
    shopt -s nullglob  # expand patterns that don't match anything to the emtpy string
    
    # Use globbing to find all subdirectories - note the trailing '/'
    # to ensure that only directories match.
    for subdir in "$dirFrom"/*/; do
    
      # Extract the mere name.
      # Note that using ${subdir##*/} is NOT an option here, because $subdir ends in '/'.
      name=$(basename -- "$subdir")
    
      # Make sure a subdirectory of the same
      # name exists in the target dir.
      mkdir -p "$dirTo/$name"
    
      # Collect the paths of the items in the subdir. in 
      # an array, so we can test up front whether anything matched.
      itms=( "$subdir"* )
    
      # Move all items in the source subdir. to the analogous target subdir,
      # but only if the subdir. contains at least 1 item.
      [[ ${#itms[@]} -gt 0 ]] && mv "${itms[@]}" "$dirTo/$name/"
    
    done
    
    • 注意shopt -s nullglob 本身是不够的 - 我们仍然必须首先在一个数组中收集通配匹配项,这样我们才能确定是否有任何匹配项。
      • 虽然我们可以改用 2>/dev/nullmv 在没有匹配项时静默失败,但这是不可取的,因为它可能掩盖真正的错误情况。

    如果您对 one-liners 感兴趣,这里有基于 find 的命令;然而,它们相当复杂。

    # Note: Both solutions below ignore symlinks to directories as subdirectories.
    
    # [POSIX-compliant] Excluding hidden items.
    # Note how `\! -name '.*'` is explicitly added to the `find` command to ensure that no hidden subdirs. are matched, so as to
    # to match the shell globbing behavior of excluding hidden items.
    find "$dirFrom" -mindepth 1 -maxdepth 1 -type d \! -name '.*' -exec sh -c \
      'f=$1 t=$2/${1##*/}; set -- "$f"/*; [ -e "$1" ] && mkdir -p "$t" && mv "$@" "$t"' \
      - {} "$dirTo" \;
    
    # [Bash] Including hidden items.
    find "$dirFrom" -mindepth 1 -maxdepth 1 -type d -exec bash -O dotglob -c \
      'f=$1 t=$2/${1##*/}; set -- "$f"/*; [[ -e $1 ]] && mkdir -p "$t" && mv "$@" "$t"' \
      - {} "$dirTo" \;
    

    【讨论】:

      【解决方案2】:

      这是一个应该可以工作的 shell 脚本。不过,我还没有时间检查它。请检查并确认。

      #!/bin/bash
      
      ##################################################################
      #
      #   Please make sure this script is run from source     
      #   directory. Also you need to have read, write and    
      #   execute permission for this directory.
      #
      #   If the subdirectories with same names are not present   
      #   then those will be created directly.
      ##################################################################
      
      # Replace name of source directory by "X"
      sourcedir="X"
      
      # Replace name of destination directory with complete path by "Y"
      destdir="/home/user/Y"
      
      # Copying names of all subdirectories in a temporary text file
      ls -Rl | grep "[a-z]:" | sed 's/://' | cut -c 2- > temp.txt
      
      # Moving files directory by directory to destination
      while read line; do
          mv $sourcedir$line/* $destdir$line/
          echo "All files from directory $sourcedir$line moved to $destdir$line"
      done < temp.txt
      
      rm temp.txt
      

      【讨论】:

      • 您应该双引号引用变量并使用while IFS= read line 以确保正确处理带有嵌入空格和其他shell 元字符的文件名。除了查找子目录名称的低效和迂回方式(改用find)之外,您不仅可以找到 child 目录,还可以找到整个 all 目录i>subtree,它会在移动时失败,因为当你到达一个 sub-subdir. 时,它已经被移动了。这在 OP 的情况下可能不是问题(听起来好像没有子目录),但值得指出。
      • 感谢您的宝贵意见。我是脚本的初学者。只是试着用我的方式回答。当你说双引号变量 ref。你的意思是当我在“mv”命令中使用变量时使用双引号?是的,我正在考虑除了子目录之外不会有子目录。
      • Re only subdirectories: glob */(注意尾部斜杠)将返回所有子目录(尽管默认情况下不包括隐藏目录)。至于双引号:是的,如果你想引用一个变量按原样,不由 shell 解释,你必须双引号它:"$var"。如果没有双引号,$var 会同时受到分词和路径名扩展(通配符)的影响。请尝试以下操作:var='spaced out'; set -- $var; echo "Count of params: $#"var='A *'; echo $var
      【解决方案3】:

      您可以使用如下脚本:

      #!/bin/bash
      dirX=X
      dirY=Y
      for subdir in `ls $dirX`; do
              mv $dirX$subdir/* $dirY$subdir
      done
      

      用你的目录替换 X, Y

      【讨论】:

      • do not use for to parse command outputdo not parse ls output(顺便说一句:ls $dirX 也会匹配 files),并双引号引用您的变量引用。
      • 除了ls会匹配文件,我觉得脚本没什么问题。
      • 运行这个(在bash中交互):mkdir -p /tmp/from/{'a b','c d'} /tmp/to; touch /tmp/from/{'a b','c d'}/{'f 1','f 2'};然后,在您的脚本中,设置dirX=/tmp/fromdirY=/tmp/to。现在运行你的脚本,看看会发生什么。
      • 感谢您的指点,我看到了问题。
      猜你喜欢
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      • 2021-10-11
      • 2016-09-25
      • 2015-07-18
      相关资源
      最近更新 更多