【问题标题】:BASH: File sorting according to file nameBASH:根据文件名排序文件
【发布时间】:2021-01-23 08:18:43
【问题描述】:

我需要根据名称将 12000 个填充物分类为 1000 个组,并为每个组创建一个包含该组填充物的新文件夹。每个文件的名称以多列格式给出(带 _ 分隔符),其中第二列从 1 到 12(部分编号)变化,最后一列从 1 到 1000(系统编号),表明最初 1000 个不同的系统(最后一列)被分成 12 个单独的部分(第二列)。 这是一个基于 3 个系统的小子集的示例,该系统由 12 个部分划分,总共 36 个填充。

7000_01_lig_cne_1.dlg
7000_02_lig_cne_1.dlg
7000_03_lig_cne_1.dlg
...
7000_12_lig_cne_1.dlg

7000_01_lig_cne_2.dlg
7000_02_lig_cne_2.dlg
7000_03_lig_cne_2.dlg
...
7000_12_lig_cne_2.dlg

7000_01_lig_cne_3.dlg
7000_02_lig_cne_3.dlg
7000_03_lig_cne_3.dlg
...
7000_12_lig_cne_3.dlg

我需要根据它们名称的第二列 (01, 02, 03 .. 12) 对这些填充进行分组,从而创建 1000 个文件夹,这些文件夹应按以下方式为每个系统约束 12 个填充:

 Folder1, name: 7000_lig_cne_1, it contains 12 filles:   7000_{this is from 01 to 12}_lig_cne_1.dlg

 Folder2, name: 7000_lig_cne_2, it contains 12 filles 7000_{this is from 01 to 12}_lig_cne_2.dlg
...
 Folder1000, name: 7000_lig_cne_1000, it contains 12 filles 7000_{this is from 01 to 12}_lig_cne_1000.dlg

假设所有 *.dlg 填充都存在于同一个目录中,我建议 bash 循环工作流,它只缺少一些排序功能(sed、awk ??),按以下方式组织:

#set the name of folder with all DLG
home=$PWD
FILES=${home}/all_DLG/7000_CNE
# set the name of protein and ligand library to analyse
experiment="7000_CNE"

#name of the output
output=${home}/sub_folders_to_analyse

#now here all magic comes
rm -r ${output}
mkdir ${output}

# sed sollution
for i in ${FILES}/*.dlg        # define this better to suit your needs
do 
    n=$( <<<"$i" sed 's/.*[^0-9]\([0-9]*\)\.dlg$/\1/' )
    # move the file to proper dir
    mkdir -p ${output}/"${experiment}_lig$n"
    cp "$i" ${output}/"${experiment}_lig$n"
done

!注意:我将每个文件夹的名称开头表示为 ${experiment} 我在末尾添加了最后一列 $n 的编号。是否可以每次根据复制的填充名称自动设置新文件夹的名称?手动可以通过跳过文件夹名称中的第二列来实现

 cp ./all_DLG/7000_*_lig_cne_987.dlg ./output/7000_lig_cne_987

【问题讨论】:

  • Folder1000, name: 7000_lig_cne_3 ?认为您的意思是Folder1000, name: 7000_lig_cne_1000Folder3, name: 7000_lig_cne_3some sorting expression为什么你需要排序文件?您不需要文件之间的顺序 - 只需将文件移动到正确的目录!。
  • 是的,你是对的,我更正了我的信息。是的,我只需要移动填充对不起我找不到另一个合适的表达方式.. mb 而是根据文件名重定向??

标签: bash shell sorting awk sed


【解决方案1】:

遍历文件。从文件名中提取目标目录名。移动文件。

for i in *.dlg; do
    # extract last number with your favorite tool
    n=$( <<<"$i" sed 's/.*[^0-9]\([0-9]*\)\.dlg$/\1/' )
    # move the file to proper dir
    echo mkdir -p "folder$n"
    echo mv "$i" "folder$n"
done

注意事项:

  • 不要在脚本中使用大写变量。使用小写变量。
  • 记得引用变量扩展。
  • 使用http://shellcheck.net 检查您的脚本
  • repl 上测试

更新: OP 的文件夹命名约定:

for i in *.dlg; do
    foldername="$HOME/output/${i%%_*}_${i#*_*_}"
    echo mkdir -p "$foldername"
    echo mv "$i" "$foldername"
done

【讨论】:

    【解决方案2】:

    仅使用 POSIX shell 的内置语法和sort

    #!/usr/bin/env sh
    
    curdir=
    
    # Create list of files with newline
    # Safe since we know there is no special
    # characters in name
    printf -- %s\\n *.dlg |
    
    # Sort the list by 5th key with _ as field delimiter
    sort -t_ -k5 |
    
    # Iterate reading the _ delimited fields of the sorted list
    while IFS=_ read -r _ _ c d e; do
    
      # Compose the new directory name
      newdir="${c}_${d}_${e%.dlg}"
    
      # If we enter a new group / directory
      if [ "$curdir" != "$newdir" ]; then
    
        # Make the new directory current
        curdir="$newdir"
    
        # Create the new directory
        echo mkdir -p "$curdir"
    
        # Move all its files into it
        echo mv -- *_"$curdir.dlg" "$curdir/"
      fi
    done
    

    可选作为sortxargs 参数流:

    printf -- %s\\n * |
    sort -u -t_ -k5 
    xargs -n1 sh -c 
    'd="lig_cne_${0##*_}"
    d="${d%.dlg}"
    echo mkdir -p "$d"
    echo mv -- *"_$d.dlg" "$d/"
    '
    

    【讨论】:

      【解决方案3】:

      这是一个非常简单的awk 脚本,可以在单次扫描中完成。

      script.awk

      BEGIN{FS="[_.]"} # make field separator "_" or "."
      { # for each filename
        dirName=$1"_"$3"_"$4"_"$5; # compute the target dir name from fields
        sysCmd = "mkdir -p " dirName"; cp "$0 " "dirName; # prepare bash command
        system(sysCmd); # run bash command
      }
      

      正在运行script.awk

      ls -1 *.dlg | awk -f script.awk
      

      oneliner awk 脚本

      ls -1 *.dlg | awk 'BEGIN{FS="[_.]"}{d=$1"_"$3"_"$4"_"$5;system("mkdir -p "d"; cp "$0 " "d);}'
      

      【讨论】:

      • 抱歉,我无法理解您的 AWK 脚本将所有文件夹复制到哪里。例如。在我的循环示例中,我定义为 $OUTPUT
      【解决方案4】:

      这可能对你有用(GNU 并行):

      ls *.dlg | 
      parallel --dry-run 'd={=s/^(7000_).*(lig.*)\.dlg/$1$2/=};mkdir -p $d;mv {} $d'
      

      ls 命令列表以.dlg 结尾的文件的输出通过管道传输到并行,这会创建目录并将文件移动到其中。

      按原样运行解决方案,如果满意,试运行的输出没问题,删除选项--dry-run

      解决方案可能是一条指令:

      parallel 'd={=s/^(7000_).*(lig.*)\.dlg/$1$2/=};mkdir -p $d;mv {} $d' ::: *.dlg
      

      【讨论】:

      • 它看起来很有趣,但我更感兴趣的是能够将文件复制到目录并根据文件名设置目录名称的更自动的解决方案,我刚刚编辑了我的拳头问题!
      • @JamesStarlight 如果你想复制而不是移动交换cpmv,上面的解决方案使用文件根据要求创建一个目录(参见$d 变量)。
      • 可能更喜欢printf '%s\n' 而不是ls
      • @tripleee 查看第二个解决方案
      猜你喜欢
      • 2016-04-08
      • 2014-06-09
      • 1970-01-01
      • 2020-04-01
      • 2013-07-01
      • 1970-01-01
      • 2012-10-19
      • 2016-02-27
      • 1970-01-01
      相关资源
      最近更新 更多