【问题标题】:Shell script to copy and prepend folder name to files from multiple subdirectories用于将文件夹名称复制并添加到多个子目录中的文件的 Shell 脚本
【发布时间】:2015-07-07 09:18:07
【问题描述】:

我有几个共享文件名的不同图像的文件夹,文件夹结构如下:

/parent/folder001/img001.jpg
/parent/folder001/img002.jpg
/parent/folder002/img001.jpg
/parent/folder002/img002.jpg
/parent/folder003/img001.jpg
/parent/folder003/img002.jpg
...

并希望将这些文件复制/重命名到一个新文件夹中,如下所示:

/newfolder/folder001_img001.jpg
/newfolder/folder001_img002.jpg
/newfolder/folder002_img001.jpg
/newfolder/folder002_img002.jpg
/newfolder/folder003_img001.jpg
/newfolder/folder003_img002.jpg
...

(如果 newfolder 不是父文件夹的子文件夹可能会更好,因为这可能最终导致非常奇怪的递归。)

所有包含图像的文件夹都没有任何子文件夹。

理想情况下,我希望能够重复使用该脚本来“更新”新文件夹,因为我可能需要稍后添加更多包含图像的文件夹。

如何使用 shell 脚本完成此操作?

【问题讨论】:

  • 好吧,手动重命名它们并不是一个真正的选择,有数百个。恐怕我自己还不太擅长 shell,但是寻找解决方案只会完全针对不同的问题/目录结构找到结果。
  • 是否将源文件夹(在本例中为“/parent/”)和目标文件夹(在本例中为“/newfolder”)作为输入,然后抓取源文件夹中的所有文件夹进行合并进入目标文件夹?
  • 在相应地重命名源文件夹中文件夹中的文件时,是的。那将非常有帮助!虽然我想如果脚本从父文件夹内部运行并将图像文件夹合并到与父文件夹位于同一目录中的文件夹中可能就足够了?
  • 我明白了。所以没有输入脚本。源目录和目标目录是当前目录。你可以重新运行它来更新。此外,更新是否会导致删除不再位于子目录中的文件?
  • 啊,不,这不是真的必要。只需对图像文件夹中当前存在的文件进行复制重命名就足够了。 (因为它所要做的就是在执行脚本和其他内容之前删除目标文件夹中的所有文件。)

标签: linux shell unix rename batch-rename


【解决方案1】:

这有点乏味,但可以:

#!/bin/bash
parent=/parent
newfolder=/newfolder
mkdir "$newfolder"
for folder in "$parent"/*; do
  if [[ -d "$folder" ]]; then
    foldername="${folder##*/}"
    for file in "$parent"/"$foldername"/*; do
      filename="${file##*/}"
      newfilename="$foldername"_"$filename"
      cp "$file" "$newfolder"/"$newfilename"
    done
  fi
done

parent 路径放到 parent 变量中,将 newfolder 路径放到 newfolder 变量中。

【讨论】:

  • 不要将全局结果存储在标量变量中,这会消除空格安全性。直接使用它们 - for folder in /parent/*
  • 感谢您的回答!不幸的是,虽然它可以很好地创建新文件夹,但它还没有复制文件。输出给出如下内容:cp: omitting directory ‘/parent/folder001’ for each folder in parent.
  • 感谢您的提示,@Kevin,@ Nekkowe,使用此编辑后的代码重试。
  • @Jahid 我对这个有点困惑。我用父文件夹的路径替换哪些位?当我尝试用正确的路径替换 /parent/ 的每个实例时,$ ./photorename-2.sh bash: ./photorename-2.sh: /bin/bash^M: bad interpreter: No such file or directory 发生了。
  • 只有/parent在2个地方
【解决方案2】:

我会使用这样的东西:

find -type f -exec sh -c 'f={}; fnew=$(rev <<< "$f" | sed 's~/~_~' | rev); echo "mv $f $fnew"' \;

这会在当前目录结构中查找文件并执行以下操作:

  • 获取文件名
  • 将最后一个/ 替换为_
  • echo "cp $old_file $new_file"

运行此命令并看到它打印出正确的命令后,删除 echo 以便它有效地执行 mv 命令。

我知道fnew=$(rev &lt;&lt;&lt; "$f" | sed 's~/~_~' | rev) 有点丑陋,但我找不到更好的方法来用_ 替换最后一个/。也许sed -r 's~/([^/]*)$~_\1~' 也可以,但我总是喜欢使用rev :)


由于您的 find-sh c 表达式中表现不佳,因此我们使用 while 循环:

while read -r file
do
    new_file=$(rev <<< "$file" | sed 's~/~_~' | rev)
    echo "mv $file $new_file"; done < <(find . -type f)
done < <(find . -type f)

作为单行:

while read -r file; do new_file=$(rev <<< "$file" | sed 's~/~_~' | rev); echo "mv $file $new_file"; done < <(find . -type f)

【讨论】:

  • 哦,呃,到目前为止它似乎给出的唯一输出是:sh: 1: Syntax error: redirection unexpected ...重复了很多行(它确实在某些时候终止)。我尝试在父文件夹和子文件夹中执行它,结果相同。
  • 你用的是什么机器?这适用于find GNU 4.5.11。这对你有用吗? --> find -type f -exec sh -c 'f={}; echo "$f"' \;.
  • 那个输出很多行,每个行都有一个'$'。
  • 如果脚本应该输出相应的文件名,则它没有按预期工作。大概是我使用错了(我尝试从终端执行它,在每个图像和父文件夹中),或者缺少其他东西?
  • f=$0 将 shell 的第零个位置参数分配给 f,并将 {} 作为参数传递给 sh 使文件名成为第零个参数。 find 的某些实现需要 {} 作为独立参数出现,这是实现这一点的一种技术。
【解决方案3】:

救援功能,更新 Jahid 的脚本:

function updateFolder
{
    mkdir "$2"
    for folder in "$1"/*; do
    if [[ -d $folder ]]; then
        foldername="${folder##*/}"
        for file in "$1"/"$foldername"/*; do
            filename="${file##*/}"
            newfilename="$foldername"_"$filename"
            cp "$file" "$2"/"$newfilename"
        done
    fi
    done
}

使用:

$ updateFolder /parent /newfolder

【讨论】:

    【解决方案4】:
    cd parent  
    for i in \*/*;do mv ${i} newfolder/\`dirname ${i}\`_\`basename ${i}\`;done  
    mv newfolder \<whereeveryouwant>
    

    【讨论】:

    • 为什么你更喜欢这个而不是现有的答案?尤其是在社区验证的公认答案上?您能否解释一下您选择不同方法的原因以及在哪里可能更合适?
    • 你是在什么环境下写这个的?你为什么要逃避第一个 * 和所有的反引号?以及为什么要使用反引号来生成子shell(使用$(...) 语法几乎总是比反引号更受欢迎以避免混淆)。
    • 我在解决类似问题时偶然发现了这个解决方案,并且更喜欢它只是因为它看起来比其他建议更简单。显然,关键部分是第二行。第一个和第三个只是针对用户的具体要求。需要反引号来评估 basename 和 dirname。为 Oracle Linux Server 8.3 编写,但没有什么特别之处,我认为应该适用于所有 linux。
    猜你喜欢
    • 1970-01-01
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 2017-08-17
    • 2021-08-21
    • 2015-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多