【问题标题】:find, xargs: execute chain of commands for each filefind, xargs: 为每个文件执行命令链
【发布时间】:2017-04-03 16:06:21
【问题描述】:

如果问题标题信息不足,我很抱歉。请随时提出更好的变体。

我想执行以下任务: 在一个目录中,我有许多 JPEG 格式的照片文件。我想从 EXIF 中提取这些照片的拍摄日期,为每个日期创建一个新目录,并将文件移动到相关目录。

(EXIF日期和时间格式为YYYY:MM:DD hh:mm:ss,我希望目录名格式为YYYY-MM-DD,这就是我使用sed的原因)

我有点知道如何分别执行这些任务,但未能将它们放在一起。我花了一些时间研究如何使用find-execxargs 执行命令,但仍然无法理解如何正确链接所有内容。

最后我能够使用两个命令完成我的任务:

find . -name '*.jpg' -exec sh -c "identify -format %[exif:DateTimeOriginal] {}
    | sed 's/ [0-9:]*//; s/:/-/g' | xargs mkdir -p" \;

find . -name '*.jpg' -exec sh -c "identify -format %[exif:DateTimeOriginal] {}
    | sed 's/ [0-9:]*//; s/:/-/g; s/$/\//' | xargs mv {}" \;

但我不喜欢重复,也不喜欢-exec sh -c。有没有正确的方法在一行中做到这一点而不使用-exec sh -c

【问题讨论】:

    标签: bash shell find pipe xargs


    【解决方案1】:

    与其专注于单行代码,更好的解决方案是将逻辑放入脚本中,以便于执行和测试。把它放在一个名为movetodate.sh的文件中:

    #!/usr/bin/env bash
    
    # This script takes one or more image file paths
    
    set -e
    set -o pipefail
    
    for path in "$@"; do
        date=$(identify -format %[exif:DateTimeOriginal] | sed 's/ [0-9:]*//; s/:/-/g')
        dest=$(dirname "$path")/$date
        mkdir -p "$dest"
        mv "$path" "$dest"
    done
    

    然后,调用它:

    find . -name '*.jpg' -exec ./movetodate.sh {} +
    

    【讨论】:

      【解决方案2】:

      exiftool很容易做到:

      exiftool "-Directory<DateTimeOriginal" -d %Y-%m-%d *.jpg
      

      例如,该命令会像这样转换布局:

      .
      ├── a.jpg  (2013:10:17 10:01:00)
      └── b.jpg  (2012:08:07 16:11:15)
      

      到这里:

      .
      ├── 2012-08-07
      │   └── b.jpg
      └── 2013-10-17
          └── a.jpg
      

      如果你还想使用identify,命令可以改写如下:

      script=$(cat <<'SCRIPT'
      d=$(
        d=$(identify -format "%[exif:DateTimeOriginal]" "$0" 2>/dev/null) || exit $?
        d=${d:0:10}
        printf '%s/%s' "$(dirname "$0")" "${d//:/-}"
      ) || exit $?
      mkdir -p "$d" && mv -v "$0" "$d"
      SCRIPT
      )
      
      find "$dir" -name '*.jpg' -exec bash -c "$script" {} \;
      

      注意脚本中$0 变量的使用。我们将{} 占位符作为第一个参数传递给脚本。

      借助for file in "$@" 循环,可以轻松地将脚本转换为接受多个参数(路径)。在这种情况下,\; 字符应替换为+。但是,如果您有大量超过$(getconf ARG_MAX) 限制的文件,您将需要xargs,或者如上面的脚本中所示逐个处理文件。同样的注意事项也适用于exiftool 命令。

      【讨论】:

        【解决方案3】:

        使用并行你不需要脚本,而是会这样做:

        doit() {
          path="$1"
          date=$(identify -format %[exif:DateTimeOriginal] | sed 's/ [0-9:]*//; s/:/-/g')
          dest=$(dirname "$path")/$date
          mkdir -p "$dest"
          mv "$path" "$dest"
        }
        export -f doit
        find . -name '*.jpg' | parallel doit
        

        【讨论】:

          猜你喜欢
          • 2016-08-11
          • 1970-01-01
          • 2021-11-08
          • 1970-01-01
          • 2016-03-15
          • 2015-05-30
          • 1970-01-01
          • 2010-09-16
          • 2011-05-26
          相关资源
          最近更新 更多