![根据文件名将文件复制到目录](https://linux22.com/image/1197804/%E6%A0%B9%E6%8D%AE%E6%96%87%E4%BB%B6%E5%90%8D%E5%B0%86%E6%96%87%E4%BB%B6%E5%A4%8D%E5%88%B6%E5%88%B0%E7%9B%AE%E5%BD%95.png)
我有一个包含数千个文件的 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