find,xargs:为每个文件执行命令链

时间:2016-11-20 02:58:24

标签: bash shell find pipe xargs

如果问题标题不够丰富,我很抱歉。请随意提出更好的变体。

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

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

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

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

xargs

但我不喜欢重复,我不喜欢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

3 个答案:

答案 0 :(得分:5)

不是专注于单行,更好的解决方案是将逻辑放入脚本中,以便于执行和测试。将其放在名为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 {} +

答案 1 :(得分: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命令应用相同的注意事项。

答案 2 :(得分:0)

使用并行,您不需要脚本,而是执行:

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