在 bash 脚本中获取祖父目录 - 重命名路径中目录的文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21270624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 09:20:11  来源:igfitidea点击:

Get grandparent directory in bash script - rename files for a directory in their paths

linuxbashpathcentosbatch-rename

提问by user1201155

I have the following script, which I normally use when I get a bunch of files that need to be renamed to the directory name which contains them.

我有以下脚本,当我得到一堆需要重命名为包含它们的目录名的文件时,我通常会使用它。

The problem now is I need to rename the file to the directory two levels up. How can I get the grandparent directory to make this work?

现在的问题是我需要将文件重命名为两个级别的目录。我怎样才能获得祖父目录来完成这项工作?

With the following I get errors like this example: "mv: cannot move ./48711/zoom/zoom.jpgto ./48711/zoom/./48711/zoom.jpg: No such file or directory". This is running on CentOS 5.6.

使用以下内容,我会收到类似此示例的错误:“mv:无法移动./48711/zoom/zoom.jpg./48711/zoom/./48711/zoom.jpg:没有此类文件或目录”。这是在 CentOS 5.6 上运行的。

I want the final file to be named: 48711.jpg

我希望最终文件命名为:48711.jpg

#!/bin/bash

function dirnametofilename() {
  for f in $*; do
    bn=$(basename "$f")
    ext="${bn##*.}"
    filepath=$(dirname "$f")
    dirname=$(basename "$filepath")
    mv "$f" "$filepath/$dirname.$ext"
  done
}

export -f dirnametofilename

find . -name "*.jpg" -exec bash -c 'dirnametofilename "{}"'  \;

find .

采纳答案by mklement0

Note:
* This answer solves the OP's specificproblem, in whose context "grandparent directory" means: the parentdirectory of the directory containing a file (it is the grandparent path from the file'sperspective).
* By contrast, given the question's generic title, other answers here focus (only) on getting a directory'sgrandparent directory; the succinct answer to the genericquestion is: grandParentDir=$(cd ../..; printf %s "$PWD")to get the full path, and grandParentDirName=$(cd ../..; basename -- "$PWD")to get the dir. name only.

注意:
*此答案解决了 OP 的特定问题,在其上下文中“祖父目录”表示:包含文件的目录的目录(从文件的角度来看,它是祖父路径)。
* 相比之下,鉴于问题的通用标题,这里的其他答案(仅)关注获取目录的祖父目录;一般问题简洁答案是grandParentDir=$(cd ../..; printf %s "$PWD")获取完整路径,并grandParentDirName=$(cd ../..; basename -- "$PWD")获取目录。只有名字。

Try the following:

请尝试以下操作:

find . -name '*.jpg' \
  -execdir bash -c \
   'old=""; new="$(cd ..; basename -- "$PWD").${old##*.}"; echo mv "$old" "$new"' - {} \;

Note: echowas prepended to mvto be safe - remove it to perform the actual renaming.

注:echo在前面加上mv是安全的-删除它来执行实际的重命名。

  • -execdir ..\;executes the specified command in the specific directory that contains a given matching file and expands {}to the filename of each.

  • bash -cis used to execute a small ad-hoc script:

    • $(cd ..; basename -- "$PWD")determines the parentdirectory name of the directory containing the file, which is the grandparentpath from the file'sperspective.

    • ${old##*.}is a Bash parameter expansionthat returns the input filename's suffix (extension).

    • Note how {}- the filename at hand - is passed as the 2ndargument to the command in order to bind to $1, because bash -cuses the 1st one to set $0(which is set to dummy value _here).

  • Note that each file is merely renamed, i.e., it stays in its original directory.

  • -execdir ..\;在包含给定匹配文件的特定目录中执行指定命令并扩展{}到每个文件的文件名。

  • bash -c用于执行一个小的临时脚本:

    • $(cd ..; basename -- "$PWD")确定包含文件的目录的目录名称,从文件的角度来看,它是祖父路径。

    • ${old##*.}是一个 Bash参数扩展,它返回输入文件名的后缀(扩展名)。

    • 请注意如何{}- 手头的文件名 - 作为第二个参数传递给命令以绑定到$1,因为bash -c使用第一个来设置$0_此处设置为虚拟值)。

  • 请注意,每个文件只是重命名,即,它保留在其原始目录中

Caveat:

警告

  • Each directory with a matching file should only contain 1matching file, otherwise multiple files will be renamed to the same target name in sequence - effectively, only the last file renamed will survive.
  • 具有匹配文件的每个目录应仅包含1 个匹配文件,否则多个文件将依次重命名为相同的目标名称 - 实际上,只有重命名的最后一个文件将继续存在。

回答by OnlineCop

Another method could be to use

另一种方法可能是使用

(cd ../../; pwd)

If this were executed in any top-level paths such as /, /usr/, or /usr/share/, you would get a valid directory of /, but when you get one level deeper, you would start seeing results: /usr/share/man/would return /usr, /my/super/deep/path/is/awesome/would return /my/super/deep/path, and so on.

如果在任何顶级路径(例如//usr/、 或 )中执行此操作/usr/share/,您将获得一个有效目录/,但是当您深入一层时,您将开始看到结果:/usr/share/man/将返回/usr/my/super/deep/path/is/awesome/将返回/my/super/deep/path,等等。

You could store this in a variable as well:

您也可以将其存储在变量中:

GRANDDADDY="$(cd ../../; pwd)"

and then use it for the rest of your script.

然后将其用于脚本的其余部分。

回答by Reinstate Monica Please

Assuming filepath doesn't end in /, which it shouldn't if you use dirname, you can do

假设文件路径不以 结尾/,如果你使用它不应该dirname,你可以这样做

Parent = "${filepath%/*}"
Grandparent = "${filepath%/*/*}"

So do something like this

所以做这样的事情

[[ "${filepath%/*/*}" == "" ]] && echo "Path isn't long enough" || echo "${filepath%/*/*}"

Also this likely won't work if you're using relative paths (like find .). In which case you will want to use

如果您使用相对路径(如find .),这也可能不起作用。在这种情况下,您将要使用

filepath=$(dirname "$f")
filepath=$(readlink -f "$filepath")

instead of

代替

filepath=$(dirname "$f")

Also you're never stripping the extension, so there is no reason to get it from the file and then append it again.

此外,您永远不会剥离扩展名,因此没有理由从文件中获取它然后再次附加它。

回答by Basile Starynkevitch

Can't you use realpath ../../or readlink -f ../../? See this, readlink(1), realpath(3), canonicalize_file_name(3), and realpath(1). You may want to install the realpathpackage on Debian or Ubuntu. Probably CentOS has an equivalent package. (readlinkshould always be available, it is in GNU coreutils)

你不能用realpath ../../readlink -f ../../吗?请参阅此内容readlink(1)realpath(3)canonicalize_file_name(3)realpath(1)。您可能希望realpath在 Debian 或 Ubuntu 上安装该软件包。CentOS 可能有一个等效的软件包。(readlink应该始终可用,它在GNU coreutils 中