【问题标题】:Change file's name using command line arguments Bash [duplicate]使用命令行参数 Bash 更改文件名 [重复]
【发布时间】:2018-04-23 12:25:01
【问题描述】:

我需要实现一个脚本 (duplq.sh),该脚本将使用命令行参数重命名当前目录中存在的所有文本文件。所以如果duplq.sh pic 0 3这个命令被执行了,它会做如下的转换:

pic0.txt 必须重命名为 pic3.txt

pic1.txtpic4.txt

pic2.txtpic5.txt

pic3.txtpic6.txt

等等……

所以第一个参数总是文件名,第二个和第三个总是一个正数。

我还需要确保在执行脚本时,第一次重命名(pic0.txt 到 pic3.txt)不会擦除当前目录中现有的 pic3.txt 文件。

这是我到目前为止所做的:

 #!/bin/bash

name="$1"
i="$2"
j="$3"


for file in $name*
do
    echo $file
    find /var/log -name 'name[$i]' | sed -e 's/$i/$j/g'
    i=$(($i+1))
    j=$(($j+1))
done 

但是 find 命令似乎不起作用。您还有其他解决方案吗?

【问题讨论】:

    标签: linux bash shell command-line-arguments file-rename


    【解决方案1】:

    您要解决的问题实际上有些棘手,而且我认为您还没有完全考虑清楚。例如,duplq.sh pic 0 3duplq.sh pic 2 5 之间有什么区别——看起来两者都应该在数字上加 3,或者第二个会跳过“pic0.txt”和“pic1.txt”?对名为“pic”、“pic.txt”、“picture.txt”、“picture2.txt”、“pic2-2.txt”或“pic999.txt”的文件有什么影响。

    到目前为止,您的脚本中还有一堆基本错误:

    • 您应该(几乎)始终将变量引用放在双引号中,以避免意外的分词和通配符扩展。因此,例如,使用echo "$file" 而不是echo $file。在for file in $name* 中,您应该在变量而不是* 周围加上双引号,因为您希望将其视为通配符。因此,正确的版本是for file in "$name"*

    • 不要将变量引用放在单引号中,它们不会在那里展开。所以在findsed 命令中,你没有传递变量的值,你传递的是文字美元符号后跟字母。再次,使用双引号。此外,“name”之前没有“$”,因此即使在双引号中也不会被视为变量。

    • 但是findsed 命令无论如何都不会做你想做的事。考虑find /var/log -name "name[1]"——它查找名为“name1”的文件,而不是“name1”+ 一些扩展名。它会在当前目录 和所有子目录 中查找,我很确定您不想要这些。并且“1”("$i")可能不是当前文件名中的数字。假设有名为“pic0.jpg”、“pic0.png”和“pic0.txt”的文件——在第一次迭代中,循环可能会找到所有三个具有类似“pic0*”的模式,然后在第二次迭代中第三次迭代试图找到不存在的“pic1*”和“pic2*”。另一方面,假设有名为“pic0.txt”、“pic5.txt”和“pic8.txt”的文件——同样,它可能会查找“pic0*”(ok),然后是“pic1*”(未找到),然后是“pic2*”(同上)。

      另外,如果你得到多位数字,模式“name[10]”将匹配“file0”和“file1”,但不匹配“file10”。我不知道你为什么在那里添加括号,但它们并没有做任何你想做的事情。

      您已经在$file 变量中一次列出一个文件,再次使用不同的标准搜索只会增加混乱。

    • 另外,在脚本中,您实际上并没有重命名任何内容。 find | sed 行将(如果有效)打印文件的新名称,但实际上不会重命名它。

      顺便说一句,当您使用mv 命令时,请使用mv -nmv -i 以防止它在发生名称冲突时以静默且不可挽回的方式覆盖文件。

    • 为防止在增加文件编号时覆盖,您需要以相反的数字顺序进行重命名(即将“pic3.txt”重命名为“pic6.txt”之前将“pic0.txt”重命名为“pic3.txt”)。这特别棘手,因为如果您只是以反向字母顺序对文件名进行排序,您将在“pic10.txt”之前获得“pic7.txt”。但是如果不先删除“pic”和“.txt”部分,就无法进行数字排序。

      IMO 这实际上是要让这个脚本正常工作需要解决的最棘手的问题。将 最大 索引号指定为参数之一可能是最简单的,并让它从那里开始倒数到 0(循环 numbers 而不是 files ),然后为每个数字迭代匹配的文件(例如“pic0.jpg”、“pic0.png”和“pic0.txt”)。

    【讨论】:

      【解决方案2】:

      所以我假设 0 3 只是衡量旧 num 和新 num 的差异,相当于 1 4 或 100 103。

      为避免覆盖现有文件,请创建一个新的临时目录,将所有受影响的文件移到那里,最后将它们全部移回。

      #/bin/bash
      #
      # duplq.sh pic 0 3
      base="$1"
      delta=$(( $3 - $2 ))
      # echo delta $delta
      
      target=$(mktemp -d)
      echo $target
      # /tmp/tmp.7uXD2GzqAb
      
      add () {
        f="$1"
        b="$2"
        d=$3
        num=${f#./${b}}
        # echo -e "file: $f \tnum: $num \tnum + d: $((num + d))" ;
        echo -e "$((num + d))" ;
      }
      
      for f in $(find -maxdepth 1 -type f -regex ".*/${base}[0-9]+")
      do
        newnum=$(add "$f" "${base}" $delta)
        echo mv "$f" "$target/${base}$newnum"
      done
      # exit
      echo mv $target/${base}* .
      

      首先,我尝试仅使用 bash 语法来检查删除前缀 (pic) 是否只剩下数字。我也没有使用扩展名 .txt - 这是留给读者的练习。从问题中不清楚 - 从未明确告知所有文件共享相同的扩展名,但示例中的所有文件都这样做。

      在 find 中使用 -regex ".*/${base}[0-9]+"),保证值只是数字。

        num=${f#./${b}}
      

      从文件 f 中删除基础(“pic”)。添加了增量 d。

      我并没有真正移动,而是回应了 mv 命令。

      @TODO:实现文件扩展名保存。

      我想到了另外两个陷阱:如果你有 3 个文件 pic0、pic00 和 pic000,它们都将重命名为 pic3。并且pic08会被切割成pic和08,然后08会被尝试读取为八进制数(或者09或者012129等等),导致错误。

      解决此问题的一种方法是,在提取的数字(001 或 018)前加上“1”,然后添加 3,并删除前导 1:

        001 1001 1004 004
        018 1018 1021 021
      

      但这个聪明的解决方案会带来新的问题:

        999 1999 2002 002? 
      

      所以前导 1 必须被截断,前导 2 必须减 1。但是现在,如果 delta 更大,比如说 300:

        018 1018 1318 318 
        918 1918 2218 1218 
      

      嗯 - 这似乎工作。

      【讨论】:

        猜你喜欢
        • 2015-05-05
        • 2013-10-06
        • 1970-01-01
        • 2015-01-29
        • 2020-10-07
        • 1970-01-01
        • 2012-08-15
        • 2022-07-26
        • 2019-09-02
        相关资源
        最近更新 更多