stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|echo mv "${|' |
SN=SHOOTNAME sh -s -- *
Lệnh trên sẽ làm những gì bạn cần. Nó có thể có mục đích chung hơn bạn yêu cầu - nhưng vui lòng xem phần dưới của câu trả lời này để biết ví dụ cụ thể hơn.
Nó hoạt động bằng cách điền vào *
tất cả các tệp trong thư mục hiện tại và in ra thời gian thay đổi của chúng. Đối với chỉ các tệp trong thư mục hiện tại có chứa hậu tố, .NEF
bạn sẽ muốn thay đổi ngôi *
sao ở cuối cả hai dòng 1 và 5 thành *.NEF
. Nó nối thêm một số biến shell và dấu ngoặc kép ở cuối - tên chỉ tồn tại ở đầu kia của đường ống trong sh
lớp con.
Ngoài ra, bởi vì chúng tôi chỉ định tên tệp chỉ theo thứ tự toàn cầu của chúng - hoặc các ${1}
tham số loại vỏ này hoạt động tốt với bất kỳ tên tệp nào - bất kỳ ký tự lạ nào nó có thể chứa.
Bây giờ lệnh bao gồm một echo
- nó đã được ghi. Chạy về cơ bản là không hoạt động - nó chỉ cho bạn thấy những gì nó muốn làm. Đây là đầu ra của nó từ thư mục nhà của tôi trước khi cho nó vào 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}"
Đây là sau 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
Bạn có thể nhận thấy rằng đầu ra của tôi hiển thị một vài tệp hình ảnh đã được đặt tên cho thời gian tạo của chúng nhưng tên được gán mới của chúng không khớp. Đây không phải là hiệu ứng của sort
- hoạt động theo quy định - mà là những tệp cuối cùng có thay đổi trạng thái vào ngày đó. Tuy nhiên, như bạn đã chỉ định trong các nhận xét về câu hỏi ctime
này là tài sản bạn đang tìm kiếm, đó là loại tài sản và tên được cung cấp ở đây. Tuy nhiên, đây là stat
đầu ra của tên tệp được đính kèm:
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
Đầu ra trên cũng sẽ giúp tôi chứng minh toàn bộ đường ống đang làm gì.
Vì vậy, stat --printf='}" "%z_${SN}_${LINENO}"\n'
in ra các dòng trông như:
}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... Đâu YMDHMS.NS -TZ
là những strftime
thành phần khác nhau mà họ đại diện cho ctime
. Định dạng đầu ra của nó giống hệt với %w
- thời gian sinh tệp %x
- thời gian truy cập lần cuối - hoặc %y
- lần sửa đổi cuối cùng, và vì vậy thay thế bất kỳ một trong số này cho %z
câu lệnh trên sẽ mở rộng thay cho giá trị của chúng. Như chúng ta đã thảo luận trong các ý kiến, %w
- thời gian sinh tệp - không phải là một thuộc tính đáng tin cậy và khi không được hỗ trợ, nó chỉ mở rộng ra 0
.
Nó thực hiện điều này cho mọi tập tin shell shell cho nó trong *
hoặc bất kỳ shell nào bạn cung cấp cho nó - chẳng hạn như *.NEF
chỉ các tệp trong thư mục hiện tại có .NEF
hậu tố.
Danh sách đó được trao cho các nl
số mà mỗi dòng tăng thêm 1. Các dòng của nó -n
được căn ln
trái và không được đệm với số thứ tự tối thiểu -w
là 1 và chỉ một ''
chuỗi null để phân biệt -s
chúng từ nội dung của dòng. Nó xuất ra:
I}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... Đâu I
là số lượng của mỗi dòng.
sort
sắp xếp đầu vào của nó từ trường -k2,3
thứ hai đến thứ ba - hoặc trên YYYY-MM-DD HH:MM:SS.NS
. Vì tại thời điểm này, chất lượng duy nhất của bất kỳ dòng nào là I
hoặc ngày và I
bị bỏ qua, không cần phải cụ thể hơn điều đó. Điều này cũng giải quyết nhận xét bạn đã thực hiện liên quan đến các tệp được đặt tên theo số chứ không phải theo ngày. Lẽ ra tôi nên thực hiện sort
trước đó sed
ngay từ đầu nhưng điều đó không xảy ra với tôi để sắp xếp theo phút và giây và tương tự.
Cơ sở thử nghiệm của tôi cho điều này đã được tạo ra như sau:
for s in 9 8 7 6 5 4 3 2 1; do touch $s && sleep 1; done
Nếu tôi sort
sau sed
- như tôi đã làm trước khi chỉnh sửa này - thì điều này sẽ đổi tên các tệp 9
thành ${DATE}.SHOOTNAME.9
vì YMD
các trường của mỗi tệp giống hệt nhau và sort
sẽ không ảnh hưởng đến thứ tự dòng của chúng. Nhưng với sự điều chỉnh này, lệnh này sort
dành riêng cho nano giây và do đó 9
được đổi tên thành ${DATE}.SHOOTNAME.1
và ngược lại cho 1
. Cảm ơn, @Seul, vì đã chú ý đến điều đó.
sed
sau đó loại bỏ chuỗi đầu tiên trông giống như <space><any char><any char>:<colon>
và tất cả các ký tự theo sau ^
không phải là _
dấu gạch dưới. Vì vậy, tại thời điểm này, dòng trông như:
I}" "YYYY-MM-DD_${SN}_${LINENO}"
... tiếp theo nó loại bỏ tất cả -
dấu gạch ngang. Và cuối cùng nó chèn echo mv "${
vào ^
đầu mỗi dòng, vì vậy nó trông như thế này:
echo mv "${I}" "YYYYMMDD_${SN}_${LINENO}"
- Cuối cùng một hình
sh
elip được gọi với biến môi trường được $SN
khai báo - ở đây giá trị của nó là SHOOTNAME
. POSIX chỉ định rằng trình bao tăng var $LINENO
cho mỗi dòng mà nó đọc, vì vậy với mỗi dòng chúng ta cung cấp cho nó giá trị đó trong tên tệp sẽ mở rộng thành một hơn so với dòng cuối cùng. Nếu - như ý kiến của bạn chỉ ra - vì một số lý do, điều này không xảy ra, một sự thay thế hoàn toàn hợp lệ sẽ được $((i=i+1))
in lần đầu tiên stat
trong dòng 1 của đường ống và như tôi đã cung cấp trong một ví dụ cụ thể dưới đây.
Shell cũng được gọi với các tham số vị trí được đặt thành toàn cầu của chúng ta - ở đây là *
sao cho tất cả các tệp trong thư mục hiện tại. Như đã đề cập, *.NEF
trong dòng này và trong dòng đầu tiên sẽ chỉ hoạt động trên các tệp trong thư mục hiện tại với hậu tố tên tệp của .NEF
.
Miễn là toàn cầu của nó giống như trong dòng đầu tiên, nó sẽ toàn cầu theo thứ tự như nl
được đánh số chúng. Vì vậy, bất kể dòng nào xảy ra "${1}"
sẽ mở rộng thành cùng tên tệp mà chúng ta đã gán cho nó theo nl
đầu ra của. Bằng cách này, bạn có thể đổi tên các tệp theo thứ tự ngày một cách nhanh chóng và an toàn và theo đúng thứ tự.
- Như đã đề cập, tôi đã ghi lệnh
echo
ở đây. Nhưng nếu bạn chạy echo
và thấy nó phù hợp với bạn sau tất cả bạn sẽ cần phải xóa echo
.
Như thế này:
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
SN=SHOOTNAME sh -s -- *
Hoặc có thể:
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
Và ở đây nó được làm việc thành một hàm shell:
_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
Điều đó sử dụng shell cho một số điều tôi đang làm với các tiện ích khác. Khái niệm này giống nhau - toàn cầu một danh sách tệp, sắp xếp theo các cách khác nhau và lưu trữ thứ tự sắp xếp. Điều thực sự khác biệt về suy nghĩ này là nó thực hiện các hoạt động di chuyển theo từng khoảng thời gian, hiển thị cho người dùng những gì nó sắp làm và chờ một lời nhắc trước khi tiếp tục. Tôi đã ghi lại chính mình bằng cách sử dụng nó ở đây để bạn có thể xem phiên cuối về cách thức hoạt động của nó.