Unix 重命名命令管道

Unix 重命名命令管道

有没有办法让rename命令只打印输出文件名(而不是前缀XXX renamed as或括号)?我宁愿不使用正则表达式,除非这是唯一的选择。

为什么我问:我想对一个文件(实际上是一个文件列表)连续进行多次重命名,这样文件名就不会难看。这很困难,因为如果第一个重命名更改了它,则没有简单的方法来获取可以输入到第二个重命名命令的文件的新名称。

例子

这应该将从网上下载的文件重命名%20.在同一管道中的后续命令中,我可能想将其重命名%28(.而且我不想使用单个重命名命令,因为正则表达式很复杂(如果可能的话)。

find -iname "*%20*" | xargs -n 1 rename --some-option 's/%20/ /g' |  xargs -n 1 rename --some-option 's/%28/(/g'

答案1

考虑使用一系列sed命令将起始文件名转换为最终文件名,然后执行 mv实际重命名文件:

for i in *%*
do
    printf "mv -v '%s' '%s'\n" "$i" \
      "$( sed -e 's/%20/ /g' \
              -e 's/%28/(/g' \
         <<< "$i" )"
done

该命令列表实际上不会更改您的文件,这是一件好事。它只是输出mv您可以用来实际执行您想要执行的操作的命令。

让我们创建一些包含%20%28出现次数的示例文件:

$ for i in foo bar farkle
> do
>     touch $i"%20".txt; touch $i"%28".txt; touch $i"%20-%28".txt
> done
$ ls -1
bar%20-%28.txt
bar%20.txt
bar%28.txt
farkle%20-%28.txt
farkle%20.txt
farkle%28.txt
foo%20-%28.txt
foo%20.txt
foo%28.txt

现在让我们运行一开始给出的命令列表:

$ for i in *%*
> do
>     printf "mv -v '%s' '%s'\n" "$i" \
>       "$(sed -e 's/%20/ /g' -e 's/%28/(/g' <<< "$i")"
> done
mv -v 'bar%20-%28.txt' 'bar -(.txt'
mv -v 'bar%20.txt' 'bar .txt'
mv -v 'bar%28.txt' 'bar(.txt'
mv -v 'farkle%20-%28.txt' 'farkle -(.txt'
mv -v 'farkle%20.txt' 'farkle .txt'
mv -v 'farkle%28.txt' 'farkle(.txt'
mv -v 'foo%20-%28.txt' 'foo -(.txt'
mv -v 'foo%20.txt' 'foo .txt'
mv -v 'foo%28.txt' 'foo(.txt'

如果该命令序列mv对您来说看起来正确,则向上箭头再次运行该命令,但这次| sh在末尾添加,以实际执行这些mv命令而不是显示它们。

答案2

从本质上讲,这似乎是 URL 解码的练习,这是一个已经解决的问题。

将其另存为rename_script.sh

DECODED="$(awk -niord '{printf RT?$0chr("0x"substr(RT,2)):$0}' RS=%.. <<< "${1}")"
[ ! -f "${1}" ] && echo "Source not found" && exit 1
[ -f "${DECODED}" ] && echo "Target already exists" && exit 1
[ "${1}" != "${DECODED}" ] && mv -i "${1}" "${DECODED}" || echo "Nothing to do"

并这样调用:

find -name "*%*" -print0 | xargs -n1 -0 ./rename_script.sh

高度赞扬这个答案: https://stackoverflow.com/a/14000368/2858703

答案3

find . -type f -name '*%*' -execdir bash -c '
    name=$1
    name=${name//%20/ }    # replace each %20 by a space
    name=${name//%28/(}    # replace each %28 by a (
    mv "$1" "$name"' bash {} \;

这用于find查找当前目录中或当前目录下名称中包含字符的任何常规文件%。对于每个这样的文件,bash都会执行一个简短的脚本,该脚本会执行您在给定文件名上提到的两个替换,然后重命名该文件。

可以很容易地添加其他参数替换来删除或更改文件名中的其他字符串。

一种可能更快的方法是编写一个简单的循环来执行相同的操作(假设 shellbash用于此特定变体):

shopt -s nullglob dotglob globstar

for pathname in ./**/*%*; do
    # skip pathnames to non-regular (or links to non-regular) files
    [[ ! -f "$pathname" ]] && continue

    name=${pathname##*/}    # strip off directory components
    name=${name//%20/ }     # ... so that we don't accidentally
    name=${name//%28/(}     # ... modify the directory path

    # the destination is made up by the directory path
    # (original pathname with filename stripped off) 
    # followed by the new name
    mv "$pathname" "${pathname%/*}/$name"
done

shellnullglob选项使模式./**/*%*扩展为没有什么如果它不匹配任何内容(通常不会展开),则dotglob允许通配模式来处理隐藏名称,并globstar允许使用**“递归”匹配到子目录。

将第二个循环调整为find用于生成路径名而不是通配模式的内容(例如,bash在版本 4 之前的版本下运行,该版本没有**):

find . -type f -name '*%*' -exec bash -c '
    for pathname do
        name=${pathname##*/}    # strip off directory components
        name=${name//%20/ }     # ... so that we don't accidentally
        name=${name//%28/(}     # ... modify the directory path

        # the destination is made up by the directory path
        # (original pathname with filename stripped off) 
        # followed by the new name
        mv "$pathname" "${pathname%/*}/$name"
    done' bash {} +

相关内容