【问题标题】:linux script: mass rename of files with whitespaceslinux脚本:使用空格批量重命名文件
【发布时间】:2014-08-27 09:12:33
【问题描述】:

我编写了一个脚本来一次重命名多个文件并添加前导零。 该脚本将要重命名的文件作为第一个参数,第二个是新名称,作为第三个参数,您可以提供新的扩展名

只要文件不包含空格(test asd 1.txt / test asd 2.txt),它实际上就可以工作,因为输出是:

~/Desktop $ gpRenameWithZero test\ asd\* test_ mp3
ls: cannot access test: No such file or directory
ls: cannot access asd*: No such file or directory
ls: cannot access test: No such file or directory
ls: cannot access asd*: No such file or directory

这是脚本:

#!/bin/bash
#rename a group of files with adding padding zero: gpRenameWithZero $1=filesToBeRenamed $2=newName $3=filetype: gpRenameWithZero \* newName_ jpg

#123 files -> length of number are 3 digits
numberOfDigits=$(ls $1| wc -l | xargs expr length)

#take extension from command line or take from filename
if [ $# -gt 2 ]; then
    extension=$3
else
    extension=$(ls -rt $1 | head -n 1 | rev | cut -d . -f1 | rev)
fi

#Preview
ls -rt $1 | cat -n | while read n f; do echo mv "$f" `printf "$2%0$numberOfDigits"d".$extension" $n`; done

read -p "Do you wish to rename [y/n]?" yn
case $yn in
    [Yy]* ) ls -rt $1 | cat -n | while read n f; do mv "$f" `printf "$2%0$numberOfDigits"d".$extension" $n`; done;;
    [Nn]* ) ;;
esac

我已经尝试过引用/双引号变量和参数,转义/不转义。

如何解决这个问题?还是有一个更简单的脚本,它将要重命名的文件、新名称和扩展名作为参数)来重命名多个文件。

【问题讨论】:

  • 为什么不使用rename 实用程序?例如,我有时使用rename "s/ /_/g" * 删除文件名中的空格

标签: linux shell file-io whitespace


【解决方案1】:

你自己明白,为什么是真的,cmets 说的:不要解析来自ls 的输出。

在这种情况下,您需要处理以 NULL 结尾的文件名。这里有很多可以使用这种以 NULL 结尾的字符串的命令。

find 命令可以使用 arg -print0 或精确指定的 -print "<format>\0" 输出以 NULL 结尾的文件名。

因为您想按文件的修改时间 (ls -rt) 对文件进行排序(编号),所以您需要使用一些技巧。所以

  • find 命令(GNU 版本)可以打印出文件修改时间
  • 此时需要对它们进行排序
  • 切割时间字段后,您将获得一个排序的文件名列表

这可以通过以下命令来实现:

find . -mindepth 1 -maxdepth 1 -name "*" -printf "%T@:%p\0" |sort -zrn |sed -z 's/[^:]*://'
                                                  ^^^^^^^^         ^^^           ^^^^^^^^^^ 
modification-time-in-seconds<colon>path-name<NULL>---+              |                |
            sort the NULL-terminated lines numerically (by time) ---+                |
               remove the <time><colon> part, so only the filename remains ----------+

您可以将上述内容放入一个bash函数中以便于使用,例如:

sort_files_by_time() {
        find "${1:-.}" -mindepth 1 -maxdepth 1 -type f -name "${2:-*}" -printf "%T@:%p\0" | sort -zrn | sed -z 's/[^:]*://'
}

上面的函数可以有两个可选参数:

  1. 查找的目录名称(默认:。)
  2. filname 搜索模式(默认:*)

现在您有了一个按时间排序的以 NULL 结尾的文件名列表,需要阅读和使用它们。下一个循环

while IFS= read -d $'\0' -r path ; do
    dir=$(dirname "$path")
    file=$(basename "$path")

    #do what you want

done < <(sort_files_by_time)

总是双引号“$variables”可能包含空格。

【讨论】:

  • 我注意到,当我用 \* 替换 $1 中的空格时,它可以工作:"gpRenameWithZero test\ asd\* test_ mp3" 变为 "gpRenameWithZero test\*asd\* test_ mp3"。只是一种解决方法,但我不必更改我的脚本:)
【解决方案2】:

在 Unix 中,命令行参数的通配符扩展由 shell 执行 - 在传递给程序之前。

即使文件名中没有空格,此脚本也不会工作。例如,如果有 song1.mp3song_2.MP3song_0003.mpeg3 三个文件,则运行 gpRenameWithZero song* test_ mp3 脚本将通过五个参数:$1 将是“song1.mp3”,$2 将是“song_0003.mpeg3”,$3 将是“song_2.MP3”,$4 将是“test_”,$5 将是“mp3”。所以你脚本中的 $1 不是你所期望的。

你最好把新的文件名前缀和扩展名作为第一个和第二个参数,然后剩下的参数(不管有多少)将是文件名。更高级的解决方案甚至可以将它们解析为单独的选项,以便所有参数都是文件名(即第一个和第二个参数不会被区别对待)。

#!/bin/sh
# Usage: renameFiles prefix extension {filenames}

# First argument is the new file name prefix

PREFIX=$1
shift

# Second argument is the new file name extension

EXT=$1
shift

# Calculate the maximum width of the number in the new file names

TMP=`echo $# | wc -c`
NUMBER_OF_DIGITS=`expr $TMP - 1`  # Because TMP also counts the newline character

# Process all the remaining arguments as file names

COUNT=1
for FILE in "$@"
do
  NEW_NAME=`printf "${PREFIX}%0${NUMBER_OF_DIGITS}d.${EXT}" $COUNT`

  # If you wish, prompt the user here

  mv "$FILE" "$NEW_NAME"
  COUNT=`expr $COUNT + 1`
done

#EOF

如果任何参数文件名包含空格,则脚本需要在 $@ 和 mv 命令的第一个参数周围加上双引号。如果前缀和/或扩展名包含空格,则需要在 mv 命令的第二个参数周围加上双引号,以便脚本正常工作。

【讨论】:

  • 我知道“gpRenameWithZero song* test_ mp3”是行不通的。但是使用 espacing * 它可以工作:“gpRenameWithZero song\* test_ mp3”。当您用 \* 替换参数中的空格时,它也可以工作
  • 你是对的:如果通配符被转义,那么它在第一次传入脚本时不会扩展,而是在替换 $1 后扩展,然后在脚本内扩展。尽管如此,这仍然不是一个好方法,因为故意传入转义的通配符不是常规的,也没有必要。
猜你喜欢
  • 2013-03-12
  • 1970-01-01
  • 1970-01-01
  • 2013-09-11
  • 2011-04-02
  • 2016-04-09
  • 2013-12-16
  • 2010-09-20
  • 2013-03-13
相关资源
最近更新 更多