ChatGPT解决这个技术问题 Extra ChatGPT

使用 sed 批量重命名文件

客观的

更改这些文件名:

F00001-0708-RG-biasliuyda

F00001-0708-CS-akgdlaul

F00001-0708-VF-hioulgigl

这些文件名:

F0001-0708-RG-biasliuyda

F0001-0708-CS-akgdlaul

F0001-0708-VF-hioulgigl

外壳代码

去测试:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

去表演:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

我的问题

我不明白 sed 代码。我明白替换命令是什么

$ sed 's/something/mv'

方法。而且我对正则表达式有所了解。但我不明白这里发生了什么:

\(.\).\(.*\)

或在这里:

& \1\2/

对我来说,前者的意思是:“一个字符,后面跟着一个字符,后面跟着一个字符的任意长度序列”——但肯定不止这些。至于后半部分:

& \1\2/

我不知道。


J
Jan

首先,我应该说最简单的方法是使用 prename 或 rename 命令。

在 Ubuntu、OSX(Homebrew 包 rename、MacPorts 包 p5-file-rename)或其他带有 perl 重命名(前名)的系统上:

rename s/0000/000/ F0000*

或者在使用 util-linux-ng 重命名的系统上,例如 RHEL:

rename 0000 000 F0000*

这比等效的 sed 命令更容易理解。

但是对于理解 sed 命令,sed 联机帮助页很有帮助。如果你运行 man sed 并搜索 &(使用 / 命令搜索),你会发现它是 s/foo/bar/ 替换中的一个特殊字符。

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

因此,\(.\) 匹配第一个字符,可以被 \1 引用。然后 . 匹配下一个字符,该字符始终为 0。然后 \(.*\) 匹配文件名的其余部分,可以由 \2 引用。

替换字符串使用 &(原始文件名)和 \1\2 将它们放在一起,它是文件名的每个部分,除了第二个字符是 0。

恕我直言,这是一种非常神秘的方法。如果由于某种原因重命名命令不可用,并且您想使用 sed 进行重命名(或者您正在做一些过于复杂而无法重命名的事情?),那么在您的正则表达式中更加明确会使其更具可读性。也许是这样的:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

能够看到 s/search/replacement/ 中实际发生的变化使其更具可读性。如果您不小心运行了两次或其他什么,它也不会继续从您的文件名中吸取字符。


在我的 RHEL 服务器上,重命名语法为“rename 0000 000 F0000*”
rename 本身很可能是一个“重命名” 链接。即 rename 已从 prename“重命名”。例如,在 Ubuntu 中:readlink -f $(which rename) 输出 /usr/bin/prename ...Davidrename > 完全是一个不同的程序。
好点子,彼得。我已经更新了解决这两个重命名实用程序的答案。
要对此进行调试,请在最后将管道删除到 sh 中。命令将回显到屏幕上。
您确定通过 sh 通过管道传输随机数据是一个很好的建议吗?这是潜在的危险,因为可以执行任意代码(您将数据视为代码)。
g
ghostdog74

你已经有了你的 sed 解释,现在你可以只使用 shell,不需要外部命令

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

很好,但你不能用括号做引用。
G
Guy

几年前,我写了一篇小帖子,其中包含有关使用 sed 进行批量重命名的示例:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

例如:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

如果正则表达式包含组(例如 \(subregex\),那么您可以在替换文本中将它们用作 \1\\2 等。


请注意,不鼓励仅链接的答案(链接随着时间的推移会变得陈旧)。请考虑编辑您的答案并在此处添加概要。
效率不高,但可以完成数百个文件的工作。赞成。
g
gniourf_gniourf

最简单的方法是:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

或者,便携式,

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

这会将文件名中的 F00001 前缀替换为 F0001。归功于这里的 mahesh:http://www.debian-administration.org/articles/150


您应该正确引用变量插值; mv "$i" "${i/F00001/F0001}"。但是+1
p
paxdiablo

sed 命令

s/\(.\).\(.*\)/mv & \1\2/

替换的意思:

\(.\).\(.*\)

和:

mv & \1\2

就像一个普通的 sed 命令。但是,括号、&\n 标记会稍微改变它。

搜索字符串匹配(并记为模式 1)开头的单个字符,然后是单个字符,然后是字符串的其余部分(记为模式 2)。

在替换字符串中,您可以参考这些匹配的模式以将它们用作替换的一部分。您也可以将整个匹配部分称为 &

因此,该 sed 命令所做的是基于原始文件(用于源文件)和字符 1 和 3 之后创建一个 mv 命令,有效地删除字符 2(用于目标文件)。它将按照以下格式为您提供一系列行:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

等等。


这是一个很好的解释,但指出如何将 sed 命令与其他命令一起使用来实际重命名文件可能会很有用。例如:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
@jcarballo:解析 ls、管道通过 sed然后通过 shell 管道是危险的! 它可能会使用伪造的文件名执行任意代码。问题是数据应该被视为数据,在这里它通常被序列化为代码而没有任何预防措施。我希望 paxdiablo 可以删除此答案,因为它确实没有显示出良好的做法。 (我偶然发现了这个问题,因为一个初学者在一个不起作用的命令之后随机传送 | sh,并且在看到这个问题和答案后认为它会更好地工作 - 我很害怕!) :)
G
Gilles Quenot

使用 perl 重命名(工具箱中必须有):

rename -n 's/0000/000/' F0000*

当输出看起来不错以真正重命名时,删除 -n 开关。

https://i.stack.imgur.com/tWPfj.png

util-linux 包中的重命名命令不会。

如果您运行以下命令 (GNU)

$ rename

并且您看到 perlexpr,那么这似乎是正确的工具。

如果不是,则使其成为 DebianUbuntu 之类的衍生工具的默认值(通常已经如此):

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

对于archlinux:

pacman -S perl-rename

对于 RedHat 系列发行版:

yum install prename

'prename' 包位于 EPEL 存储库中。

对于 Gentoo:

emerge dev-perl/rename

对于 *BSD:

pkg install gprename

p5-File-Rename

对于 Mac 用户:

brew install rename

如果您在其他发行版中没有此命令,请搜索您的包管理器以安装它或 do it manually

cpan -i File::Rename

可以找到旧的独立版本 here

man rename

这个工具最初是由 Perl 的父亲 Larry Wall 编写的。


P
Pointy

反斜杠括号的意思是,“在匹配模式时,保留这里匹配的东西。”稍后,在替换文本方面,您可以使用“\1”(第一个带括号的块)、“\2”(第二个块)等来取回那些记住的片段。


D
Dennis Williamson

如果您真正要做的只是删除第二个字符,无论它是什么,您都可以这样做:

s/.//2

但是您的命令正在构建一个 mv 命令并将其通过管道传送到 shell 以供执行。

这并不比您的版本更具可读性:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

第四个字符被删除,因为 find 在每个文件名前加上“./”。


我希望你能删除这个答案。虽然在 OP 的特定情况下它可能很好,但有很多人看到这样的答案并且不理解它,并在一个不起作用的命令之后随机管道 | sh,希望它会工作得更好。太可怕了! (此外,这不是一个好习惯)。我希望你会明白!
a
agc

这是我要做的:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

然后,如果看起来没问题,请将 | sh 添加到末尾。所以:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

D
Digvijay S
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

欢迎来到 SO。请考虑添加您的代码的解释。它将帮助其他用户理解它。
这个答案很好,但它与上面高度赞成的答案几乎重复。
E
Ewan Todd

括号捕获特定字符串以供反斜杠数字使用。


g
ghostdog74
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

可怕!受到任意代码执行的影响(可能不是在问题的特定上下文中,但有很多人看到这样的答案并尝试随机输入看起来像它的东西,这很危险!)。我希望您可以删除此答案(此外,您在这里还有另一个好答案,我赞成)。
V
Victoria Stuart

一些对我有用的例子:

$ tree -L 1 -F .
.
├── A.Show.2020.1400MB.txt
└── Some Show S01E01 the Loreming.txt

0 directories, 2 files

## remove "1400MB" (I: ignore case) ...

$ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/.[0-9]{1,}mb//I'`"; done;
renamed 'A.Show.2020.1400MB.txt' -> 'A.Show.2020.txt'

## change "S01E01 the" to "S01E01 The"
## \U& : change (here: regex-selected) text to uppercase;
##       note also: no need here for `\1` in that regex expression

$ for f in *; do mv 2>/dev/null "$f" "`echo $f | sed -r "s/([0-9] [a-z])/\U&/"`"; done

$ tree -L 1 -F .
.
├── A.Show.2020.txt
└── Some Show S01E01 The Loreming.txt

0 directories, 2 files
$ 

2>/dev/null 抑制无关输出(警告...)

参考[此线程]:https://stackoverflow.com/a/2372808/1904943

更改案例:https://www.networkworld.com/article/3529409/converting-between-uppercase-and-lowercase-on-the-linux-command-line.html