根据文件名将文件复制到目录

根据文件名将文件复制到目录

我有一个包含数千个文件的 webdav 服务器:

└── Sync
     ├── 20180719_120823.jpg
     ├── 20180719_120933.jpg
     ├── 20190719_120955.jpg
     ├── 20190719_121023.jpg
     ├── 20190719_121032.jpg
     ├── 20190720_121037.jpg
     ├── 20190721_120823.mp4
     ├── 20190822_220013.jpg
     └── 20190822_230155.mp4
                 "
                 "

文件名的第一部分是 YYYYMMDD。我想用它将文件复制到另一个驱动器,结构如下。如果 MM 被翻译成月份名称,那就更好了……:

├── 2018
│    └── Jul
│         ├── 20180719_120823.jpg
│         └── 20180719_120933.jpg
└── 2019 
     ├── Jul
     │    ├── 20190719_120955.jpg
     │    ├── 20190719_121023.jpg
     │    ├── 20190719_121032.jpg
     │    ├── 20190720_121037.jpg
     │    └── 20190721_120823.mp4
     └── Aug
          ├── 20190822_220013.jpg
          └── 20190822_230155.mp4

我可以手动创建年/月结构。但如果目录不存在,则创建目录会更好。该脚本每天运行一次,如果文件已经存在,我们将跳过它。

答案1

以下是一种方法:

declare -A months=( 
 [01]="Jan" 
 [02]="Feb" 
 [03]="Mar" 
 [04]="Apr" 
 [05]="May" 
 [06]="Jun" 
 [07]="Jul" 
 [08]="Aug" 
 [09]="Sep" 
 [10]="Oct" 
 [11]="Nov" 
 [12]="Dec"
) 

for file in Sync/*; do
    year=$(cut -c 1-4 <<<"${file##*/}") 
    month=$(cut -c 5-6 <<<"${file##*/}")
    mkdir -p "$year/${months[$month]}"
    cp -n -- "$file" "$year/${months[$month]}"
 done

首先,我们创建一个关联数组,将数字与月份名称关联起来。然后,对于目录中的每个文件Sync,我们使用cut来提取年份和月份(参见https://tldp.org/LDP/abs/html/string-manipulation.html了解格式${file##*/})。然后,mkdir -p如果目录不存在,则创建目录,然后移动文件。该-n选项可cp确保现有文件不会被覆盖(感谢添加该选项的@Raffa!)。

答案2

在 Bash 中可以做这样的事情:

你有:

$ tree Sync/
Sync/
├── 20180719_120823.jpg
├── 20190719_120823.jpg
├── 20190719_120920.jpg
├── 20190720_121037.jpg
├── 20190822_120823.jpg
└── 20190822_120823.mp4

1 directory, 6 files

然后,你这样做:

$ for f in Sync/*
  do
  if [ -f "$f" ]
    then
    n="${f/*\//}" # Get filename
    y="${n:0:4}" # Get Year part
    m="${n:4:2}" # Get Month part
    mn="$(date -d "$m/01/2000" "+%b")" # Translate Month number to name
    mkdir -p Dest/"${y}/${mn}" # Create destination directories if they don't exist
    cp -nv -- "$f" Dest/"${y}/${mn}/"
    fi
  done
'Sync/20180719_120823.jpg' -> 'Dest/2018/Jul/20180719_120823.jpg'
'Sync/20190719_120823.jpg' -> 'Dest/2019/Jul/20190719_120823.jpg'
'Sync/20190719_120920.jpg' -> 'Dest/2019/Jul/20190719_120920.jpg'
'Sync/20190720_121037.jpg' -> 'Dest/2019/Jul/20190720_121037.jpg'
'Sync/20190822_120823.jpg' -> 'Dest/2019/Aug/20190822_120823.jpg'
'Sync/20190822_120823.mp4' -> 'Dest/2019/Aug/20190822_120823.mp4'

要得到:

$ tree Dest/
Dest/
├── 2018
│   └── Jul
│       └── 20180719_120823.jpg
└── 2019
    ├── Aug
    │   ├── 20190822_120823.jpg
    │   └── 20190822_120823.mp4
    └── Jul
        ├── 20190719_120823.jpg
        ├── 20190719_120920.jpg
        └── 20190720_121037.jpg

6 directories, 6 files

注意上面用于将月份数字转换为月份名称的命令date将根据默认语言环境进行操作(即语言),但您可以将其更改为其他语言/区域设置(需要已安装在你的系统上LC_ALL)通过在同一行上并在命令之前直接临时设置环境变量date(例如将月份数字转换为瑞典月份名称),您可以将上述相关行从:

mn="$(date -d "$m/01/2000" "+%b")" # Translate Month number to name

到:

mn="$(LC_ALL=sv_SE.utf8 date -d "$m/01/2000" "+%b")" # Translate Month number to Swedish name

.. 例如,它将把月份数字翻译10成瑞典语简称okt。如果您想要全名,您可以更改"+%b""+%B",这将分别翻译为oktober

答案3

谢谢您的意见...这很有趣!
我最终将上述两个建议结合起来。

#! /bin/bash

# Declare monts in Swedish
declare -A months=(
 [01]="Januari"
 [02]="Februari"
 [03]="Mars"
 [04]="April"
 [05]="Maj"
 [06]="Juni"
 [07]="Juli"
 [08]="Augusti"
 [09]="September"
 [10]="Oktober"
 [11]="November"
 [12]="December"
)

for f in /mnt/4TB/apa/*
do
  if [ -f "$f" ]; then
   filename="${f##*/}" # Get the base filename

    # pick the first 9 characters
    first_nine="${filename:0:9}"

    # Use regular expression to check if the first nine characters are 8 digits followed by an underscore
    if [[ "$first_nine" =~ ^[0-9]{8}_ ]]; then
           n="${f/*\//}" # Get filename
           y="${n:0:4}" # Get Year part
           m="${n:4:2}" # Get Month part
           mn="${months[$m]}" # Get Month in Swedish
           mkdir -p /mnt/4TB/bepa/"${y}/${mn}" # Create destination directories if they don't exist
           cp -np -- "$f" /mnt/4TB/bepa/"${y}/${mn}/" # Copy file
    fi
  fi
done

相关内容