如何将包含目录作为前缀添加到复制的文件名中?
How could I add the containing directory as a prefix to a copied file name?
问题: 我有一堆文件分布在多个目录中,它们都具有相同的名称 (input.txt)。
我的目的:我想先把这些都复制到一个新的目录下,同时把包含的目录加上后缀,避免混淆,防止覆盖。这是我正在尝试做的事情的基础:
cp -nr /foo/bar/*/input.txt /new/path/
我从这里去哪里?
为了回应下面的评论,如果我在 /old/directory 中的文件结构包含文件夹:
/old/directory/1/input.txt
/old/directory/2/input.txt
/old/directory/3/input.txt
这是我想要的输出示例:
/new/directory/ 应包含:
1input.txt
2input.txt
3input.txt
谢谢
好吧,坏消息是没有一种明显的方式可以在一行中执行此操作 - 无论如何也不是毫无意义地难以理解的方式。可能有一种方法可以用 rsync 做到这一点,我敢肯定有人比我更聪明,可以用 awk 做到这一点,但在我看来,你最好制作一个脚本,甚至编写一个自定义二进制文件来执行此操作给你。
find . -name input.txt | while read line
do
cp "$line" /new/path/`echo $line | cut -d '/' -f3- | sed 's/\//_/'`
done
请注意,您可能需要更改 cut 命令的 -f3- 部分,以便 select 您希望以哪个目录名称开始您的后缀。
一种方法是使用数组来保存文件,而且由于文件名不允许 /
,另一种方法是将其更改为其他名称,例如下划线。
#!/usr/bin/env bash
##: Just in case there are no files/directories the * glob will not expand by itself.
shopt -s nullglob
files=(foo/bar/*/input.txt)
for file in "${files[@]}"; do
new_file=${file//\//_} ##: Replace all /'s with an _ by Parameter Expansion
echo cp -v "$file" new/path/"$new_file"
done
根据 OP 的要求,这里是新的答案。
#!/usr/bin/env bash
shopt -s nullglob
##: Although files=(/old/directory/{1..3}/input.txt)
##: can be a replacement, no need for nullglob
files=(/old/directory/*/input.txt)
for file in "${files[@]}"; do
tmp=${file%[0-9]*}
new_file=${file#*$tmp}
echo cp -v "$file" new/path/"${new_file//\//}"
done
另一种选择是使用 /
作为分隔符来拆分字段。
#!/usr/bin/env bash
##: Do not expand a literal glob * if there are no files/directories
shopt -s nullglob
##: Save the whole path and files in an array.
files=(/old/directory/*/input.txt)
for file in "${files[@]}"; do
IFS='/' read -ra path <<< "$file" ##: split via / and save in an array
tmp=${path[@]:(-2)} ##: Remain only the last 2, e.g. 1 input.txt
new_file=${tmp// } ##: Remove the space, becomes 1input.txt
echo cp -v "$file" new/path/"$new_file"
done
如果您认为输出正确,请删除 echo
。
很容易理解文件来自哪个目录,只需将下划线替换为 /
这可以解决问题,还可以处理名称中可能包含空格(或任何其他奇怪字符)的任何目录。
#!/bin/bash
tld=./old/directory
newpath=./new/directory
while IFS= read -r -d $'[=10=]' file; do
tmp="${file#*${tld}/}"
echo cp "$file" "$newpath/${tmp//\//}"
done < <(find "$tld" -type f -name "input.txt" -print0)
概念验证
$ tree ./old/directory/
./old/directory/
├── 1
│ └── input.txt
├── 2
│ └── input.txt
└── 3
├── 3a
│ └── input.txt
└── input.txt
4 directories, 4 files
$ ./mvinput.sh
cp ./old/directory/3/input.txt ./new/directory/3input.txt
cp ./old/directory/3/3a/input.txt ./new/directory/33ainput.txt
cp ./old/directory/1/input.txt ./new/directory/1input.txt
cp ./old/directory/2/input.txt ./new/directory/2input.txt
问题: 我有一堆文件分布在多个目录中,它们都具有相同的名称 (input.txt)。
我的目的:我想先把这些都复制到一个新的目录下,同时把包含的目录加上后缀,避免混淆,防止覆盖。这是我正在尝试做的事情的基础:
cp -nr /foo/bar/*/input.txt /new/path/
我从这里去哪里?
为了回应下面的评论,如果我在 /old/directory 中的文件结构包含文件夹:
/old/directory/1/input.txt
/old/directory/2/input.txt
/old/directory/3/input.txt
这是我想要的输出示例: /new/directory/ 应包含:
1input.txt
2input.txt
3input.txt
谢谢
好吧,坏消息是没有一种明显的方式可以在一行中执行此操作 - 无论如何也不是毫无意义地难以理解的方式。可能有一种方法可以用 rsync 做到这一点,我敢肯定有人比我更聪明,可以用 awk 做到这一点,但在我看来,你最好制作一个脚本,甚至编写一个自定义二进制文件来执行此操作给你。
find . -name input.txt | while read line
do
cp "$line" /new/path/`echo $line | cut -d '/' -f3- | sed 's/\//_/'`
done
请注意,您可能需要更改 cut 命令的 -f3- 部分,以便 select 您希望以哪个目录名称开始您的后缀。
一种方法是使用数组来保存文件,而且由于文件名不允许 /
,另一种方法是将其更改为其他名称,例如下划线。
#!/usr/bin/env bash
##: Just in case there are no files/directories the * glob will not expand by itself.
shopt -s nullglob
files=(foo/bar/*/input.txt)
for file in "${files[@]}"; do
new_file=${file//\//_} ##: Replace all /'s with an _ by Parameter Expansion
echo cp -v "$file" new/path/"$new_file"
done
根据 OP 的要求,这里是新的答案。
#!/usr/bin/env bash
shopt -s nullglob
##: Although files=(/old/directory/{1..3}/input.txt)
##: can be a replacement, no need for nullglob
files=(/old/directory/*/input.txt)
for file in "${files[@]}"; do
tmp=${file%[0-9]*}
new_file=${file#*$tmp}
echo cp -v "$file" new/path/"${new_file//\//}"
done
另一种选择是使用 /
作为分隔符来拆分字段。
#!/usr/bin/env bash
##: Do not expand a literal glob * if there are no files/directories
shopt -s nullglob
##: Save the whole path and files in an array.
files=(/old/directory/*/input.txt)
for file in "${files[@]}"; do
IFS='/' read -ra path <<< "$file" ##: split via / and save in an array
tmp=${path[@]:(-2)} ##: Remain only the last 2, e.g. 1 input.txt
new_file=${tmp// } ##: Remove the space, becomes 1input.txt
echo cp -v "$file" new/path/"$new_file"
done
如果您认为输出正确,请删除
echo
。很容易理解文件来自哪个目录,只需将下划线替换为
/
这可以解决问题,还可以处理名称中可能包含空格(或任何其他奇怪字符)的任何目录。
#!/bin/bash
tld=./old/directory
newpath=./new/directory
while IFS= read -r -d $'[=10=]' file; do
tmp="${file#*${tld}/}"
echo cp "$file" "$newpath/${tmp//\//}"
done < <(find "$tld" -type f -name "input.txt" -print0)
概念验证
$ tree ./old/directory/
./old/directory/
├── 1
│ └── input.txt
├── 2
│ └── input.txt
└── 3
├── 3a
│ └── input.txt
└── input.txt
4 directories, 4 files
$ ./mvinput.sh
cp ./old/directory/3/input.txt ./new/directory/3input.txt
cp ./old/directory/3/3a/input.txt ./new/directory/33ainput.txt
cp ./old/directory/1/input.txt ./new/directory/1input.txt
cp ./old/directory/2/input.txt ./new/directory/2input.txt