AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 409298
Accepted
sdaau
sdaau
Asked: 2017-12-07 12:04:52 +0800 CST2017-12-07 12:04:52 +0800 CST 2017-12-07 12:04:52 +0800 CST

递归列出目录,首先使用子路径和叶节点(文件)(用于批量重命名文件名的一部分)?

  • 772

在声明它重复之前,请考虑我出于特定原因需要它:批量重命名(或复制到新名称)包含文件和目录名称中的公共字符串的树结构。这是一个示例(在 Ubuntu 14.04 上尝试过,因此使用 GNU 工具):

cd /tmp
mkdir myproj
mkdir -p myproj/myproj_AA/myproj_BB
touch myproj/myproj_AA/myproj_BB/myproj_CC.dat
mkdir myproj/myproj_AA/myproj_DD
touch myproj/myproj_AA/myproj_DD/myproj_EE.dat
mkdir -p myproj/myproj_XX/myproj_YY
touch myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
mkdir -p myproj/myproj_XX/myproj_WW
touch myproj/myproj_XX/myproj_WW/myproj_QQ.dat
tree myproj # to visualise

这个目录结构tree看起来像这样:

myproj
├── myproj_AA
│   ├── myproj_BB
│   │   └── myproj_CC.dat
│   └── myproj_DD
│       └── myproj_EE.dat
└── myproj_XX
    ├── myproj_WW
    │   └── myproj_QQ.dat
    └── myproj_YY
        └── myproj_ZZ.dat

6 directories, 4 files

所以,我希望 中的所有条目myproj/,包括myproj它本身,都重命名为myTESTproj而不是myproj(无论它可能作为名称出现在哪里)。所以,首先我需要获得一个相对于当前目录的相对路径列表 - 然后我需要对其进行排序,以便最外面的孩子(我认为这相当于具有最长相对路径名的文件,但不确定)是第一个(因为如果我先重命名/mv 目录,然后尝试重命名其中的文件,它可能会使用旧的目录名称作为第一个参数,并且由于名称现在已更改而失败)。

我知道首先ls -R --group-directories-first myproj/要使用ls递归和组目录,但它的输出是这样的:

$ ls -R --group-directories-first myproj/
myproj/:
myproj_AA  myproj_XX

myproj/myproj_AA:
myproj_BB  myproj_DD

myproj/myproj_AA/myproj_BB:
myproj_CC.dat

myproj/myproj_AA/myproj_DD:
myproj_EE.dat

myproj/myproj_XX:
myproj_WW  myproj_YY

myproj/myproj_XX/myproj_WW:
myproj_QQ.dat

myproj/myproj_XX/myproj_YY:
myproj_ZZ.dat

...也就是说,它不是带有子路径的简单列表,我可以轻松地提供给while read f; do ...

我最接近的是使用find:

$ find myproj/
myproj/
myproj/myproj_AA
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW
myproj/myproj_XX/myproj_WW/myproj_QQ.dat

所以,在这里我确实有一个简单的子路径列表,但是它首先向叶节点排序根节点 - 我首先需要叶节点。我正在尝试类似的东西find myproj/ | sort -n,但似乎没有什么区别。因此,如果我执行以下操作:

$ find myproj/ | sort -n | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/’ -> ‘myTESTproj/’
mv: cannot stat ‘myproj/myproj_AA’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’: No such file or directory
...

...然后预期的递归重命名立即失败,因为根节点(目录)首先被重命名,因此对它的所有进一步引用都是无效的。

那么,如何首先获得带有叶节点的子目录的正确递归列表,以便像这样在批量重命名中使用它?

bash sed
  • 3 3 个回答
  • 1230 Views

3 个回答

  • Voted
  1. Best Answer
    ilkkachu
    2017-12-07T12:35:30+08:002017-12-07T12:35:30+08:00

    如果您的目标只是重命名,那么在目录本身之前处理每个目录的内容还不够吗,也就是说,您不需要首先(来自所有目录)的所有叶子?正是这样做的。find -depth

    $ mkdir -p a/b c/d
    $ find -depth
    ./a/b
    ./a
    ./c/d
    ./c
    .
    

    然后您可以使用find -exec和 Bash 重命名文件:

    $ find -depth ! -name . -name "*myproj*" -execdir bash -c '
        for f; do mv "$f" "${f/myproj/myTESTproj}" ; done' bash {} +
    
    • 2
  2. roaima
    2017-12-07T12:41:55+08:002017-12-07T12:41:55+08:00

    如果您安装了 Perl 版本的rename命令(有时称为prename),这将适用于您

    find myproj -depth -name '*myproj*' -exec rename -n 's!(.*)myproj!$1myTESTproj!' {} +
    

    确保任何目录中的子项列在目录本身之前的选项-depth。find操作的+后缀允许对指定命令的一次调用-exec进行多次插入。{}以降低效率为代价,您可以将其替换为\;.

    当你确定它会做你想做的事时,删除-n或替换它-v。

    • 1
  3. sdaau
    2017-12-07T12:04:52+08:002017-12-07T12:04:52+08:00

    我记得发布问题后要查找的内容 -如果叶节点是具有最长相对路径名的节点(我不确定它是否总是如此,但似乎至少在 OP 示例中),那么一个简单的需要一种按字符串长度对字符串列表进行排序的方法;不幸sort的是似乎没有这样的选择。

    但是,我找到了https://stackoverflow.com/questions/5917576/sort-a-text-file-by-line-length-include-spaces - 并从那里选择了perl解决方案:

    $ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>'
    myproj/myproj_AA/myproj_DD/myproj_EE.dat
    myproj/myproj_AA/myproj_BB/myproj_CC.dat
    myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
    myproj/myproj_XX/myproj_WW/myproj_QQ.dat
    myproj/myproj_AA/myproj_DD
    myproj/myproj_AA/myproj_BB
    myproj/myproj_XX/myproj_YY
    myproj/myproj_XX/myproj_WW
    myproj/myproj_AA
    myproj/myproj_XX
    myproj/
    

    但是,微不足道的sed 's/myproj/myTESTproj/g'替换在这里也不起作用:

    $ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
    > | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
    ‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’
    mv: cannot move ‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ to ‘myTESTproj/myTESTproj_AA/myTESTproj_DD/myTESTproj_EE.dat’: No such file or directory
    ...
    

    ...所以我们sed 只需要替换一行中的最后一个匹配项,即sed -E 's/(.*)myproj/\1myTESTproj/g':

    $ find myproj/ | perl -e 'print sort { length($b) <=> length($a) } <>' \
    | while read f; do mv -v $f $(echo $f | sed -E 's/(.*)myproj/\1myTESTproj/g'); done
    ‘myproj/myproj_AA/myproj_DD/myproj_EE.dat’ -> ‘myproj/myproj_AA/myproj_DD/myTESTproj_EE.dat’
    ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’ -> ‘myproj/myproj_AA/myproj_BB/myTESTproj_CC.dat’
    ‘myproj/myproj_XX/myproj_YY/myproj_ZZ.dat’ -> ‘myproj/myproj_XX/myproj_YY/myTESTproj_ZZ.dat’
    ‘myproj/myproj_XX/myproj_WW/myproj_QQ.dat’ -> ‘myproj/myproj_XX/myproj_WW/myTESTproj_QQ.dat’
    ‘myproj/myproj_AA/myproj_DD’ -> ‘myproj/myproj_AA/myTESTproj_DD’
    ‘myproj/myproj_AA/myproj_BB’ -> ‘myproj/myproj_AA/myTESTproj_BB’
    ‘myproj/myproj_XX/myproj_YY’ -> ‘myproj/myproj_XX/myTESTproj_YY’
    ‘myproj/myproj_XX/myproj_WW’ -> ‘myproj/myproj_XX/myTESTproj_WW’
    ‘myproj/myproj_AA’ -> ‘myproj/myTESTproj_AA’
    ‘myproj/myproj_XX’ -> ‘myproj/myTESTproj_XX’
    ‘myproj/’ -> ‘myTESTproj/’
    $ tree myTESTproj/
    myTESTproj/
    ├── myTESTproj_AA
    │   ├── myTESTproj_BB
    │   │   └── myTESTproj_CC.dat
    │   └── myTESTproj_DD
    │       └── myTESTproj_EE.dat
    └── myTESTproj_XX
        ├── myTESTproj_WW
        │   └── myTESTproj_QQ.dat
        └── myTESTproj_YY
            └── myTESTproj_ZZ.dat
    
    6 directories, 4 files
    

    我想这是我想要的 - 但是,我不确定最长路径名 == 叶文件节点的假设是否总是正确的;即使是这样 - 有没有更简单的方法可以做到这一点?


    编辑:在这样的结构情况下,这肯定会失败:

    myproj/somespecdir/someotherdir/myproj_CC.dat
    myproj/myproj_AA/myproj_DD/myproj_EE.dat
    myproj/somespecdir/someotherdir
    myproj/myproj_AA/myproj_DD
    myproj/somespecdir
    myproj/myproj_AA
    myproj/
    

    ...也就是说,如果要在重命名的路径中搜索和替换的子字符串的第一次出现也是最后一次(唯一的);它出现在列表中多次出现子字符串的路径之前。

    • 0

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    JSON数组使用jq来bash变量

    • 4 个回答
  • Marko Smith

    日期可以为 GMT 时区格式化当前时间吗?[复制]

    • 2 个回答
  • Marko Smith

    bash + 通过 bash 脚本从文件中读取变量和值

    • 4 个回答
  • Marko Smith

    如何复制目录并在同一命令中重命名它?

    • 4 个回答
  • Marko Smith

    ssh 连接。X11 连接因身份验证错误而被拒绝

    • 3 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Marko Smith

    systemctl 命令在 RHEL 6 中不起作用

    • 3 个回答
  • Marko Smith

    rsync 端口 22 和 873 使用

    • 2 个回答
  • Marko Smith

    以 100% 的利用率捕捉 /dev/loop -- 没有可用空间

    • 1 个回答
  • Marko Smith

    jq 打印子对象中所有的键和值

    • 2 个回答
  • Martin Hope
    EHerman JSON数组使用jq来bash变量 2017-12-31 14:50:58 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Drux 日期可以为 GMT 时区格式化当前时间吗?[复制] 2017-12-26 11:35:07 +0800 CST
  • Martin Hope
    AllisonC 如何复制目录并在同一命令中重命名它? 2017-12-22 05:28:06 +0800 CST
  • Martin Hope
    Steve “root”用户的文件权限如何工作? 2017-12-22 02:46:01 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST
  • Martin Hope
    Cbhihe 将默认编辑器更改为 vim for _ sudo systemctl edit [unit-file] _ 2017-12-03 10:11:38 +0800 CST
  • Martin Hope
    showkey 如何下载软件包而不是使用 apt-get 命令安装它? 2017-12-03 02:15:02 +0800 CST
  • Martin Hope
    youxiao 为什么目录 /home、/usr、/var 等都具有相同的 inode 编号 (2)? 2017-12-02 05:33:41 +0800 CST
  • Martin Hope
    user223600 gpg —list-keys 命令在将私钥导入全新安装后输出 uid [未知] 2017-11-26 18:26:02 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve