find, xargs:为每个文件执行命令链
find, xargs: execute chain of commands for each file
如果问题标题信息量不够,我很抱歉。请随时提出更好的变体。
我要执行以下任务:
在一个目录中,我有许多 JPEG 格式的照片文件。我想从 EXIF 中提取这些照片的拍摄日期,为每个日期创建一个新目录,并将文件移动到相关目录。
(EXIF 日期和时间的格式为 YYYY:MM:DD hh:mm:ss
,我希望目录名称的格式为 YYYY-MM-DD
,这就是我使用 sed 的原因)
我有点知道如何分别执行这些任务,但无法将它们组合在一起。我花了一些时间研究如何使用 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
。有没有正确的方法可以在不使用 -exec sh -c
的情况下在一行中执行此操作?
与其专注于单行代码,更好的解决方案是将逻辑放入脚本中,以便于执行和测试。将其放入名为 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 {} +
用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]" "[=13=]" 2>/dev/null) || exit $?
d=${d:0:10}
printf '%s/%s' "$(dirname "[=13=]")" "${d//:/-}"
) || exit $?
mkdir -p "$d" && mv -v "[=13=]" "$d"
SCRIPT
)
find "$dir" -name '*.jpg' -exec bash -c "$script" {} \;
请注意脚本中 [=15=]
变量的使用。我们将 {}
占位符作为第一个参数传递给脚本。
在 for file in "$@"
循环的帮助下,脚本可以很容易地转换为接受多个参数(路径)。在这种情况下,\;
字符应替换为 +
。但是,如果您有大量超过 $(getconf ARG_MAX)
限制的文件,您将需要 xargs
,或者如上面的脚本所示一个一个地处理文件。同样的注意事项也适用于 exiftool
命令。
使用 parallel 你不需要脚本而是做:
doit() {
path=""
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
如果问题标题信息量不够,我很抱歉。请随时提出更好的变体。
我要执行以下任务: 在一个目录中,我有许多 JPEG 格式的照片文件。我想从 EXIF 中提取这些照片的拍摄日期,为每个日期创建一个新目录,并将文件移动到相关目录。
(EXIF 日期和时间的格式为 YYYY:MM:DD hh:mm:ss
,我希望目录名称的格式为 YYYY-MM-DD
,这就是我使用 sed 的原因)
我有点知道如何分别执行这些任务,但无法将它们组合在一起。我花了一些时间研究如何使用 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
。有没有正确的方法可以在不使用 -exec sh -c
的情况下在一行中执行此操作?
与其专注于单行代码,更好的解决方案是将逻辑放入脚本中,以便于执行和测试。将其放入名为 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 {} +
用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]" "[=13=]" 2>/dev/null) || exit $?
d=${d:0:10}
printf '%s/%s' "$(dirname "[=13=]")" "${d//:/-}"
) || exit $?
mkdir -p "$d" && mv -v "[=13=]" "$d"
SCRIPT
)
find "$dir" -name '*.jpg' -exec bash -c "$script" {} \;
请注意脚本中 [=15=]
变量的使用。我们将 {}
占位符作为第一个参数传递给脚本。
在 for file in "$@"
循环的帮助下,脚本可以很容易地转换为接受多个参数(路径)。在这种情况下,\;
字符应替换为 +
。但是,如果您有大量超过 $(getconf ARG_MAX)
限制的文件,您将需要 xargs
,或者如上面的脚本所示一个一个地处理文件。同样的注意事项也适用于 exiftool
命令。
使用 parallel 你不需要脚本而是做:
doit() {
path=""
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