我有许多txt
文件,分散在不同的文件夹中。
- case1
|
- 0.25
|
- case1.txt
- 0.35
|
_ case1.txt
- 0.30
|
_ case1.txt
- 0.45
|
_ case1.txt
- case2
|
- 0.25
|
- case2.txt
- 0.35
|
_ case2.txt
- 0.30
|
_ case2.txt
- 0.45
|
_ case2.txt
.
.
.
我想将它们全部复制到一个文件夹中,但不幸的是,正如您所见,其中一些文件具有相同的名称,因此一个简单的find
解决方案最终会覆盖它们。我想将所有文件复制txt
到一个目录foo
,并在扩展名之前插入它们所在子文件夹的名称.txt
。此外,由于子文件夹名称中有一个点,并且我需要将这些文件复制到 Windows,所以我还想更改0.25
为0_25
。换句话说,文件
- case2
|
- 0.25
|
- case2.txt
必须复制到foo
as case2_0_25.txt
。如果 bash 解决方案太复杂/难以理解,Python 解决方案也可以,但是不是一个 zsh 。
答案1
将副本分为两个转换,第一步将点和斜线转换为下划线,下一步将匹配的子表达式重新排列为所需的顺序。
shopt -s globstar; \
tar -cvf - --show-transformed-names --transform='s![.]!_!g' --transform='s!.*/\([^/]\+\)/\([^/]\+\)_txt$!foo/\2_\1.txt!' case*/**/*.txt | tar -xf -
现在,匹配至关重要--transform
,否则这些文件将无法得到处理。
Bash 解决方案的工作方式大致相同。
#!/bin/bash
shopt -s nullglob globstar
mkdir -p foo
while read -rd ''; do
[[ ${REPLY//./_} =~ ([^/]+)/([^/]+)_txt$ ]] &&
cp -va "$REPLY" "foo/${BASH_REMATCH[2]}_${BASH_REMATCH[1]}.txt"
done < <(printf %s\\0 case*/**/*.txt)
答案2
您可以使用 bash 的 globstar 选项轻松地完成此操作(来自man bash
):
全球星
如果设置,路径名扩展上下文中使用的模式
**
将匹配所有文件和零个或多个目录和子目录。如果模式后跟/
,则仅匹配目录和子目录。
因为我们可以用来**
查找文件,所以我们只需要将新名称定义为包含原始目录名称并更改.
为_
:
for file in **/*.txt; do
newName=$(sed 's|[/.]|_|g' <<<"$file" | sed 's/_txt$/.txt/')
cp -- "$file" foo/"$newName"
done
解释
for file in **/*.txt; do
:查找当前目录中名称以 结尾的所有文件(和目录,如果相关)*.txt
。newName=$(sed 's|[/.]|_|g' <<<"$file" | sed 's/_txt$/.txt/')
:使用将所有和sed
转换为文件名中的 。请注意,这里还包括路径,因此它将类似于, 变成。然后,我们将第一个的输出传递给第二个,它将转换(如果在行尾找到)为,得到“case1_0_25_case1.txt $newName”。/
.
_
$file
case1/0.25/case1.txt
case1_0_25_case1_txt
sed
_txt
.txt
. The final output is saved in the variable
cp -- "$file" foo/"$newName"
:我们现在将文件复制到目录foo/
并使用新名称。--
这里实际上不需要,但可以确保该方法适用于任何文件名,包括名称以 开头的文件名-
。
我重新创建了您在问题中显示的文件夹结构,运行了上述命令并得到:
$ tree
.
├── case1
│ ├── 0.25
│ │ └── case1.txt
│ ├── 0.30
│ │ └── case1.txt
│ ├── 0.35
│ │ └── case1.txt
│ └── 0.45
│ └── case1.txt
├── case2
│ ├── 0.25
│ │ └── case2.txt
│ ├── 0.30
│ │ └── case2.txt
│ ├── 0.35
│ │ └── case2.txt
│ └── 0.45
│ └── case2.txt
└── foo
├── case1_0_25_case1.txt
├── case1_0_30_case1.txt
├── case1_0_35_case1.txt
├── case1_0_45_case1.txt
├── case2_0_25_case2.txt
├── case2_0_30_case2.txt
├── case2_0_35_case2.txt
└── case2_0_45_case2.txt
11 directories, 16 files