在简单的 while 循环中保留文件扩展名(一行)

在简单的 while 循环中保留文件扩展名(一行)

我有一个简单的字符串,它将按顺序重命名文件夹中的所有 jpeg 文件:

ls -v | cat -n | while read n f; do mv -n "$f" "pic-$n.jpg"; done

但是我有一个包含多种不同文件类型的文件夹。是否有一个简单的字符串或变量可以替换.jpg以保留文件扩展名?还是现在必须是一个完整的脚本?如果可能的话,我想将其保留为一行。

例子

假设我有一个这样的文件夹:

a long hash name.mp4
another name.m4v
video.mov
another name.mp4

等等。我想像下面这样重命名文件,以便它们看起来更有条理,并且没有太多字符,就像这样:

vid-1.mp4
vid-2.m4v
vid-3.mov
vid-4.mp4

如果我要使用ls -v | cat -n | while read n f; do mv -n "$f" "vid-$n"; done,那么我就不会有文件扩展名。我想保留文件扩展名,因为这样有时可以更轻松地打开它们。

我主要想知道 bash 中是否有一个全局变量来定义文件扩展名

答案1

这里不要使用循环,只需使用rename

$ ls
'a bad name.png'   asdlha.gif   askd.png  'a worse'$'\n''name.jpg'   kasdjh.jpg

$ rename -n 's/.*(\..+)/"pic-" . ++$a . "$1"/se' *
a bad name.png -> pic0.png
asdlha.gif -> pic1.gif
askd.png -> pic2.png
a worse
name.jpg -> pic3.jpg
kasdjh.jpg -> pic4.jpg

如果看起来正确,则重新运行但不-n进行实际重命名。

解释:

rename命令是一个 perl 脚本,可以运行 perl 代码来重命名文件。在这里,我们使用替换运算符 ( s/old/new/) 和几个特殊选项:

  • /s使得替换运算符将其输入视为一行,从而允许.匹配换行符。我们需要它来处理带有换行符的文件名,就像我在上面的例子中所做的那样。
  • /e我们将右侧 ( s/left/right/) 评估为 perl 表达式。表达式的"pic-" . ++$a . "$1"意思是“字符串pic-,然后是变量的值$a增加一,然后是左侧括号内匹配的内容”。在我们的例子中,是扩展。

接下来,使用的正则表达式 ( .*(\..+)) 表示“零个或多个字符 ( .*) 直到最后一个.(\.匹配文字.),然后是一个或多个字符 ( .+)。因为最后一个\..+在括号中,所以匹配的内容(扩展名)可以$1在右侧找到。

运行上述不带 的命令后-n,剩下:

$ rename  's/.*(\..+)/"pic-" . ++$a . "$1"/se' *
terdon@oregano foo $ ls
pic-1.png  pic-2.gif  pic-3.png  pic-4.jpg  pic-5.jpg

或者,你可以在 shell 中使用以下命令执行此操作:

i=1; for file in *; do 
  ext="${file##*.}"; 
  mv -- "$file" pic-$((i++))."$ext"; 
done

在这里,我们循环遍历所有文件(和目录,如果你不想重命名目录,请小心),将每个文件另存为$file。然后,我们用 获得扩展名${file##*.}。格式将从变量 中${variable##pattern}删除最长的匹配项。通过使用,我们删除所有内容直到最后一个,只留下我们另存为 的扩展名。pattern$variable${file##*.}.$ext

接下来,该mv命令将重命名$file为,加上我们用初始化的pic-变量的值,加一,然后是 a和扩展名。$ii=1.

与上面的解决方案类似rename,这也可以处理任意文件名:

$ ls
'a bad name.png'   asdlha.gif   askd.png  'a worse'$'\n''name.jpg'   kasdjh.jpg
$ i=1; for file in *; do 
  ext="${file##*.}"; 
  mv -- "$file" pic-$((i++))."$ext"; 
done
$ ls
pic-1.png  pic-2.gif  pic-3.png  pic-4.jpg  pic-5.jpg

答案2

如果你特别需要文件以算术方式命名版本排序顺序(与ls -v)那么我建议

printf '%s\0' *.* | sort -zV | {
  local n=1
  while IFS= read -r -d '' f; do 
    echo mv -nv -- "$f" "pic-$((++n)).${f##*.}"
  done
}

或者,如果你想用零填充新名称(这会使后续排序更容易),也许

printf '%s\0' *.* | sort -zV | { 
  local n=1
  while IFS= read -r -d '' f; do 
    printf -v p 'pic-%03d.%s' $((++n)) "${f##*.}"
    echo mv -nv -- "$f" "$p"
  done
}

echo确认正确的文件将按照所需顺序处理后,删除。如果您不关心可读性,当然可以将它们写成“一行”,例如

printf '%s\0' *.* | sort -zV | { local n=1; while IFS= read -r -d '' f; do printf -v p 'pic-%03d.%s' $((++n)) "${f##*.}"; echo mv -nv -- "$f" "$p"; done; }

相关内容