この質問は、最初に質問したときに知らなかった最初の2つの回答とコメントで学ぶことによって完全に書き直されました。
写真を撮った後、そのような書類を持って家に帰りました_DSC1234.NEF
。NEF
NikonのCamera Rawファイル形式なので、EXIF
データがファイルに存在します。
3つの部分に自動的に名前を変更したいと思います。
- 作成日の形式
YYYYMMDD
- 撮影名
- 写真数
したがって、最終ファイル名は次のようにする必要があります
20140707_NameOfShoot_0001.NEF
いくつかの質問があります:
広告1.作成日
場合によっては、ファイルを撮影してから数日後にのみファイルをコピーして名前を変更できるため、日付はコピーした日付ではなく写真を撮った日付を反映する必要があります。mtime
最良の選択のようですが、可能であれば作成日を教えてくださいEXIF
。
広告2:撮影名 理想的には、スクリプトを呼び出すときにパラメータとして設定できる変数です。
広告3.写真の枚数
これは画像の年齢を反映する必要があり、最も古いものから番号が付けられています。問題は、カメラが通常0000
ヒット後に番号付けを再開することです9999
。それでは9995-9999
おそらくそれよりも古いでしょう0000-0004
。ファイルの寿命を反映するソリューションを探しています。この特別なケースでは、名前を変更する
- _DSC0000.NEF -> 20140707_FOO_0004.NEF
- _DSC0001.NEF -> 20140707_FOO_0005.NEF
- _DSC0002.NEF -> 20140707_FOO_0006.NEF
- ...
- _DSC9997.NEF -> 20140707_FOO_0001.NEF
- _DSC9998.NEF -> 20140707_FOO_0002.NEF
- _DSC9999.NEF -> 20140707_FOO_0003.NEF
もう一度申し上げますが、mtime
可能であれば作成日がEXIF
正しいようです。
~からここ日付別にフォルダ内のすべての.NEFファイルの名前を変更する作業ソリューションがあります。
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.NEF\n", $0, a++ }' |
bash
ファイルの変更日とショット名をハードコーディングするのはうまくいきますが、自動化できれば良いでしょう。
タイムスタンプの場合の記事を見つけstrftime()
ましたが、それを使用してファイルの変更日を返す方法を理解していません。また、gawk-lineに追加および追加しようとしましたが、これにより現在のフォルダ内のすべてのファイルが削除されます(時間を変更するのではなく、おそらくsystimeも削除されます)。mktime()
systime()
DATE=$(date +"%Y%m%d")
$DATE
私が試した変数について
gawk 'BEGIN{ a=1 }{ printf "mv %s $1_%04d.NEF\n", $0, a++ }' |
呼び出しスクリプトを使用します./rename FOO
が、名前を変更するとFOOは無視されます。
答え1
ほとんどのUnixはファイル作成日を追跡しません。とにかく、「作成日」は明確に定義されていません。 (ファイルをコピーすると新しいファイルが作成されますか?)合理的な解釈によれば、最新バージョンのデータが作成された日付であるファイル修正時間を使用できます。ファイルをコピーする場合は、変更時間を保存する必要があります(たとえば、そのコマンドを使用しているcp -p
場合はbareを使用しないでください)。cp -a
cp
cp
一部のファイル形式には、作成者アプリケーションが作成日を入力するファイル内のフィールドがあります。これは写真の場合であり、カメラはいくつかのコンテンツを埋めます。Exf生成時間を含むJPEGまたはTIFF画像のデータ。 NikonのNEF画像フォーマットはTIFFをラップし、Exifをサポートします。
ファイル名に作成日を含めるためにExifデータを含む画像ファイルの名前を変更するために使用できるツールがあります。名前に作成日を含めるように画像の名前を変更する2つの解決策が表示されます。エクスポートツールそしてexiv2。
どちらのツールも、ファイル名にカウンタを含めることを許可しないと思います。 2つのステップに名前を変更できます。まず、ファイル名に日付を含め(順序を維持するために可能な高解像度で)、その日付部分に基づいてファイル番号を付けます(時間は削除します)。最新のデジタル一眼レフは連続的に画像を撮影できるので(NikonのD4は11fpsで撮影する)、最初のステップで元のファイル名を維持することをお勧めします。そうしないと、同じファイル名を持つ複数のファイルが発生する可能性があります。
exiv2 mv -r %Y%m%d-%H%M%S:basename: *.NEF
# exiv2 uses `strftime(3)`, so `%Y%m%d-%H%M%S` returns YYYYMMDD-hhmmss
# :basename: is a naming variable exiv2's `-r`-handle provides. See `exiv2 -h` for more
# Now you have files with names like 20140630-235958_DSCC1234.NEF.
# Note that chronological order and lexicographic order agree with this naming format.
i=10000
for x in *.NEF; do
i=$((i+1))
mv "$x" "${x%-*}_FOO_${i#1}.NEF"
done
${x%-*}
文字の後ろの部分を削除します-
。カウンタ変数は、i
10000から計算を開始し、1
先行番号を削除することによって使用されます。これは、すべてのカウンタ値が同じ数値を持つように先行ゼロを取得するトリックです。
ファイル名の数を増やしてファイル名を変更するカウンタを含むように複数のファイルの名前を変更する別のソリューションがあります。
Exifデータの代わりにファイルのタイムスタンプを使用するには、次を参照してください。ファイル名の末尾に変更された日付タイムスタンプを使用して複数のファイルの名前を変更しますか?
通常、シェルコードを生成してからシェルにパイプしないでください。これは不必要に複雑です。たとえば、代わりに
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ printf "mv %s %04d.NEF\n", $0, a++ }' |
bash
書くことができます
find -name '*.NEF' |
gawk 'BEGIN{ a=1 }{ system(sprintf("mv %s %04d.NEF\n", $0, a++)) }'
'
ファイル名がシェルコードとして解釈されるため、ファイル名にシェル特殊文字(スペース、、、など)が含まれていると、両方のバージョンが悲惨な結果になる可能性があります。これを強力なコードに置き換える方法がありますが、最も簡単な方法ではないので、そのアプローチを使用しないでください。$
`
1 「ctime」というものがありますが、c
動作しません。作る、これは変化。 ctimeは、ファイルの内容やメタデータ(名前、権限など)が変更されるたびに変更されます。 ctime は生成時間とほぼ反対です。
答え2
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|echo mv "${|' |
SN=SHOOTNAME sh -s -- *
上記のコマンドはユーザーの要件を満たす必要があります。おそらくあなたが要求するよりも一般的でしょう。ただし、より具体的な例については、この回答の下部を参照してください。
*
現在のディレクトリ内のすべてのファイルを繰り返し、変更時間を印刷するように機能します。サフィックスを含む現在のディレクトリのファイルの場合は、行1と5の末尾にあるglobstarをに変更する.NEF
必要があります。これにより、最後にいくつかのシェル変数と引用符が追加されます。これらの名前はパイプのもう一方の端にのみ存在します。サブシェル。*
*.NEF
sh
また、${1}
グローバル順序またはシェル型パラメータを介してのみファイル名を指定するため、ファイル名にどのような奇妙な文字が含まれていても、すべてのファイル名に対して機能します。
現在のコマンドには以下が含まれています。このコマンドecho
は弱体化しました。実行はデフォルトでは機能しません。ただ実行したいことを示すだけです。これは私のホームディレクトリの出力で、次に供給されますsh
。
echo mv "${2}" "20140611_${SN}_${LINENO}"
echo mv "${4}" "20140614_${SN}_${LINENO}"
echo mv "${11}" "20140617_${SN}_${LINENO}"
echo mv "${7}" "20140622_${SN}_${LINENO}"
echo mv "${8}" "20140622_${SN}_${LINENO}"
echo mv "${1}" "20140624_${SN}_${LINENO}"
echo mv "${10}" "20140704_${SN}_${LINENO}"
echo mv "${5}" "20140704_${SN}_${LINENO}"
echo mv "${9}" "20140704_${SN}_${LINENO}"
echo mv "${12}" "20140705_${SN}_${LINENO}"
echo mv "${3}" "20140705_${SN}_${LINENO}"
echo mv "${13}" "20140706_${SN}_${LINENO}"
echo mv "${6}" "20140706_${SN}_${LINENO}"
以下は次のとおりですsh
。
mv Desktop-1 20140611_SHOOTNAME_1
mv Library 20140614_SHOOTNAME_2
mv target.txt 20140617_SHOOTNAME_3
mv script.sh 20140622_SHOOTNAME_4
mv script.sh~ 20140622_SHOOTNAME_5
mv Desktop 20140624_SHOOTNAME_6
mv shot-2014-06-22_17-11-06.jpg 20140704_SHOOTNAME_7
mv Terminology.log 20140704_SHOOTNAME_8
mv shot-2014-06-22_17-10-16.jpg 20140704_SHOOTNAME_9
mv test 20140705_SHOOTNAME_10
mv Downloads 20140705_SHOOTNAME_11
mv test.tar 20140706_SHOOTNAME_12
mv new
file 20140706_SHOOTNAME_13
私の出力には、生成された時間に応じて名前付き画像ファイルがいくつか表示されますが、新しく割り当てられた名前が一致しないことがわかります。これはsort
、指定されたとおりに操作した結果ではなく、そのファイルがその日付に最後に状態が変更されたという事実です。それにもかかわらず、この質問に対するコメントで指定されているとおりに探してctime
いる属性は、ここで提供されている並べ替えと名前の属性です。それにもかかわらず、stat
ファイル名が追加された出力は次のとおりです。
stat -c '%z %n' -- *
2014-06-24 16:50:09.110283839 -0700 Desktop
2014-06-11 23:34:02.981981145 -0700 Desktop-1
2014-07-05 01:00:43.213344635 -0700 Downloads
2014-06-14 10:32:13.537014418 -0700 Library
2014-07-04 23:02:25.079690701 -0700 Terminology.log
2014-07-06 11:24:05.398936386 -0700 new
file
2014-06-22 11:26:53.658004123 -0700 script.sh
2014-06-22 11:26:53.658004123 -0700 script.sh~
2014-07-04 13:34:00.063296353 -0700 shot-2014-06-22_17-10-16.jpg
2014-07-04 13:34:00.066629687 -0700 shot-2014-06-22_17-11-06.jpg
2014-06-17 19:59:38.475358571 -0700 target.txt
2014-07-05 23:53:39.097065292 -0700 test
2014-07-06 00:38:57.060521397 -0700 test.tar
上記の出力は、パイプライン全体が何をするのかを示すのにも役立ちます。
したがって、
stat --printf='}" "%z_${SN}_${LINENO}"\n'
次の行が印刷されます。}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
...彼らが表すYMDHMS.NS -TZ
個々のコンポーネントはどこにありますか?出力形式はファイルの作成時間 - 最後のアクセス時間 - または最後の変更時間と同じであるため、上記の文でこれらのいずれかを置き換えると、その値に拡張されます。コメントですでに議論したように、ファイル作成時間は信頼できる属性ではなく、それをサポートしていない場合。strftime
ctime
%w
%x
%y
%z
%w
0
これは、シェルグロブのすべてのファイル、またはユーザーが提供するすべてのシェルグロブ*
に対してこれを行います。たとえば、*.NEF
現在のディレクトリに.NEF
サフィックスがあるファイルにのみ適用されます。
リストには、
nl
各行に対して 1 ずつ増加する数値が渡されます。行-n
番号はln
左揃えでゼロで埋められず、最小-w
IDは1で、行の内容と空の''
文字列のみが区切られます。-s
次のように出力されます。I}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
...I
各行の番号はどこにありますか?
sort
-k2,3
2番目のフィールドの入力を3番目のフィールドまたはで並べ替えますYYYY-MM-DD HH:MM:SS.NS
。この時点で、すべての行の唯一の固有品質は日付でスキップされるI
ためI
、それよりも具体的である必要はありません。これはまた、日付ではなく数字で名前が付けられたファイルに対するあなたのコメントを解決します。私しなければならないまず、以前の作業はすべてやりましたが、分、秒などsort
で並べ替えるつもりはありませんでした。sed
私のテストベースは次のように生成されます。
for s in 9 8 7 6 5 4 3 2 1; do touch $s && sleep 1; done
もし私がsort
後ろに sed
- この編集以前に行ったように、フィールドは各ファイルに対して同じで、9
行の順序には影響しないため、ファイル名が変更されます。ただし、この調整により、コマンドはナノ秒にのみ適用されるため、名前が変わり、その逆も同様です。この問題に興味を持ってくれた@Seulに感謝します。${DATE}.SHOOTNAME.9
YMD
sort
sort
9
${DATE}.SHOOTNAME.1
1
sed
次に、アンダースコア<space><any char><any char>:<colon>
ではなく、次の最初の文字列と文字を削除します。したがって、この時点で行は次のようになります。^
_
I}" "YYYY-MM-DD_${SN}_${LINENO}"
...次にすべての-
ダッシュを削除します。最後に、各行の頭部echo mv "${
に挿入されるので、^
次のようになります。
echo mv "${I}" "YYYYMMDD_${SN}_${LINENO}"
- 最後に、
sh
ellが呼び出され、環境変数が宣言されます$SN
。ここで、その値はですSHOOTNAME
。 POSIXはシェルが読み取る各行に対してvarを増やすことを指定するので、$LINENO
提供する各行のファイル名の値は最後の行よりも1つ大きくする必要があります。あなたの意見からわかるように、何らかの理由でこれが起こらない場合、完全に有効な代替案は、以下の特定の例で提供されているように、$((i=i+1))
最初にstat
パイプのライン1に印刷することです。
シェルはまた、globに設定された位置引数を使用して呼び出されます。ここでは、*
現在のディレクトリ内のすべてのファイルのglobstarです。すでに述べたように、*.NEF
この行と最初の行は現在のディレクトリのファイル名のみです.NEF
。
globが最初の行のglobと同じであれば、nl
番号が付けられた順序でglobされます。したがって、どの行に表示されても、出力に基づいて割り当てられた同じファイル名"${1}"
に拡張されます。nl
これにより、日付ごとに正しい順序でファイル名をすばやく安全に変更できます。
echo
すでに述べたように、ここではコマンドを弱めました。ただし、これを実行してecho
効果があると判断された場合は削除することをお勧めしますecho
。
このように:
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
SN=SHOOTNAME sh -s -- *
または:
export SN=SHOOTNAME SUFX=.NEF
stat --printf='}" "%z_${SN}_$((i=i+1))${SUFX}"\n' -- *$SUFX |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
sh -s -- *$SUFX
ここではシェル関数として扱われます。
_batch_date_rename () ( # a big one
ERR= # for error reporting
export "DIR=$1" "SUFX=$2" \ # args 1,2 must be dirname and file suffix
"NAME=${3-${ERR:?no rename string specified}}" \ # need name string
"TIME=${4-%y}" INT=$((${INT:-25}*3)) ${NOCONFIRM+NOCONFIRM=}
#all above vars are exported to all points below
_path_chk () { #run once at start - fn quits if any below test fails
[ -d "$1" ] && [ -w "$1" ] && set -- "$1"/*"$2" && [ -e "$1" ]
} # chks for user writable dirname and resolvable $1/*$2 glob
_print_fmt () { #shell printf now not stat - last field zero padded
printf 'mv "${%d}" "${DIR}/%d_${NAME}_%04d${SUFX}"\n' "$@"
}
_print_mv () { #prints copy of mv action before attempting
echo '(set -x' #uses shells debug printer to show expanded vals
printf ': ${0+%s}\n' "$@" \
${NOCONFIRM-'Key "ENTER" to accept or "CTRL+C" to quit'}
echo \) #above can be disabled by declaring NOCONFIRM at invocation
} #by default fn batches 25 mvs at a time, displays them, and confirms
_read_loop () { #parses piped in with IFS, batches in INTerval of 25
argc=${1-$argc} ; ${1+shift} #total globbed files - quit point
while IFS=' -' read nl y m d na ; do #split on -
set -- "$@" "$nl" "$y$m$d" "$((i=i+1))" #build array until
[ "$#" -ge "$INT" ] && break #hit interval
done ; IFS='
'; set -- $(_print_fmt "$@") && unset IFS #finalize array in _print_fmt
_print_mv "$@" #do the debug out
${NOCONFIRM+:} read < /dev/tty #if $NOCONFIRM not set confirm
printf '%s\n' "$@" #now print the actual command
[ $((argc>i)) -eq 1 ] || echo 'exit 0' #check if quit point
_read_loop #if not quit repeat
}
_pipeline () { #this is mostly same - no sed though
stat -c "$TIME" -- "$@" | nl -nln -w1 -s ' ' | sort -k2,3 | {
_read_loop $# || echo 'exit 1' #read loop stands in for sed
} | sh -s -- "$@" #sh still evaluates on args
} #only two calls from main function below
_path_chk "$1" "$2" || ${ERR:?Invalid pathname parameters specified}
_pipeline "$DIR"/*"$SUFX" #if _path_chk do _pipeline
) #that's all folks
これは、シェルを使用して他のユーティリティで実行するいくつかのタスクを実行します。コンセプトは同じです。ファイルの完全なリストが異なるようにソートされ、ソート順が保存されます。何ですか本物このアイデアの違いは、間隔を空けて移動タスクをバッチ処理し、ユーザーに実行するタスクを表示し、続行する前にプロンプトを待つことです。私の状況を記録するために使用しましたこここれにより、ターミナルセッションを観察して動作を確認できます。