【问题标题】:Bash, change a file name based on a known keyword in a scriptBash,根据脚本中的已知关键字更改文件名
【发布时间】:2014-03-21 21:46:30
【问题描述】:

我需要根据已知关键字更改脚本中的文件名。

例如,文件keywords.root.T127C 应重命名为keywords_environment.root.T127C

我尝试使用这个命令:

find . -name $keywords* | sed -e "p"

哪个输出:./current_directory/keywords.root.T127C,但我不知道将其分配给变量$var。如果可以分配,我知道如何使用以下命令对其进行修剪:

echo "${var##*.}".

我的问题:

(1) 有没有只用一个命令就能做到的有效方法?话说,把“keywords.root.T127C”直接转成“keywords_environment.root.T127C”?

(2) 如果不是(1),如何将“./current_directory/keywords.root.T127C”分配给变量?

谢谢!

【问题讨论】:

    标签: bash rename


    【解决方案1】:

    这是一个最佳实践实现(用于递归使用;用例可以使用 glob 而不是 find 更简单)——使用 NUL 分隔的文件名流,因此即使使用不寻常的名称(例如那些包含文字换行符的):

    while IFS='' read -r -d '' filename; do
      [[ $filename = *.* ]] \
        && mv -- "$filename" "${filename%%.*}_environment.${filename#*.}"
    done < <(find . -name "${keyword}*" -print0)
    

    另见BashFAQ #30

    【讨论】:

    • +1 用于使用/显示使用 find 输出的最有效方式。
    • @Charles,感谢您的解决方案和链接!不幸的是,我们的服务器暂时无法工作(至少希望它是暂时的),因此我无法在您的解决方案恢复正常之前运行您的解决方案。如果有任何结果,我会在这里发布我的反馈。
    • 下周四前服务器不可能恢复!我真的很想杀人!
    【解决方案2】:

    使用sed:

    for i in $(find . -name "$keyword*"); do
      nn="$(echo -n "$i" | sed 's/\(.*'$keyword'\)\(.*\)/\1_environment\2/g')"
      mv -- "$i" "$nn"
    done
    

    这会将./keywords.root.T127C 更改为./keywords_environment.root.T127C

    【讨论】:

    • 如果 find 的输出包括带有空格的文件名、带有可能扩展的全局字符的文件名等,这将无法正常工作。
    • 另见mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29(虽然它的标题参考ls,但也讨论了find的误用)。
    • 另外:考虑安全性。当有人恶意创建一个名为 $'/tmp/foo \n/etc/passwd' 的文件名时,您不希望做错这件事,并且您的清理脚本会逐行检查文件列表以决定 rm 的内容。
    • 此外,C 程序存在缓冲区溢出或类似错误创建文件名中有垃圾文件的情况并非闻所未闻。处理意外是编写好软件的一部分。
    • 更好,但仍未修复——for i in $(find ...) 方法本身就被破坏了。另请参阅mywiki.wooledge.org/UsingFind,尤其是第 5 节及以后的部分。
    【解决方案3】:

    非常感谢查尔斯! 我对他的帖子稍作修改以采纳我的申请:

    1. 我将文件名搜索改为“./sno*.root.T127C”

    2. 我添加了一行“newfilename”来删除文件名的“./”部分。

    这是整个代码对我来说很好用。

    希望它对其他潜在的人也有用。

    env_str="_environment."
    while IFS='' read -r -d '' filename; do
      [[ $filename = ./sno*.root.T127C ]] \
        && newfilename="${filename#./}" \
        && mv -- "$newfilename" "${newfilename%%.*}${env_str}${newfilename#*.}"
    done < <(find . -name "${keyword}*" -print0)
    

    【讨论】:

    • 很高兴这有帮助!如果您不介意,我将编辑此答案以修复缩进。 ./ 是否重要,如果您所做的只是将其传递给 mv?
    • 是的,我实际上只需要“./”后面的文件名,因为我需要进一步“播放”文件名。
    • 在这种情况下,您可以将 -print0 替换为 -printf '%P\0' 首先没有任何前导 ./
    • 顺便说一句,您还可以考虑find . '(' -name "${keyword}*" -a -name "sno*.root.T127C" ')' -printf '%P\0'find 中应用这两个过滤器。
    • @CharlesDuffy,用-printf '%p'\0 替换-print0 效果很好。但是 `&& newfilename="${filename#./}" ` 行仍然是必要的(我认为没有必要,但这不起作用。)。
    猜你喜欢
    • 2020-01-07
    • 2020-08-10
    • 1970-01-01
    • 2014-07-25
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多