【问题标题】:how to make 'for in' not to split a string [duplicate]如何使'for in'不拆分字符串[重复]
【发布时间】:2016-01-18 13:17:21
【问题描述】:

我正在尝试重命名几个文件。所以我首先需要这些文件名。 我正在使用:

for FILE in $(find . -type f -name "*.flv" -exec basename {} \; ); do
    echo "$FILE"
done

当我只尝试 find 命令时,它会正确返回文件的数量,但是当我使用 for 时,我期望 ARQ 将包含单个文件的完整名称,但相反,它会返回拆分后的单词文件名。 那么我怎样才能得到完整的名字,而不仅仅是分开的单词呢?

【问题讨论】:

  • 添加语言标签(bash 脚本?)
  • 看看this question。那里的答案显示了 find 找到的文件的循环,由空字符分隔(find 中的-print0 选项)。
  • find . -type f -name "*.flv" -exec basename {} \; 已经做了你想做的事。
  • 您可能会发现this article,即“设置IFS”部分,内容丰富。

标签: string bash shell split find


【解决方案1】:

不要使用find,,而是使用旨在重命名多个文件的rename 命令。

例如:

rename 's/foo/bar/' **/*.flv

这会将文件名中的foo 递归地替换为所有*.flv 文件中的bar。如果您的 shell 支持 a new globbing option(例如 Bash 4.x 或 zsh),请确保该选项由 shopt -s globstar 启用。

或者,如果您将 find 与循环一起使用,您可以使用:

  • -print0 管道到外部程序时,例如xargs(使用-0),
  • 使用-exec cmd直接在文件上运行命令({}
  • 使用-execdir cmd在文件所在目录执行命令

【讨论】:

    【解决方案2】:

    看题名:避免使用for in分割字符串:

    不要在循环中使用 IFS 字段分隔符:

    :~> a="sdad asd asda  ad
    > fdvbdsvf
    > dfvsdfv
    > 4"
    
    :~> for s in $a; do
           echo "== $s =="; 
        done
    == sdad ==
    == asd ==
    == asda ==
    == ad ==
    == fdvbdsvf ==
    == dfvsdfv ==
    == 4 ==
    
    :~> (IFS=; for s in $a; do
           echo "== $s =="; 
         done)
    == sdad asd asda  ad
    fdvbdsvf
    dfvsdfv
    4 ==
    

    我在最后一个命令中使用了圆括号,因此 IFS 的更改值仅限于该子进程。

    【讨论】:

      【解决方案3】:

      有几种方法可以让它发挥作用。最简单的就是完全使用find的exec

      find . -type f -name "*.flv" -exec bash -c 'f=$(basename "$1"); printf "%s\n" "$f"' _ {} \;
      

      换句话说,如果您愿意,您可以将复杂的脚本放在-exec 子句中。

      作为第二个选择,考虑这个循环:

      find . -name '*.flv' -print0 | while IFS= read -d '' -r file
      do 
         f=$(basename "$file")
         printf "%s\n" "$f"
      done
      

      【讨论】:

      • 进程替换是一个选项while read -r line; do f=$(basename "$line"); echo "$f"; done < <(find . -name '*.flv')
      • 这是一个useless use of echo -- basename 已经将其结果打印到标准输出;将其捕获在一个变量中,这样您就可以echo 该变量只是......笨拙。
      • @tripleee 如果 OP 的目标是回显文件名,那么您绝对正确。我认为,如果您仔细阅读该问题,很明显 basename-echo 只是他打算编写的更复杂脚本(重命名文件)的替身。不过,他在这里提出的问题仅限于如何处理带空格的文件名。我保留了这两条语句,因为它们清楚地表明了如何使用这些技术编写更复杂的脚本。
      • @John1024:您的第二次尝试需要 GNU 或 BSD 查找,并且将失败,文件名以破折号开头或包含转义序列。使用basename也是没用的,只是使用参数扩展。
      • @cuonglm find 将返回带有路径的文件名,因此它们不会以破折号开头。例如,哪些转义序列在这里会成为问题?您的意思是由echo(某些方言)解释的转义?
      【解决方案4】:

      使用for 循环与来自Command Substitution 的结果不带双引号导致结果默认为break on space, tab and newline(即IFS 默认值)。

      POSIXly,除了 find 和内联脚本之外,您不需要任何其他东西:

      $ find . -type f -name "*.flv" -exec sh -c '
        for f do
          printf "%s\n" "${f##*/}"
        done
      ' sh {} +
      

      使用 GNU find,您不需要内联脚本:

      $ find . -type f -name "*.flv" -printf '%f\n'
      

      【讨论】:

        猜你喜欢
        • 2017-07-22
        • 2012-09-18
        • 2015-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-19
        • 2010-11-03
        • 1970-01-01
        相关资源
        最近更新 更多